signalr

package module
v0.3.6-0...-9b6f97c Latest Latest
Warning

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

Go to latest
Published: Apr 6, 2021 License: MIT Imports: 21 Imported by: 0

README

GoDoc Build Status Go Report Card Maintainability codecov

Project depricated

Unfortunately, I am no longer able to provide support for this project. Please see https://github.com/carterjones/signalr/network for some forks that have been created.

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/sirupsen/logrus"

	"github.com/dipdup-net/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/sirupsen/logrus"

	"github.com/dipdup-net/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

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

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

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

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

TestReconnect provides a sample "/reconnect" handling function. It simply calls TestConnect.

func TestStart

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/sirupsen/logrus"

	"github.com/dipdup-net/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

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 stdJSON.RawMessage
	H stdJSON.RawMessage // could be bool or string depending on a message type
	D stdJSON.RawMessage
	T stdJSON.RawMessage
}

Message represents a message sent from the server to the persistent websocket connection.

type MsgHandler

type MsgHandler func(msg Message)

MsgHandler processes a Message.

type SafeString

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

SafeString is a thread-safe string.

func (*SafeString) Get

func (s *SafeString) Get() string

Get returns the string value.

func (*SafeString) Set

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

Jump to

Keyboard shortcuts

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