Documentation ¶
Overview ¶
Package signalr contains a SignalR client and a SignalR server. Both support the transport types Websockets and Server-Sent Events and the transfer formats Text (JSON) and Binary (MessagePack).
Basics ¶
The SignalR Protocol is a protocol for two-way RPC over any stream- or message-based transport. Either party in the connection may invoke procedures on the other party, and procedures can return zero or more results or an error. Typically, SignalR connections are HTTP-based, but it is dead simple to implement a signalr.Connection on any transport that supports io.Reader and io.Writer.
Client ¶
A Client can be used in client side code to access server methods. From an existing connection, it can be created with NewClient().
// NewClient with raw TCP connection and MessagePack encoding conn, err := net.Dial("tcp", "example.com:6502") client := NewClient(ctx, WithConnection(NewNetConnection(ctx, conn)), TransferFormat("Binary), WithReceiver(receiver)) client.Start()
A special case is NewHTTPClient(), which creates a Client from a server address and negotiates with the server which kind of connection (Websockets, Server-Sent Events) will be used.
// Configurable HTTP connection conn, err := NewHTTPConnection(ctx, "http://example.com/hub", WithHTTPHeaders(..)) // Client with JSON encoding client, err := NewClient(ctx, WithConnection(conn), TransferFormat("Text"), WithReceiver(receiver)) client.Start()
The object which will receive server callbacks is passed to NewClient() by using the WithReceiver option. After calling client.Start(), the client is ready to call server methods or to receive callbacks.
Server ¶
A Server provides the public methods of a server side class over signalr to the client. Such a server side class is called a hub and must implement HubInterface. It is reasonable to derive your hubs from the Hub struct type, which already implements HubInterface. Servers for arbitrary connection types can be created with NewServer().
// Typical server with log level debug to Stderr server, err := NewServer(ctx, SimpleHubFactory(hub), Logger(log.NewLogfmtLogger(os.Stderr), true))
To serve a connection, call server.Serve(connection) in a goroutine. Serve ends when the connection is closed or the servers context is canceled.
// Serving over TCP, accepting client who use MessagePack or JSON addr, _ := net.ResolveTCPAddr("tcp", "localhost:6502") listener, _ := net.ListenTCP("tcp", addr) tcpConn, _ := listener.Accept() go server.Serve(NewNetConnection(conn))
To server a HTTP connection, use server.MapHTTP(), which connects the server with a path in an http.ServeMux. The server then automatically negotiates which kind of connection (Websockets, Server-Sent Events) will be used.
// build a signalr.Server using your hub // and any server options you may need server, _ := signalr.NewServer(ctx, signalr.SimpleHubFactory(&AppHub{}) signalr.KeepAliveInterval(2*time.Second), signalr.Logger(kitlog.NewLogfmtLogger(os.Stderr), true)) ) // create a new http.ServerMux to handle your app's http requests router := http.NewServeMux() // ask the signalr server to map it's server // api routes to your custom baseurl server.MapHTTP(signalr.WithHTTPServeMux(router), "/chat") // in addition to mapping the signalr routes // your mux will need to serve the static files // which make up your client-side app, including // the signalr javascript files. here is an example // of doing that using a local `public` package // which was created with the go:embed directive // // fmt.Printf("Serving static content from the embedded filesystem\n") // router.Handle("/", http.FileServer(http.FS(public.FS))) // bind your mux to a given address and start handling requests fmt.Printf("Listening for websocket connections on http://%s\n", address) if err := http.ListenAndServe(address, router); err != nil { log.Fatal("ListenAndServe:", err) }
Supported method signatures ¶
The SignalR protocol constrains the signature of hub or receiver methods that can be used over SignalR. All methods with serializable types as parameters and return types are supported. Methods with multiple return values are not generally supported, but returning one or no value and an optional error is supported.
// Simple signatures for hub/receiver methods func (mh *MathHub) Divide(a, b float64) (float64, error) // error on division by zero func (ah *AlgoHub) Sort(values []string) []string func (ah *AlgoHub) FindKey(value []string, dict map[int][]string) (int, error) // error on not found func (receiver *View) DisplayServerValue(value interface{}) // will work for every serializable value
Methods which return a single sending channel (<-chan), and optionally an error, are used to initiate callee side streaming. The caller will receive the contents of the channel as stream. When the returned channel is closed, the stream will be completed.
// Streaming methods func (n *Netflix) Stream(show string, season, episode int) (<-chan []byte, error) // error on password shared
Methods with one or multiple receiving channels (chan<-) as parameters are used as receivers for caller side streaming. The caller invokes this method and pushes one or multiple streams to the callee. The method should end when all channels are closed. A channel is closed by the server when the assigned stream is completed.
// Caller side streaming func (mh *MathHub) MultiplyAndSum(a, b chan<- float64) float64
In most cases, the caller will be the client and the callee the server. But the vice versa case is also possible.
Index ¶
- Variables
- func AllowOriginPatterns(origins []string) func(Party) error
- func ChanReceiveTimeout(timeout time.Duration) func(Party) error
- func CtxPipe(ctx context.Context) (*PipeReader, *PipeWriter)
- func EnableDetailedErrors(enable bool) func(Party) error
- func HTTPTransports(transports ...string) func(Party) error
- func HandshakeTimeout(timeout time.Duration) func(Party) error
- func HubFactory(factory func() HubInterface) func(Party) error
- func InsecureSkipVerify(skip bool) func(Party) error
- func KeepAliveInterval(interval time.Duration) func(Party) error
- func Logger(logger StructuredLogger, debug bool) func(Party) error
- func MaximumReceiveMessageSize(size uint) func(Party) error
- func NewNetConnection(ctx context.Context, conn net.Conn) *netConnection
- func SimpleHubFactory(hubProto HubInterface) func(Party) error
- func StreamBufferCapacity(capacity uint) func(Party) error
- func TimeoutInterval(timeout time.Duration) func(Party) error
- func TransferFormat(format string) func(Party) error
- func UseHub(hub HubInterface) func(Party) error
- func WithAutoReconnect(connectionFactory func() (Connection, error)) func(Party) error
- func WithConnection(connection Connection) func(party Party) error
- func WithHTTPClient(client Doer) func(*httpConnection) error
- func WithHTTPHeaders(headers func() http.Header) func(*httpConnection) error
- func WithHTTPServeMux(serveMux *http.ServeMux) func() MappableRouter
- func WithReceiver(receiver interface{}) func(Party) error
- type Client
- type ClientProxy
- type ClientState
- type Connection
- type ConnectionBase
- type ConnectionWithTransferMode
- type Doer
- type GroupManager
- type Hub
- func (h *Hub) Abort()
- func (h *Hub) Clients() HubClients
- func (h *Hub) ConnectionID() string
- func (h *Hub) Context() context.Context
- func (h *Hub) Groups() GroupManager
- func (h *Hub) Initialize(ctx HubContext)
- func (h *Hub) Items() *sync.Map
- func (h *Hub) Logger() (info StructuredLogger, dbg StructuredLogger)
- func (h *Hub) OnConnected(string)
- func (h *Hub) OnDisconnected(string)
- type HubClients
- type HubContext
- type HubInterface
- type HubLifetimeManager
- type InvokeResult
- type MappableRouter
- type Party
- type PipeReader
- type PipeWriter
- type Receiver
- type ReceiverInterface
- type Server
- type StructuredLogger
- type TransferMode
Constants ¶
This section is empty.
Variables ¶
var ErrClosedPipe = errors.New("io: read/write on closed pipe")
ErrClosedPipe is the error used for read or write operations on a closed pipe.
Functions ¶
func AllowOriginPatterns ¶
AllowOriginPatterns lists the host patterns for authorized origins which is used for avoid same origin strategy. See https://pkg.go.dev/nhooyr.io/websocket#AcceptOptions
func ChanReceiveTimeout ¶
ChanReceiveTimeout is the timeout for processing stream items from the client, after StreamBufferCapacity was reached If the hub method is not able to process a stream item during the timeout duration, the server will send a completion with error. Default is 5 seconds.
func CtxPipe ¶
func CtxPipe(ctx context.Context) (*PipeReader, *PipeWriter)
CtxPipe creates a synchronous in-memory pipe. It can be used to connect code expecting an io.Reader with code expecting an io.Writer.
By canceling the context, Read and Write can be canceled ¶
Reads and Writes on the pipe are matched one to one except when multiple Reads are needed to consume a single Write. That is, each Write to the PipeWriter blocks until it has satisfied one or more Reads from the PipeReader that fully consume the written data. The data is copied directly from the Write to the corresponding Read (or Reads); there is no internal buffering.
It is safe to call Read and Write in parallel with each other or with Close. Parallel calls to Read and parallel calls to Write are also safe: the individual calls will be gated sequentially.
func EnableDetailedErrors ¶
EnableDetailedErrors If true, detailed exception messages are returned to the other Party when an exception is thrown in a Hub method. The default is false, as these exception messages can contain sensitive information.
func HTTPTransports ¶
HTTPTransports sets the list of available transports for http connections. Allowed transports are "WebSockets", "ServerSentEvents". Default is both transports are available.
func HandshakeTimeout ¶
HandshakeTimeout is the interval if the other Party doesn't send an initial handshake message within, the connection is closed. This is an advanced setting that should only be modified if handshake timeout errors are occurring due to severe network latency. For more detail on the handshake process, see https://github.com/dotnet/aspnetcore/blob/master/src/SignalR/docs/specs/HubProtocol.md
func HubFactory ¶
func HubFactory(factory func() HubInterface) func(Party) error
HubFactory sets the function which returns the hub instance for every hub method invocation The function might create a new hub instance on every invocation. If hub instances should be created and initialized by a DI framework, the frameworks' factory method can be called here.
func InsecureSkipVerify ¶
InsecureSkipVerify disables Accepts origin verification behaviour which is used to avoid same origin strategy. See https://pkg.go.dev/nhooyr.io/websocket#AcceptOptions
func KeepAliveInterval ¶
KeepAliveInterval is the interval if the Party hasn't sent a message within, a ping message is sent automatically to keep the connection open. When changing KeepAliveInterval, change the Timeout setting on the other Party. The recommended Timeout value is double the KeepAliveInterval value. Default is 15 seconds.
func Logger ¶
func Logger(logger StructuredLogger, debug bool) func(Party) error
Logger sets the logger used by the Party to log info events. If debug is true, debug log event are generated, too
func MaximumReceiveMessageSize ¶
MaximumReceiveMessageSize is the maximum size of a single incoming hub message. Default is 32KB
func NewNetConnection ¶
NewNetConnection wraps net.Conn into a Connection
func SimpleHubFactory ¶
func SimpleHubFactory(hubProto HubInterface) func(Party) error
SimpleHubFactory sets a HubFactory which creates a new hub with the underlying type of hubProto on each hub method invocation.
func StreamBufferCapacity ¶
StreamBufferCapacity is the maximum number of items that can be buffered for client upload streams. If this limit is reached, the processing of invocations is blocked until the server processes stream items. Default is 10.
func TimeoutInterval ¶
TimeoutInterval is the interval one Party will consider the other Party disconnected if it hasn't received a message (including keep-alive) in it. The recommended value is double the KeepAliveInterval value. Default is 30 seconds.
func TransferFormat ¶
TransferFormat sets the transfer format used on the transport. Allowed values are "Text" and "Binary"
func UseHub ¶
func UseHub(hub HubInterface) func(Party) error
UseHub sets the hub instance used by the server
func WithAutoReconnect ¶
func WithAutoReconnect(connectionFactory func() (Connection, error)) func(Party) error
WithAutoReconnect makes the Client to auto reconnect using the Connection build by the connectionFactory.
func WithConnection ¶
func WithConnection(connection Connection) func(party Party) error
WithConnection sets the Connection of the Client
func WithHTTPClient ¶
WithHTTPClient sets the http client used to connect to the signalR server
func WithHTTPHeaders ¶
WithHTTPHeaders sets the function for providing request headers for HTTP and websocket requests
func WithHTTPServeMux ¶
func WithHTTPServeMux(serveMux *http.ServeMux) func() MappableRouter
WithHTTPServeMux is a MappableRouter factory for MapHTTP which converts a http.ServeMux to a MappableRouter. For factories for other routers, see github.com/philippseith/signalr/router
func WithReceiver ¶
WithReceiver sets the object which will receive server side calls to client methods (e.g. callbacks)
Types ¶
type Client ¶
type Client interface { Party Start() State() ClientState PushStateChanged(chan<- struct{}) Err() error WaitForState(ctx context.Context, waitFor ClientState) <-chan error Invoke(method string, arguments ...interface{}) <-chan InvokeResult Send(method string, arguments ...interface{}) <-chan error PullStream(method string, arguments ...interface{}) <-chan InvokeResult PushStreams(method string, arguments ...interface{}) <-chan error }
Client is the signalR connection used on the client side.
Start()
Start starts the client loop. After starting the client, the interaction with a server can be started. The client loop will run until the server closes the connection. If WithAutoReconnect is used, Start will start a new loop. To end the loop from the client side, the context passed to NewClient has to be canceled.
State() ClientState
State returns the current client state. When WithAutoReconnect is set, the client leaves ClientClosed and tries to reach ClientConnected after the last connection has ended.
PushStateChanged(chan<- struct{})
PushStateChanged pushes a new item != nil to the channel when State has changed.
Err() error
Err returns the last error occurred while running the client. When the client goes to ClientConnecting, Err is set to nil
WaitForState(ctx context.Context, waitFor ClientState) <-chan error
WaitForState returns a channel for waiting on the Client to reach a specific ClientState. The channel either returns an error if ctx or the client has been canceled. or nil if the ClientState waitFor was reached.
Invoke(method string, arguments ...interface{}) <-chan InvokeResult
Invoke invokes a method on the server and returns a channel wich will return the InvokeResult. When failing, InvokeResult.Error contains the client side error.
Send(method string, arguments ...interface{}) <-chan error
Send invokes a method on the server but does not return a result from the server but only a channel, which might contain a client side error occurred while sending.
PullStream(method string, arguments ...interface{}) <-chan InvokeResult
PullStream invokes a streaming method on the server and returns a channel which delivers the stream items. For more info about Streaming see https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/docs/specs/HubProtocol.md#streaming
PushStreams(method string, arguments ...interface{}) <-chan error
PushStreams pushes all items received from its arguments of type channel to the server (Upload Streaming). For more info about Upload Streaming see https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/docs/specs/HubProtocol.md#upload-streaming
type ClientProxy ¶
type ClientProxy interface {
Send(target string, args ...interface{})
}
ClientProxy allows the hub to send messages to one or more of its clients
type ClientState ¶
type ClientState int
ClientState is the state of the client.
const ( ClientCreated ClientState = iota ClientConnecting ClientConnected ClientClosed ClientError )
Client states
type Connection ¶
type Connection interface { io.Reader io.Writer Context() context.Context ConnectionID() string SetConnectionID(id string) Timeout() time.Duration SetTimeout(duration time.Duration) }
Connection describes a connection between signalR client and server
func NewHTTPConnection ¶
func NewHTTPConnection(ctx context.Context, address string, options ...func(*httpConnection) error) (Connection, error)
NewHTTPConnection creates a signalR HTTP Connection for usage with a Client. ctx can be used to cancel the SignalR negotiation during the creation of the Connection but not the Connection itself.
type ConnectionBase ¶
type ConnectionBase struct {
// contains filtered or unexported fields
}
ConnectionBase is a baseclass for implementers of the Connection interface.
func (*ConnectionBase) ConnectionID ¶
func (cb *ConnectionBase) ConnectionID() string
ConnectionID is the ID of the connection.
func (*ConnectionBase) Context ¶
func (cb *ConnectionBase) Context() context.Context
Context can be used to wait for cancellation of the Connection
func (*ConnectionBase) SetConnectionID ¶
func (cb *ConnectionBase) SetConnectionID(id string)
SetConnectionID sets the ConnectionID
func (*ConnectionBase) SetTimeout ¶
func (cb *ConnectionBase) SetTimeout(duration time.Duration)
SetTimeout sets the Timeout
func (*ConnectionBase) Timeout ¶
func (cb *ConnectionBase) Timeout() time.Duration
Timeout is the timeout of the Connection
type ConnectionWithTransferMode ¶
type ConnectionWithTransferMode interface { TransferMode() TransferMode SetTransferMode(transferMode TransferMode) }
ConnectionWithTransferMode is a Connection with TransferMode (e.g. Websocket)
type GroupManager ¶
type GroupManager interface { AddToGroup(groupName string, connectionID string) RemoveFromGroup(groupName string, connectionID string) }
GroupManager manages the client groups of the hub
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub is a base class for hubs
func (*Hub) ConnectionID ¶
ConnectionID gets the ID of the current connection
func (*Hub) Groups ¶
func (h *Hub) Groups() GroupManager
Groups returns the client groups of this hub
func (*Hub) Initialize ¶
func (h *Hub) Initialize(ctx HubContext)
Initialize initializes a hub with a HubContext
func (*Hub) Logger ¶
func (h *Hub) Logger() (info StructuredLogger, dbg StructuredLogger)
Logger returns the loggers used in this server. By this, derived hubs can use the same loggers as the server.
func (*Hub) OnConnected ¶
OnConnected is called when the hub is connected
func (*Hub) OnDisconnected ¶
OnDisconnected is called when the hub is disconnected
type HubClients ¶
type HubClients interface { All() ClientProxy Caller() ClientProxy Client(connectionID string) ClientProxy Group(groupName string) ClientProxy }
HubClients gives the hub access to various client groups All() gets a ClientProxy that can be used to invoke methods on all clients connected to the hub Caller() gets a ClientProxy that can be used to invoke methods of the current calling client Client() gets a ClientProxy that can be used to invoke methods on the specified client connection Group() gets a ClientProxy that can be used to invoke methods on all connections in the specified group
type HubContext ¶
type HubContext interface { Clients() HubClients Groups() GroupManager Items() *sync.Map ConnectionID() string Context() context.Context Abort() Logger() (info StructuredLogger, dbg StructuredLogger) }
HubContext is a context abstraction for a hub Clients gets a HubClients that can be used to invoke methods on clients connected to the hub Groups gets a GroupManager that can be used to add and remove connections to named groups Items holds key/value pairs scoped to the hubs connection ConnectionID gets the ID of the current connection Abort aborts the current connection Logger returns the logger used in this server
type HubInterface ¶
type HubInterface interface { Initialize(hubContext HubContext) OnConnected(connectionID string) OnDisconnected(connectionID string) }
HubInterface is a hubs interface
type HubLifetimeManager ¶
type HubLifetimeManager interface { OnConnected(conn hubConnection) OnDisconnected(conn hubConnection) InvokeAll(target string, args []interface{}) InvokeClient(connectionID string, target string, args []interface{}) InvokeGroup(groupName string, target string, args []interface{}) AddToGroup(groupName, connectionID string) RemoveFromGroup(groupName, connectionID string) }
HubLifetimeManager is a lifetime manager abstraction for hub instances OnConnected() is called when a connection is started OnDisconnected() is called when a connection is finished InvokeAll() sends an invocation message to all hub connections InvokeClient() sends an invocation message to a specified hub connection InvokeGroup() sends an invocation message to a specified group of hub connections AddToGroup() adds a connection to the specified group RemoveFromGroup() removes a connection from the specified group
type InvokeResult ¶
type InvokeResult struct { Value interface{} Error error }
InvokeResult is the combined value/error result for async invocations. Used as channel type.
type MappableRouter ¶
type MappableRouter interface { HandleFunc(string, func(w http.ResponseWriter, r *http.Request)) Handle(string, http.Handler) }
MappableRouter encapsulates the methods used by server.MapHTTP to configure the handlers required by the signalr protocol. this abstraction removes the explicit binding to http.ServerMux and allows use of any mux which implements those basic Handle and HandleFunc methods.
type Party ¶
type Party interface {
// contains filtered or unexported methods
}
Party is the common base of Server and Client. The Party methods are only used internally, but the interface is public to allow using Options on Party as parameters for external functions
type PipeReader ¶
type PipeReader struct {
// contains filtered or unexported fields
}
A PipeReader is the read half of a pipe.
func (*PipeReader) Close ¶
func (r *PipeReader) Close() error
Close closes the reader; subsequent writes to the write half of the pipe will return the error ErrClosedPipe.
func (*PipeReader) CloseWithError ¶
func (r *PipeReader) CloseWithError(err error) error
CloseWithError closes the reader; subsequent writes to the write half of the pipe will return the error err.
CloseWithError never overwrites the previous error if it exists and always returns nil.
func (*PipeReader) Read ¶
func (r *PipeReader) Read(data []byte) (n int, err error)
Read implements the standard Read interface: it reads data from the pipe, blocking until a writer arrives or the write end is closed. If the write end is closed with an error, that error is returned as err; otherwise err is EOF.
type PipeWriter ¶
type PipeWriter struct {
// contains filtered or unexported fields
}
A PipeWriter is the write half of a pipe.
func (*PipeWriter) Close ¶
func (w *PipeWriter) Close() error
Close closes the writer; subsequent reads from the read half of the pipe will return no bytes and EOF.
func (*PipeWriter) CloseWithError ¶
func (w *PipeWriter) CloseWithError(err error) error
CloseWithError closes the writer; subsequent reads from the read half of the pipe will return no bytes and the error err, or EOF if err is nil.
CloseWithError never overwrites the previous error if it exists and always returns nil.
func (*PipeWriter) Write ¶
func (w *PipeWriter) Write(data []byte) (n int, err error)
Write implements the standard Write interface: it writes data to the pipe, blocking until one or more readers have consumed all the data or the read end is closed. If the read end is closed with an error, that err is returned as err; otherwise err is ErrClosedPipe.
type Receiver ¶
type Receiver struct {
// contains filtered or unexported fields
}
Receiver is a base class for receivers in the client. It implements ReceiverInterface
type ReceiverInterface ¶
ReceiverInterface allows receivers to interact with the server directly from the receiver methods
Init(Client)
Init is used by the Client to connect the receiver to the server.
Server() Client
Server can be used inside receiver methods to call Client methods, e.g. Client.Send, Client.Invoke, Client.PullStream and Client.PushStreams
type Server ¶
type Server interface { Party MapHTTP(routerFactory func() MappableRouter, path string) Serve(conn Connection) error HubClients() HubClients // contains filtered or unexported methods }
Server is a SignalR server for one type of hub.
MapHTTP(mux *http.ServeMux, path string)
maps the servers hub to an path on an http.ServeMux.
Serve(conn Connection)
serves the hub of the server on one connection. The same server might serve different connections in parallel. Serve does not return until the connection is closed or the servers context is canceled.
HubClients() allows to call all HubClients of the server from server-side, non-hub code. Note that HubClients.Caller() returns nil, because there is no real caller which can be reached over a HubConnection.
type StructuredLogger ¶
type StructuredLogger interface {
Log(keyVals ...interface{}) error
}
StructuredLogger is the simplest logging interface for structured logging. See github.com/go-kit/log
type TransferMode ¶
type TransferMode int
TransferMode is either TextTransferMode or BinaryTransferMode
const ( // TextTransferMode is for UTF-8 encoded text messages like JSON. TextTransferMode TransferMode = iota + 1 // BinaryTransferMode is for binary messages like MessagePack. BinaryTransferMode )
MessageType constants.
Source Files ¶
- Invokeresult.go
- client.go
- clientoptions.go
- clientproxy.go
- clientsseconnection.go
- connection.go
- connectionbase.go
- ctxpipe.go
- doc.go
- groupmanager.go
- httpconnection.go
- httpmux.go
- hub.go
- hubclients.go
- hubconnection.go
- hubcontext.go
- hublifetimemanager.go
- hubprotocol.go
- invokeclient.go
- jsonhubprotocol.go
- loop.go
- messagepackhubprotocol.go
- negotiateresponse.go
- netconnection.go
- options.go
- party.go
- receiver.go
- server.go
- serveroptions.go
- serversseconnection.go
- streamclient.go
- streamer.go
- websocketconnection.go