signalr

package module
v0.3.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 19, 2019 License: MIT Imports: 20 Imported by: 25

README

GoDoc Build Status Go Report Card Maintainability codecov

Overview

This is my personal attempt at implementating the client side of the WebSocket portion of the SignalR protocol. I use it for various virtual currency trading platforms that use SignalR.

It supports CloudFlare-protected sites by default.

Examples

Simple example:

package main

import (
	"log"

	"github.com/carterjones/signalr"
)

func main() {
	// Prepare a SignalR client.
	c := signalr.New(
		"fake-server.definitely-not-real",
		"1.5",
		"/signalr",
		`[{"name":"awesomehub"}]`,
		nil,
	)

	// Define message and error handlers.
	msgHandler := func(msg signalr.Message) { log.Println(msg) }
	panicIfErr := func(err error) {
		if err != nil {
			log.Panic(err)
		}
	}

	// Start the connection.
	err := c.Run(msgHandler, panicIfErr)
	panicIfErr(err)

	// Wait indefinitely.
	select {}
}

Generic usage:

Cryptocurrency examples:

Proxy examples:

Documentation

Contribute

If anything is unclear or could be improved, please open an issue or submit a pull request. Thanks!

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() {
	// Prepare a SignalR client.
	c := signalr.New(
		"fake-server.definitely-not-real",
		"1.5",
		"/signalr",
		`[{"name":"awesomehub"}]`,
		nil,
	)

	// Define message and error handlers.
	msgHandler := func(msg signalr.Message) { log.Println(msg) }
	panicIfErr := func(err error) {
		if err != nil {
			log.Panic(err)
		}
	}

	// Start the connection.
	err := c.Run(msgHandler, panicIfErr)
	panicIfErr(err)

	// Wait indefinitely.
	select {}
}
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() {
	// Prepare a SignalR client.
	c := signalr.New(
		"fake-server.definitely-not-real",
		"1.5",
		"/signalr",
		`[{"name":"awesomehub"}]`,
		map[string]string{"custom-key": "custom-value"},
	)

	// Perform any optional modifications to the client here. Read the docs for
	// all the available options that are exposed via public fields.

	// Define message and error handlers.
	msgHandler := func(msg signalr.Message) { log.Println(msg) }
	panicIfErr := func(err error) {
		if err != nil {
			log.Panic(err)
		}
	}

	// Manually perform the initialization routine.
	err := c.Negotiate()
	panicIfErr(err)
	conn, err := c.Connect()
	panicIfErr(err)
	err = c.Start(conn)
	panicIfErr(err)

	// Begin the message reading loop.
	go c.ReadMessages(msgHandler, panicIfErr)

	// Wait indefinitely.
	select {}
}
Output:

Index

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.

func TestStart added in v0.1.4

func TestStart(w http.ResponseWriter, r *http.Request)

TestStart provides a sample "/start" handling function.

If an error occurs while writing the response data, it will panic.

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

	// The maximum amount of time to spend retrying a reconnect attempt.
	MaxReconnectAttemptDuration 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.
	//
	// This is an example groups token:
	// nolint:lll
	// yUcSohHrAZGEwK62B4Ao0WYac82p5yeRvHHInBgVmSK7jX++ym3kIgDy466yW/gRPp2l3Py8G45mRLJ9FslB3sKfsDPUNWL1b54cvjaSXCUo0znzyACxrN2Y0kNLR59h7hb6PgOSfy3Z2R5CUSVm5LZg6jg=
	GroupsToken SafeString

	// The message ID that is used during reconnect attempts.
	//
	// This is an example message ID: d-8B839DC3-C,0|aaZe,0|aaZf,2|C1,2A801
	MessageID SafeString

	// 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 New

func New(host, protocol, endpoint, connectionData string, params map[string]string) *Client

New creates and initializes a SignalR client.

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) Connect

func (c *Client) Connect() (*websocket.Conn, error)

Connect implements the connect step of the SignalR connection sequence.

func (*Client) Negotiate

func (c *Client) Negotiate() error

Negotiate implements the negotiate step of the SignalR connection sequence.

func (*Client) ReadMessages

func (c *Client) ReadMessages(msgHandler MsgHandler, errHandler ErrHandler)

ReadMessages processes WebSocket messages from the underlying websocket connection.

func (*Client) Reconnect

func (c *Client) Reconnect() (*websocket.Conn, error)

Reconnect implements the reconnect step of the SignalR connection sequence.

func (*Client) Run

func (c *Client) Run(msgHandler MsgHandler, errHandler ErrHandler) error

Run connects to the host and performs the websocket initialization routines that are part of the SignalR specification.

Example
package main

import (
	"log"

	"github.com/carterjones/signalr"
)

func main() {
	// Prepare a SignalR client.
	c := signalr.New(
		"fake-server.definitely-not-real",
		"1.5",
		"/signalr",
		`[{"name":"awesomehub"}]`,
		nil,
	)

	// Define handlers.
	msgHandler := func(msg signalr.Message) { log.Println(msg) }
	panicIfErr := func(err error) {
		if err != nil {
			log.Panic(err)
		}
	}

	// Start the connection.
	err := c.Run(msgHandler, panicIfErr)
	if err != nil {
		log.Panic(err)
	}

	// Wait indefinitely.
	select {}
}
Output:

func (*Client) Send

func (c *Client) Send(m hubs.ClientMsg) error

Send sends a message to the websocket connection.

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 ErrHandler added in v0.3.0

type ErrHandler func(err error)

ErrHandler processes an error.

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 MsgHandler added in v0.3.0

type MsgHandler func(msg Message)

MsgHandler processes a Message.

type SafeString added in v0.2.3

type SafeString struct {
	// contains filtered or unexported fields
}

SafeString is a thread-safe string.

func (*SafeString) Get added in v0.2.3

func (s *SafeString) Get() string

Get returns the string value.

func (*SafeString) Set added in v0.2.3

func (s *SafeString) Set(str string)

Set sets the string value.

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.

const (
	// HTTPS is the literal string, "https".
	HTTPS Scheme = "https"

	// HTTP is the literal string, "http".
	HTTP Scheme = "http"

	// WSS is the literal string, "wss".
	WSS Scheme = "wss"

	// WS is the literal string, "ws".
	WS Scheme = "ws"
)

type WebsocketConn

type WebsocketConn interface {
	// ReadMessage is modeled after the function 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
	ReadMessage() (messageType int, p []byte, err error)

	// WriteJSON is modeled after the function 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.
	WriteJSON(v interface{}) error
}

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.

Directories

Path Synopsis
examples
Package hubs provides functionality used by the SignalR Hubs API.
Package hubs provides functionality used by the SignalR Hubs API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL