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, nil)
// 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"}`
params := map[string]string{"custom-key": "custom-value"}
// Prepare a SignalR client.
c := signalr.New(host, protocol, endpoint, connectionData, params)
// 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
// Connection data passed to the service's websocket.
ConnectionData string
// User-defined custom parameters passed with each request to the server.
Params map[string]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
// The groups token that is used during reconnect attempts.
GroupsToken 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", nil)
// 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.