README
¶
Pusher Channels HTTP Go Library
The Golang library for interacting with the Pusher Channels HTTP API.
This package lets you trigger events to your client and query the state of your Pusher channels. When used with a server, you can validate Pusher Channels webhooks and authenticate private-
or presence-
channels.
Register for free at https://pusher.com/channels and use the application credentials within your app as shown below.
Supported Platforms
- Go - supports Go 1.5 or greater.
Table of Contents
Installation
$ go get github.com/pusher/pusher-http-go
Getting Started
package main
import (
"github.com/pusher/pusher-http-go"
)
func main(){
// instantiate a client
pusherClient := pusher.Client{
AppID: "APP_ID",
Key: "APP_KEY",
Secret: "APP_SECRET",
Cluster: "APP_CLUSTER",
}
data := map[string]string{"message": "hello world"}
// trigger an event on a channel, along with a data payload
err := pusherClient.Trigger("my-channel", "my_event", data)
// All trigger methods return an error object, it's worth at least logging this!
if err != nil {
panic(err)
}
}
Configuration
The easiest way to configure the library is by creating a new Pusher
instance:
pusherClient := pusher.Client{
AppID: "APP_ID",
Key: "APP_KEY",
Secret: "APP_SECRET",
Cluster: "APP_CLUSTER",
}
Additional options
Instantiation From URL
pusherClient := pusher.ClientFromURL("http://<key>:<secret>@api-<cluster>.pusher.com/apps/app_id")
Note: the API URL differs depending on the cluster your app was created in:
http://key:secret@api-eu.pusher.com/apps/app_id
http://key:secret@api-ap1.pusher.com/apps/app_id
Instantiation From Environment Variable
pusherClient := pusher.ClientFromEnv("PUSHER_URL")
This is particularly relevant if you are using Pusher Channels as a Heroku add-on, which stores credentials in a "PUSHER_URL"
environment variable.
HTTPS
To ensure requests occur over HTTPS, set the Secure
property of a pusher.Client
to true
.
pusherClient.Secure = true
This is false
by default.
Request Timeouts
If you wish to set a time-limit for each HTTP request, create a http.Client
instance with your specified Timeout
field and set it as the Pusher Channels instance's Client
:
httpClient := &http.Client{Timeout: time.Second * 3}
pusherClient.HTTPClient = httpClient
If you do not specifically set a HTTP client, a default one is created with a timeout of 5 seconds.
Changing Host
Changing the pusher.Client
's Host
property will make sure requests are sent to your specified host.
pusherClient.Host = "foo.bar.com"
By default, this is "api.pusherapp.com"
.
Changing the Cluster
Setting the pusher.Client
's Cluster
property will make sure requests are sent to the cluster where you created your app.
NOTE! If Host
is set then Cluster
will be ignored.
pusherClient.Cluster = "eu" // in this case requests will be made to api-eu.pusher.com.
End to End Encryption
This library supports end to end encryption of your private channels. This means that only you and your connected clients will be able to read your messages. Pusher cannot decrypt them. You can enable this feature by following these steps:
-
You should first set up Private channels. This involves creating an authentication endpoint on your server.
-
Next, generate a 32 byte master encryption key, base64 encode it and store it securely.
This is secret and you should never share this with anyone. Not even Pusher.
To generate a suitable key from a secure random source, you could use:
openssl rand -base64 32
-
Pass the encoded key when constructing your pusher.Client
pusherClient := pusher.Client{ AppID: "APP_ID", Key: "APP_KEY", Secret: "APP_SECRET", Cluster: "APP_CLUSTER", EncryptionMasterKeyBase64 "<output from command above>", }
-
Channels where you wish to use end to end encryption should be prefixed with
private-encrypted-
. -
Subscribe to these channels in your client, and you're done! You can verify it is working by checking out the debug console on the https://dashboard.pusher.com/ and seeing the scrambled ciphertext.
Important note: This will not encrypt messages on channels that are not prefixed by private-encrypted-.
Google App Engine
As of version 1.0.0, this library is compatible with Google App Engine's urlfetch library. Pass in the HTTP client returned by urlfetch.Client
to your Pusher Channels initialization struct.
package helloworldapp
import (
"appengine"
"appengine/urlfetch"
"fmt"
"github.com/pusher/pusher-http-go"
"net/http"
)
func init() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
urlfetchClient := urlfetch.Client(c)
pusherClient := pusher.Client{
AppID: "APP_ID",
Key: "APP_KEY",
Secret: "APP_SECRET",
HTTPClient: urlfetchClient,
}
pusherClient.Trigger("my-channel", "my_event", map[string]string{"message": "hello world"})
fmt.Fprint(w, "Hello, world!")
}
Usage
Triggering events
It is possible to trigger an event on one or more channels. Channel names can contain only characters which are alphanumeric, _
or -
and have to be at most 200 characters long. Event name can be at most 200 characters long too.
Single channel
func (c *Client) Trigger
Argument | Description |
---|---|
channel string |
The name of the channel you wish to trigger on. |
event string |
The name of the event you wish to trigger |
data interface{} |
The payload you wish to send. Must be marshallable into JSON. |
data := map[string]string{"hello": "world"}
pusherClient.Trigger("greeting_channel", "say_hello", data)
Multiple channels
func (c. *Client) TriggerMulti
Argument | Description |
---|---|
channels []string |
A slice of channel names you wish to send an event on. The maximum length is 10. |
event string |
As above. |
data interface{} |
As above. |
pusherClient.TriggerMulti([]string{"a_channel", "another_channel"}, "event", data)
Excluding event recipients
func (c *Client) TriggerExclusive
and func (c *Client) TriggerMultiExclusive
follow the patterns above, except a socket_id
is given as the last parameter.
These methods allow you to exclude a recipient whose connection has that socket_id
from receiving the event. You can read more here.
On one channel:
pusherClient.TriggerExclusive("a_channel", "event", data, "123.12")
On multiple channels:
pusherClient.TriggerMultiExclusive([]string{"a_channel", "another_channel"}, "event", data, "123.12")
Batches
func (c. *Client) TriggerBatch
Argument | Description |
---|---|
batch []Event |
A list of events to publish |
pusherClient.TriggerBatch([]pusher.Event{
{ Channel: "a_channel", Name: "event", Data: "hello world", nil },
{ Channel: "a_channel", Name: "event", Data: "hi my name is bob", nil },
})
Authenticating Channels
Application security is very important so Pusher Channels provides a mechanism for authenticating a user’s access to a channel at the point of subscription.
This can be used both to restrict access to private channels, and in the case of presence channels notify subscribers of who else is also subscribed via presence events.
This library provides a mechanism for generating an authentication signature to send back to the client and authorize them.
For more information see our docs.
Private channels
func (c *Client) AuthenticatePrivateChannel
Argument | Description |
---|---|
params []byte |
The request body sent by the client |
Return Value | Description |
---|---|
response []byte |
The response to send back to the client, carrying an authentication signature |
err error |
Any errors generated |
func pusherAuth(res http.ResponseWriter, req *http.Request) {
params, _ := ioutil.ReadAll(req.Body)
response, err := pusherClient.AuthenticatePrivateChannel(params)
if err != nil {
panic(err)
}
fmt.Fprintf(res, string(response))
}
func main() {
http.HandleFunc("/pusher/auth", pusherAuth)
http.ListenAndServe(":5000", nil)
}
Example (JSONP)
func pusherJsonpAuth(res http.ResponseWriter, req *http.Request) {
var (
callback, params string
)
{
q := r.URL.Query()
callback = q.Get("callback")
if callback == "" {
panic("callback missing")
}
q.Del("callback")
params = []byte(q.Encode())
}
response, err := pusherClient.AuthenticatePrivateChannel(params)
if err != nil {
panic(err)
}
res.Header().Set("Content-Type", "application/javascript; charset=utf-8")
fmt.Fprintf(res, "%s(%s);", callback, string(response))
}
func main() {
http.HandleFunc("/pusher/auth", pusherJsonpAuth)
http.ListenAndServe(":5000", nil)
}
Authenticating presence channels
Using presence channels is similar to private channels, but in order to identify a user, clients are sent a user_id and, optionally, custom data.
func (c *Client) AuthenticatePresenceChannel
Argument | Description |
---|---|
params []byte |
The request body sent by the client |
member pusher.MemberData |
A struct representing what to assign to a channel member, consisting of a UserID and any custom UserInfo . See below |
pusher.MemberData
type MemberData struct {
UserID string
UserInfo map[string]string
}
Example
params, _ := ioutil.ReadAll(req.Body)
presenceData := pusher.MemberData{
UserID: "1",
UserInfo: map[string]string{
"twitter": "jamiepatel",
},
}
response, err := pusherClient.AuthenticatePresenceChannel(params, presenceData)
if err != nil {
panic(err)
}
fmt.Fprintf(res, response)
Application state
This library allows you to query our API to retrieve information about your application's channels, their individual properties, and, for presence-channels, the users currently subscribed to them.
Get the list of channels in an application
func (c *Client) Channels
Argument | Description |
---|---|
additionalQueries map[string]string |
A map with query options. A key with "filter_by_prefix" will filter the returned channels. To get number of users subscribed to a presence-channel, specify an "info" key with value "user_count" . Pass in nil if you do not wish to specify any query attributes. |
Return Value | Description |
---|---|
channels *pusher.ChannelsList |
A struct representing the list of channels. See below. |
err error |
Any errors encountered |
pusher.ChannelsList
type ChannelsList struct {
Channels map[string]ChannelListItem
}
pusher.ChannelsListItem
type ChannelListItem struct {
UserCount int
}
Example
channelsParams := map[string]string{
"filter_by_prefix": "presence-",
"info": "user_count",
}
channels, err := pusherClient.Channels(channelsParams)
// channels => &{Channels:map[presence-chatroom:{UserCount:4} presence-notifications:{UserCount:31}]}
Get the state of a single channel
func (c *Client) Channel
Argument | Description |
---|---|
name string |
The name of the channel |
additionalQueries map[string]string |
A map with query options. An "info" key can have comma-separated values of "user_count" , for presence-channels, and "subscription_count" , for all-channels. To use the "subscription_count" value, first check the "Enable subscription counting" checkbox in your App Settings on your Pusher Channels dashboard.Pass in nil if you do not wish to specify any query attributes. |
Return Value | Description |
---|---|
channel *pusher.Channel |
A struct representing a channel. See below. |
err error |
Any errors encountered |
pusher.Channel
type Channel struct {
Name string
Occupied bool
UserCount int
SubscriptionCount int
}
Example
channelParams := map[string]string{
"info": "user_count,subscription_count",
}
channel, err := pusherClient.Channel("presence-chatroom", channelParams)
// channel => &{Name:presence-chatroom Occupied:true UserCount:42 SubscriptionCount:42}
Get a list of users in a presence channel
func (c *Client) GetChannelUsers
Argument | Description |
---|---|
name string |
The channel name |
Return Value | Description |
---|---|
users *pusher.Users |
A struct representing a list of the users subscribed to the presence-channel. See below |
err error |
Any errors encountered. |
pusher.Users
type Users struct {
List []User
}
pusher.User
type User struct {
ID string
}
Example
users, err := pusherClient.GetChannelUsers("presence-chatroom")
// users => &{List:[{ID:13} {ID:90}]}
Webhook validation
On your dashboard, you can set up webhooks to POST a payload to your server after certain events. Such events include channels being occupied or vacated, members being added or removed in presence-channels, or after client-originated events. For more information see https://pusher.com/docs/webhooks.
This library provides a mechanism for checking that these POST requests are indeed from Pusher, by checking the token and authentication signature in the header of the request.
func (c *Client) Webhook
Argument | Description |
---|---|
header http.Header |
The header of the request to verify |
body []byte |
The body of the request |
Return Value | Description |
---|---|
webhook *pusher.Webhook |
If the webhook is valid, this method will return a representation of that webhook that includes its timestamp and associated events. If invalid, this value will be nil . |
err error |
If the webhook is invalid, an error value will be passed. |
pusher.Webhook
type Webhook struct {
TimeMs int
Events []WebhookEvent
}
pusher.WebhookEvent
type WebhookEvent struct {
Name string
Channel string
Event string
Data string
SocketID string
}
Example
func pusherWebhook(res http.ResponseWriter, req *http.Request) {
body, _ := ioutil.ReadAll(req.Body)
webhook, err := pusherClient.Webhook(req.Header, body)
if err != nil {
fmt.Println("Webhook is invalid :(")
} else {
fmt.Printf("%+v\n", webhook.Events)
}
}
Feature Support
Feature | Supported |
---|---|
Trigger event on single channel | ✔ |
Trigger event on multiple channels | ✔ |
Trigger events in batches | ✔ |
Excluding recipients from events | ✔ |
Authenticating private channels | ✔ |
Authenticating presence channels | ✔ |
Get the list of channels in an application | ✔ |
Get the state of a single channel | ✔ |
Get a list of users in a presence channel | ✔ |
WebHook validation | ✔ |
Heroku add-on support | ✔ |
Debugging & Logging | ✔ |
Cluster configuration | ✔ |
Timeouts | ✔ |
HTTPS | ✔ |
HTTP Proxy configuration | ✘ |
HTTP KeepAlive | ✘ |
Helper Functionality
These are helpers that have been implemented to to ensure interactions with the HTTP API only occur if they will not be rejected e.g. channel naming conventions.
Helper Functionality | Supported |
---|---|
Channel name validation | ✔ |
Limit to 10 channels per trigger | ✔ |
Limit event name length to 200 chars | ✔ |
Developing the Library
Feel more than free to fork this repo, improve it in any way you'd prefer, and send us a pull request :)
Running the tests
$ go test
License
This code is free to use under the terms of the MIT license.
Documentation
¶
Overview ¶
Package pusher is the Golang library for interacting with the Pusher HTTP API.
This package lets you trigger events to your client and query the state of your Pusher channels. When used with a server, you can validate Pusher webhooks and authenticate private- or presence-channels.
In order to use this library, you need to have a free account on http://pusher.com. After registering, you will need the application credentials for your app.
Getting Started ¶
To create a new client, pass in your application credentials to a `pusher.Client` struct:
pusherClient := pusher.Client{ AppID: "your_app_id", Key: "your_app_key", Secret: "your_app_secret", }
To start triggering events on a channel, we call `pusherClient.Trigger`:
data := map[string]string{"message": "hello world"} // trigger an event on a channel, along with a data payload pusherClient.Trigger("test_channel", "event", data)
Read on to see what more you can do with this library, such as authenticating private- and presence-channels, validating Pusher webhooks, and querying the HTTP API to get information about your channels.
Author: Jamie Patel, Pusher
Index ¶
- type Channel
- type ChannelListItem
- type ChannelsList
- type Client
- func (c *Client) AuthenticatePresenceChannel(params []byte, member MemberData) (response []byte, err error)
- func (c *Client) AuthenticatePrivateChannel(params []byte) (response []byte, err error)
- func (c *Client) Channel(name string, additionalQueries map[string]string) (*Channel, error)
- func (c *Client) Channels(additionalQueries map[string]string) (*ChannelsList, error)
- func (c *Client) GetChannelUsers(name string) (*Users, error)
- func (c *Client) Trigger(channel string, eventName string, data interface{}) error
- func (c *Client) TriggerBatch(batch []Event) error
- func (c *Client) TriggerExclusive(channel string, eventName string, data interface{}, socketID string) error
- func (c *Client) TriggerMulti(channels []string, eventName string, data interface{}) error
- func (c *Client) TriggerMultiExclusive(channels []string, eventName string, data interface{}, socketID string) error
- func (c *Client) Webhook(header http.Header, body []byte) (*Webhook, error)
- type EncryptedMessage
- type Event
- type MemberData
- type User
- type Users
- type Webhook
- type WebhookEvent
Constants ¶
Variables ¶
Functions ¶
Types ¶
type Channel ¶
type Channel struct { Name string Occupied bool `json:"occupied,omitempty"` UserCount int `json:"user_count,omitempty"` SubscriptionCount int `json:"subscription_count,omitempty"` }
Channel represents the information about a channel from the Pusher API.
type ChannelListItem ¶
type ChannelListItem struct {
UserCount int `json:"user_count"`
}
ChannelListItem represents an item within ChannelsList
type ChannelsList ¶
type ChannelsList struct {
Channels map[string]ChannelListItem `json:"channels"`
}
ChannelsList represents a list of channels received by the Pusher API.
type Client ¶
type Client struct { AppID string Key string Secret string Host string // host or host:port pair Secure bool // true for HTTPS Cluster string HTTPClient *http.Client EncryptionMasterKey string // deprecated EncryptionMasterKeyBase64 string // for E2E // contains filtered or unexported fields }
Client to the HTTP API of Pusher.
There easiest way to configure the library is by creating a `Pusher` instance:
client := pusher.Client{ AppID: "your_app_id", Key: "your_app_key", Secret: "your_app_secret", }
To ensure requests occur over HTTPS, set the `Secure` property of a `pusher.Client` to `true`.
client.Secure = true // false by default
If you wish to set a time-limit for each HTTP request, set the `Timeout` property to an instance of `time.Duration`, for example:
client.Timeout = time.Second * 3 // 5 seconds by default
Changing the `pusher.Client`'s `Host` property will make sure requests are sent to your specified host.
client.Host = "foo.bar.com" // by default this is "api.pusherapp.com".
func ClientFromEnv ¶
ClientFromEnv allows instantiation of a client from an environment variable. This is particularly relevant if you are using Pusher as a Heroku add-on, which stores credentials in a `"PUSHER_URL"` environment variable. For example:
client := pusher.ClientFromEnv("PUSHER_URL")
func ClientFromURL ¶
ClientFromURL allows client instantiation from a specially-crafted Pusher URL.
c := pusher.ClientFromURL("http://key:secret@api.pusherapp.com/apps/app_id")
func (*Client) AuthenticatePresenceChannel ¶
func (c *Client) AuthenticatePresenceChannel(params []byte, member MemberData) (response []byte, err error)
AuthenticatePresenceChannel allows you to authenticate a users subscription to a presence channel. It returns authentication signature to send back to the client and authorize them. In order to identify a user, clients are sent a user_id and, optionally, custom data.
In this library, one does this by passing a `pusher.MemberData` instance.
params, _ := ioutil.ReadAll(req.Body) presenceData := pusher.MemberData{ UserID: "1", UserInfo: map[string]string{ "twitter": "jamiepatel", }, } response, err := client.AuthenticatePresenceChannel(params, presenceData) if err != nil { panic(err) } fmt.Fprintf(res, response)
func (*Client) AuthenticatePrivateChannel ¶
AuthenticatePrivateChannel allows you to authenticate a users subscription to a private channel. It returns authentication signature to send back to the client and authorize them.
For more information see our docs: http://pusher.com/docs/authenticating_users.
This is an example of authenticating a private-channel, using the built-in Golang HTTP library to start a server.
In order to authorize a client, one must read the response into type `[]byte` and pass it in. This will return a signature in the form of a `[]byte` for you to send back to the client.
func pusherAuth(res http.ResponseWriter, req *http.Request) { params, _ := ioutil.ReadAll(req.Body) response, err := client.AuthenticatePrivateChannel(params) if err != nil { panic(err) } fmt.Fprintf(res, string(response)) } func main() { http.HandleFunc("/pusher/auth", pusherAuth) http.ListenAndServe(":5000", nil) }
func (*Client) Channel ¶
Channel allows you to get the state of a single channel. The parameter `additionalQueries` is a map with query options. An `"info"` key can have comma-separated vales of `"user_count"`, for presence-channels, and `"subscription_count"`, for all-channels. Note that the subscription count is not allowed by default. Please contact us at http://support.pusher.com if you wish to enable this. Pass in `nil` if you do not wish to specify any query attributes.
channelParams := map[string]string{ "info": "user_count,subscription_count", } channel, err := client.Channel("presence-chatroom", channelParams) //channel=> &{Name:presence-chatroom Occupied:true UserCount:42 SubscriptionCount:42}
func (*Client) Channels ¶
func (c *Client) Channels(additionalQueries map[string]string) (*ChannelsList, error)
Channels returns a list of all the channels in an application. The parameter `additionalQueries` is a map with query options. A key with `"filter_by_prefix"` will filter the returned channels. To get number of users subscribed to a presence-channel, specify an `"info"` key with value `"user_count"`. Pass in `nil` if you do not wish to specify any query attributes
channelsParams := map[string]string{ "filter_by_prefix": "presence-", "info": "user_count", } channels, err := client.Channels(channelsParams) //channels=> &{Channels:map[presence-chatroom:{UserCount:4} presence-notifications:{UserCount:31} ]}
func (*Client) GetChannelUsers ¶
GetChannelUsers returns a list of users in a presence-channel by passing to this method the channel name.
users, err := client.GetChannelUsers("presence-chatroom") //users=> &{List:[{ID:13} {ID:90}]}
func (*Client) Trigger ¶
Trigger triggers an event to the Pusher API. It is possible to trigger an event on one or more channels. Channel names can contain only characters which are alphanumeric, `_` or `-“ and have to be at most 200 characters long. Event name can be at most 200 characters long too.
Pass in the channel's name, the event's name, and a data payload. The data payload must be marshallable into JSON.
data := map[string]string{"hello": "world"} client.Trigger("greeting_channel", "say_hello", data)
func (*Client) TriggerBatch ¶
TriggerBatch triggers multiple events on multiple channels in a single call:
client.TriggerBatch([]pusher.Event{ { Channel: "donut-1", Name: "ev1", Data: "d1" }, { Channel: "private-encrypted-secretdonut", Name: "ev2", Data: "d2" }, })
func (*Client) TriggerExclusive ¶
func (c *Client) TriggerExclusive(channel string, eventName string, data interface{}, socketID string) error
TriggerExclusive triggers an event excluding a recipient whose connection has the `socket_id` you specify here from receiving the event. You can read more here: http://pusher.com/docs/duplicates.
client.TriggerExclusive("a_channel", "event", data, "123.12")
func (*Client) TriggerMulti ¶
TriggerMulti is the same as `client.Trigger`, except one passes in a slice of `channels` as the first parameter. The maximum length of channels is 100.
client.TriggerMulti([]string{"a_channel", "another_channel"}, "event", data)
func (*Client) TriggerMultiExclusive ¶
func (c *Client) TriggerMultiExclusive(channels []string, eventName string, data interface{}, socketID string) error
TriggerMultiExclusive triggers an event to multiple channels excluding a recipient whose connection has the `socket_id` you specify here from receiving the event on any of the channels.
client.TriggerMultiExclusive([]string{"a_channel", "another_channel"}, "event", data, "123.12")
func (*Client) Webhook ¶
Webhook allows you to check that a Webhook you receive is indeed from Pusher, by checking the token and authentication signature in the header of the request. On your dashboard at http://app.pusher.com, you can set up webhooks to POST a payload to your server after certain events. Such events include channels being occupied or vacated, members being added or removed in presence-channels, or after client-originated events. For more information see https://pusher.com/docs/webhooks.
If the webhook is valid, a `*pusher.Webhook* will be returned, and the `err` value will be nil. If it is invalid, the first return value will be nil, and an error will be passed.
func pusherWebhook(res http.ResponseWriter, req *http.Request) { body, _ := ioutil.ReadAll(req.Body) webhook, err := client.Webhook(req.Header, body) if err != nil { fmt.Println("Webhook is invalid :(") } else { fmt.Printf("%+v\n", webhook.Events) } }
type EncryptedMessage ¶
EncryptedMessage contains an encrypted message
type MemberData ¶
type MemberData struct { UserID string `json:"user_id"` UserInfo map[string]string `json:"user_info,omitempty"` }
MemberData represents what to assign to a channel member, consisting of a `UserID` and any custom `UserInfo`.
type User ¶
type User struct {
ID string `json:"id"`
}
User represents a user and contains their ID.
type Users ¶
type Users struct {
List []User `json:"users"`
}
Users represents a list of users in a presence-channel
type Webhook ¶
type Webhook struct { TimeMs int `json:"time_ms"` // the timestamp of the request Events []WebhookEvent `json:"events"` // the events associated with the webhook }
Webhook is the parsed form of a valid webhook received by the server.
type WebhookEvent ¶
type WebhookEvent struct { Name string `json:"name"` // the type of the event Channel string `json:"channel"` // the channel on which it was sent Event string `json:"event,omitempty"` // the name of the event Data string `json:"data,omitempty"` // the data associated with the event SocketID string `json:"socket_id,omitempty"` // the socket_id of the sending socket UserID string `json:"user_id,omitempty"` // the user_id of a member who has joined or vacated a presence-channel }
WebhookEvent is the parsed form of a valid webhook event received by the server.