Documentation ¶
Overview ¶
Package signalr provides the client side implementation of the WebSocket portion of the SignalR protocol.
First things first: this was almost entirely written using https://blog.3d-logic.com/2015/03/29/signalr-on-the-wire-an-informal-description-of-the-signalr-protocol/ as a reference guide. It is an excellent technical write-up. Many thanks to Pawel Kadluczka for writing that and sharing it with the public. If you want deep-dive technical details of how this all works, read that blog. I won't try to replicate it here.
At a high level, the WebSocket portion of SignalR goes through the following steps:
- negotiate: use HTTP/HTTPS to get connection info for how to connect to the websocket endpoint
- connect: attempt to connect to the websocket endpoint
- start: make the WebSocket connection usable by SignalR connections
See the provided examples for how to use this library.
Example (Basic) ¶
This example shows the most basic way to start a websocket connection.
package main import ( "log" "github.com/carterjones/signalr" ) func main() { host := "myhost.not-real-tld" protocol := "some-protocol-version-123" endpoint := "/usually/something/like/this" connectionData := `{"custom":"data"}` // Prepare a SignalR client. c := signalr.New(host, protocol, endpoint, connectionData) // Start the connection. msgs, errs, err := c.Run() if err != nil { log.Panic(err) } // Process messages and errors. for { select { case msg := <-msgs: // Handle the message. log.Println(msg) case err := <-errs: // Handle the error. log.Panic(err) } } }
Output:
Example (Complex) ¶
This example shows how to manually perform each of the initialization steps.
package main import ( "log" "github.com/carterjones/signalr" ) func main() { host := "myhost.not-real-tld" protocol := "some-protocol-version-123" endpoint := "/usually/something/like/this" connectionData := `{"custom":"data"}` // Prepare a SignalR client. c := signalr.New(host, protocol, endpoint, connectionData) // Perform any optional modifications to the client here. Read the docs for // all the available options that are exposed via public fields. // Manually perform the initialization routine. err := c.Negotiate() if err != nil { log.Panic(err) } conn, err := c.Connect() if err != nil { log.Panic(err) } err = c.Start(conn) if err != nil { log.Panic(err) } // Create message and error channels. msgs := make(chan signalr.Message) errs := make(chan error) // Begin the message reading loop. go c.ReadMessages(msgs, errs) // Process messages and errors. for { select { case msg := <-msgs: // Handle the message. log.Println(msg) case err := <-errs: // Handle the error. log.Panic(err) } } }
Output:
Index ¶
- func TestCompleteHandler(w http.ResponseWriter, r *http.Request)
- func TestConnect(w http.ResponseWriter, r *http.Request)
- func TestNegotiate(w http.ResponseWriter, r *http.Request)
- func TestReconnect(w http.ResponseWriter, r *http.Request)
- func TestStart(w http.ResponseWriter, r *http.Request)
- type Client
- func (c *Client) Close()
- func (c *Client) Conn() WebsocketConn
- func (c *Client) Connect() (*websocket.Conn, error)
- func (c *Client) Negotiate() error
- func (c *Client) ReadMessages(msgCh chan Message, errCh chan error)
- func (c *Client) Reconnect() (*websocket.Conn, error)
- func (c *Client) Run() (chan Message, chan error, error)
- func (c *Client) Send(m hubs.ClientMsg) error
- func (c *Client) SetConn(conn WebsocketConn)
- func (c *Client) Start(conn WebsocketConn) error
- type JSONWriter
- type Message
- type MessageReader
- type Scheme
- type WebsocketConn
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func TestCompleteHandler ¶ added in v0.1.4
func TestCompleteHandler(w http.ResponseWriter, r *http.Request)
TestCompleteHandler combines the negotiate, connect, reconnect, and start handlers found in this package into one complete response handler.
func TestConnect ¶ added in v0.1.4
func TestConnect(w http.ResponseWriter, r *http.Request)
TestConnect provides a sample "/connect" handling function.
If an error occurs while upgrading the websocket, it will panic.
func TestNegotiate ¶ added in v0.1.4
func TestNegotiate(w http.ResponseWriter, r *http.Request)
TestNegotiate provides a sample "/negotiate" handling function.
If an error occurs while writing the response data, it will panic.
func TestReconnect ¶ added in v0.1.4
func TestReconnect(w http.ResponseWriter, r *http.Request)
TestReconnect provides a sample "/reconnect" handling function. It simply calls TestConnect.
Types ¶
type Client ¶
type Client struct { // The host providing the SignalR service. Host string // The relative path where the SignalR service is provided. Endpoint string // The websockets protocol version. Protocol string ConnectionData string // The HTTPClient used to initialize the websocket connection. HTTPClient *http.Client // An optional setting to provide a non-default TLS configuration to use // when connecting to the websocket. TLSClientConfig *tls.Config // Either HTTPS or HTTP. Scheme Scheme // The maximum number of times to re-attempt a negotiation. MaxNegotiateRetries int // The maximum number of times to re-attempt a connection. MaxConnectRetries int // The maximum number of times to re-attempt a reconnection. MaxReconnectRetries int // The maximum number of times to re-attempt a start command. MaxStartRetries int // The time to wait before retrying, in the event that an error occurs // when contacting the SignalR service. RetryWaitDuration time.Duration // This is the connection token set during the negotiate phase of the // protocol and used to uniquely identify the connection to the server // in all subsequent phases of the connection. ConnectionToken string // This is the ID of the connection. It is set during the negotiate // phase and then ignored by all subsequent steps. ConnectionID string // Header values that should be applied to all HTTP requests. Headers map[string]string // This value is not part of the SignalR protocol. If this value is set, // it will be used in debug messages. CustomID string // contains filtered or unexported fields }
Client represents a SignlR client. It manages connections so that the caller doesn't have to.
func (*Client) Close ¶
func (c *Client) Close()
Close sends a signal to the loop reading WebSocket messages to indicate that the loop should terminate.
func (*Client) Conn ¶
func (c *Client) Conn() WebsocketConn
Conn returns the underlying websocket connection.
func (*Client) Negotiate ¶
Negotiate implements the negotiate step of the SignalR connection sequence.
func (*Client) ReadMessages ¶
ReadMessages processes WebSocket messages from the underlying websocket connection. When a message is processed, it is passed along the msgCh channel. When an error ocurrs, it is sent along the errCh channel.
func (*Client) Reconnect ¶
Reconnect implements the reconnect step of the SignalR connection sequence.
func (*Client) Run ¶
Run connects to the host and performs the websocket initialization routines that are part of the SignalR specification. It returns channels that:
- receive messages from the websocket connection
- receive errors encountered while processing the weblocket connection
Example ¶
package main import ( "log" "time" "github.com/carterjones/signalr" ) func main() { // Prepare a SignalR client. c := signalr.New("fake-server.definitely-not-real", "123", "my-endpoint", "special connection data") // Start the connection. msgs, errs, err := c.Run() if err != nil { log.Panic(err) } // Process messages and errors. for { select { case msg := <-msgs: // Handle the message. log.Println(msg) case err := <-errs: // Handle the error. log.Panic(err) case <-time.After(2 * time.Second): log.Println("exiting, since we haven't sente messages") } } }
Output:
func (*Client) SetConn ¶
func (c *Client) SetConn(conn WebsocketConn)
SetConn changes the underlying websocket connection to the specified connection. This is done using a mutex to wait until existing read operations have completed.
func (*Client) Start ¶
func (c *Client) Start(conn WebsocketConn) error
Start implements the start step of the SignalR connection sequence.
type JSONWriter ¶
type JSONWriter interface {
WriteJSON(v interface{}) error
}
JSONWriter is the interface that wraps WriteJSON.
WriteJSON is defined at https://godoc.org/github.com/gorilla/websocket#Conn.WriteJSON
At a high level, it writes a structure to the underlying websocket and returns any error that was encountered during the write operation.
type Message ¶
type Message struct { // message id, present for all non-KeepAlive messages C string // an array containing actual data M []hubs.ClientMsg // indicates that the transport was initialized (a.k.a. init message) S int // groups token – an encrypted string representing group membership G string // other miscellaneous variables that sometimes are sent by the server I string E string R json.RawMessage H json.RawMessage // could be bool or string depending on a message type D json.RawMessage T json.RawMessage }
Message represents a message sent from the server to the persistent websocket connection.
type MessageReader ¶
MessageReader is the interface that wraps ReadMessage.
ReadMessage is defined at https://godoc.org/github.com/gorilla/websocket#Conn.ReadMessage
At a high level, it reads messages and returns:
- the type of message read
- the bytes that were read
- any errors encountered during reading the message
type Scheme ¶
type Scheme string
Scheme represents a type of transport scheme. For the purposes of this project, we only provide constants for schemes relevant to HTTP and websockets.
type WebsocketConn ¶
type WebsocketConn interface { MessageReader JSONWriter }
WebsocketConn is a combination of MessageReader and JSONWriter. It is used to provide an interface to objects that can read from and write to a websocket connection.