webwire

package module
v1.0.0-rc1 Latest Latest
Warning

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

Go to latest
Published: Jun 13, 2018 License: MIT Imports: 19 Imported by: 28

README

Build Status Coverage Status Go Report Card GoDoc Donate

WebWire for Golang

WebWire is a high-level asynchronous duplex messaging library built on top of WebSockets and an open source binary message protocol with builtin authentication and support for UTF8 and UTF16 encoding. The webwire-go library provides both a client and a server implementation for the Go programming language. An official JavaScript client implementation is also available. WebWire provides a compact set of useful features that are not available and/or cumbersome to implement on raw WebSockets such as Request-Reply, Sessions, Thread-safety etc.

Table of Contents

Installation

Choose any stable release from the available release tags and copy the source code into your project's vendor directory: $YOURPROJECT/vendor/github.com/qbeon/webwire-go. All necessary transitive dependencies are already embedded into the webwire-go repository.

Dep

If you're using dep, just use dep ensure to add a specific version of webwire-go including all its transitive dependencies to your project: dep ensure -add github.com/qbeon/webwire-go@v1.0.0-beta1. This will remove all embedded transitive dependencies and move them to your projects vendor directory.

Go Get

You can also use go get: go get github.com/qbeon/webwire-go but beware that this will fetch the latest commit of the master branch which is currently not yet considered a stable release branch. It's therefore recommended to use dep instead.

Contribution

Feel free to report bugs and propose new features or changes in the Issues section.

Maintainers
Maintainer Role Specialization
Roman Sharkov Core Maintainer Dev (Go, JavaScript)
Daniil Trishkin CI Maintainer DevOps

WebWire Binary Protocol

WebWire is built for speed and portability implementing an open source binary protocol. Protocol Subset Diagram

The first byte defines the type of the message. Requests and replies contain an incremental 8-byte identifier that must be unique in the context of the senders' session. A 0 to 255 bytes long 7-bit ASCII encoded name is contained in the header of a signal or request message. A header-padding byte is applied in case of UTF16 payload encoding to properly align the payload sequence. Fraudulent messages are recognized by analyzing the message length, out-of-range memory access attacks are therefore prevented.

Examples

Features

Request-Reply

Clients can initiate multiple simultaneous requests and receive replies asynchronously. Requests are multiplexed through the connection similar to HTTP2 pipelining.

// Send a request to the server, will block the goroutine until replied
reply, err := client.Request("", wwr.Payload{Data: []byte("sudo rm -rf /")})
if err != nil {
  // Oh oh, request failed for some reason!
}
reply // Here we go!

Timed requests will timeout and return an error if the server doesn't manage to reply within the specified time frame.

// Send a request to the server, will block the goroutine for 200ms at max
reply, err := client.TimedRequest("", wwr.Payload{Data: []byte("hurry up!")}, 200*time.Millisecond)
if err != nil {
  // Probably timed out!
}
reply // Just in time!
Client-side Signals

Individual clients can send signals to the server. Signals are one-way messages guaranteed to arrive, though they're not guaranteed to be processed like requests are. In cases such as when the server is being shut down, incoming signals are ignored by the server and dropped while requests will acknowledge the failure.

// Send signal to server
err := client.Signal("eventA", wwr.Payload{Data: []byte("something")})
Server-side Signals

The server also can send signals to individual connected clients.

func onRequest(
  client *wwr.Client,
  _ *wwr.Message,
  _ context.Context,
) (wwr.Payload, error) {
  // Send a signal to the client before replying to the request
  client.Signal("", wwr.Payload{Data: []byte("ping!")})
  return wwr.Payload{}, nil
}
Namespaces

Different kinds of requests and signals can be differentiated using the builtin namespacing feature.

func onRequest(
  client *wwr.Client,
  message *wwr.Message,
  _ context.Context,
) (wwr.Payload, error) {
  switch message.Name {
  case "auth":
    // Authentication request
  case "query":
    // Query request
  }
  return wwr.Payload{}, nil
}
func onSignal(
  client *wwr.Client,
  message *wwr.Message,
  _ context.Context,
) {
  switch message.Name {
  case "event A":
    // handle event A
  case "event B":
    // handle event B
  }
}
Sessions

Individual connections can get sessions assigned to identify them. The state of the session is automagically synchronized between the client and the server. WebWire doesn't enforce any kind of authentication technique though, it just provides a way to authenticate a connection. WebWire also doesn't enforce any kind of session storage, the user could implement a custom session manager implementing the WebWire SessionManager interface to use any kind of volatile or persistent session storage, be it a database or a simple in-memory map.

func onRequest(
  client *wwr.Client,
  message *wwr.Message,
  _ context.Context,
) (wwr.Payload, error) {
  // Verify credentials
  if string(message.Payload.Data) != "secret:pass" {
    return wwr.Payload{}, wwr.Error {
      Code: "WRONG_CREDENTIALS",
      Message: "Incorrect username or password, try again"
    }
  }
  // Create session, will automatically synchronize to the client
  err := client.CreateSession(/*arbitrary data*/); err != nil {
    return wwr.Payload{}, fmt.Errorf("Couldn't create session for some reason")
  }
  client.Session // return wwr.Payload{}, nil
}

WebWire provides a basic file-based session manager implementation out of the box used by default when no custom session manager is defined. The default session manager creates a file with a .wwrsess extension for each opened session in the configured directory (which, by default, is the directory of the executable). During the restoration of a session the file is looked up by name using the session key, read and unmarshalled recreating the session object.

Automatic Session Restoration

The client will automatically try to restore the previously opened session during connection establishment when getting disconnected without explicitly closing the session before.

// Will automatically restore the previous session if there was any
err := client.Connect()

The session can also be restored manually given its key assuming the server didn't yet delete it. Session restoration will fail and return an error if the provided key doesn't correspond to any active session on the server or else if there's another active session already assigned to this client.

err := client.RestoreSession([]byte("yoursessionkeygoeshere"))
Automatic Connection Maintenance

The WebWire client maintains the connection fully automatically to guarantee maximum connection uptime. It will automatically reconnect in the background whenever the connection is lost.

The only things to remember are:

  • Client API methods such as client.Request, client.TimedRequest and client.RestoreSession will timeout if the server is unavailable for the entire duration of the specified timeout and thus the client fails to reconnect.
  • client.Signal will immediately return a DisconnectedErr error if there's no connection at the time the signal was sent.

This feature is entirely optional and can be disabled at will which will cause client.Request, client.TimedRequest and client.RestoreSession to immediately return a DisconnectedErr error when there's no connection at the time the request is made.

Thread Safety

It's safe to use both the session agents (those that are provided by the server through messages) and the client concurrently from multiple goroutines, the library automatically synchronizes concurrent operations.

Hooks

Various hooks provide the ability to asynchronously react to different kinds of events and control the behavior of both the client and the server.

Server-side Hooks
  • OnOptions
  • BeforeUpgrade
  • OnClientConnected
  • OnClientDisconnected
  • OnSignal
  • OnRequest
  • OnSessionKeyGeneration
  • OnSessionCreated
  • OnSessionLookup
  • OnSessionClosed
Client-side Hooks
  • OnServerSignal
  • OnSessionCreated
  • OnSessionClosed
  • OnDisconnected
Graceful Shutdown

The server will finish processing all ongoing signals and requests before closing when asked to shut down.

// Will block the calling goroutine until all handlers have finished
server.Shutdown()

While the server is shutting down new connections are refused with 503 Service Unavailable and incoming new requests from connected clients will be rejected with a special error: RegErrSrvShutdown. Any incoming signals from connected clients will be ignored during the shutdown.

Seamless JavaScript Support

The official JavaScript library enables seamless support for various JavaScript environments providing a fully compliant client implementation supporting the latest feature set of the webwire-go library.

Dependencies

This library depends on:

  • websocket version v1.2.0 by Gorilla web toolkit - A WebSocket implementation for Go.
    This library is used internally to abstract away the underlying websockets implementation.
  • tmdwg-go version v1.0.0 by QBEON - A timed wait group implementation used internally for asynchronous testing.

© 2018 Roman Sharkov roman.sharkov@qbeon.com

Documentation

Index

Constants

View Source
const (
	// MsgMinLenSignal represents the minimum binary/UTF8 encoded signal message length.
	// binary/UTF8 signal message structure:
	//  1. message type (1 byte)
	//  2. name length flag (1 byte)
	//  3. name (n bytes, optional if name length flag is 0)
	//  4. payload (n bytes, at least 1 byte)
	MsgMinLenSignal = int(3)

	// MsgMinLenSignalUtf16 represents the minimum UTF16 encoded signal message length.
	// UTF16 signal message structure:
	//  1. message type (1 byte)
	//  2. name length flag (1 byte)
	//  3. name (n bytes, optional if name length flag is 0)
	//  4. header padding (1 byte, required if name length flag is odd)
	//  5. payload (n bytes, at least 2 bytes)
	MsgMinLenSignalUtf16 = int(4)

	// MsgMinLenRequest represents the minimum binary/UTF8 encoded request message length.
	// binary/UTF8 request message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	//  3. name length flag (1 byte)
	//  4. name (from 0 to 255 bytes, optional if name length flag is 0)
	//  5. payload (n bytes, at least 1 byte or optional if name len > 0)
	MsgMinLenRequest = int(11)

	// MsgMinLenRequestUtf16 represents the minimum UTF16 encoded request message length.
	// UTF16 request message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	//  3. name length flag (1 byte)
	//  4. name (n bytes, optional if name length flag is 0)
	//  5. header padding (1 byte, required if name length flag is odd)
	//  6. payload (n bytes, at least 2 bytes)
	MsgMinLenRequestUtf16 = int(11)

	// MsgMinLenReply represents the minimum binary/UTF8 encoded reply message length.
	// binary/UTF8 reply message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	//  3. payload (n bytes, optional or at least 1 byte)
	MsgMinLenReply = int(9)

	// MsgMinLenReplyUtf16 represents the minimum UTF16 encoded reply message length
	// UTF16 reply message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	//  3. header padding (1 byte)
	//  4. payload (n bytes, optional or at least 2 bytes)
	MsgMinLenReplyUtf16 = int(10)

	// MsgMinLenErrorReply represents the minimum error reply message length
	// Error reply message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	//  3. error code length flag (1 byte, cannot be 0)
	//  4. error code (from 1 to 255 bytes, length must correspond to the length flag)
	//  5. error message (n bytes, UTF8 encoded, optional)
	MsgMinLenErrorReply = int(11)

	// MsgMinLenRestoreSession represents the minimum session restoration request message length
	// Session restoration request message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	//  3. session key (n bytes, 7-bit ASCII encoded, at least 1 byte)
	MsgMinLenRestoreSession = int(10)

	// MsgMinLenCloseSession represents the minimum session destruction request message length
	// Session destruction request message structure:
	//  1. message type (1 byte)
	//  2. message id (8 bytes)
	MsgMinLenCloseSession = int(9)

	// MsgMinLenSessionCreated represents the minimum session creation notification message length
	// Session creation notification message structure:
	//  1. message type (1 byte)
	//  2. session key (n bytes, 7-bit ASCII encoded, at least 1 byte)
	MsgMinLenSessionCreated = int(2)

	// MsgMinLenSessionClosed represents the minimum session creation notification message length
	// Session destruction notification message structure:
	//  1. message type (1 byte)
	MsgMinLenSessionClosed = int(1)
)
View Source
const (

	// MsgErrorReply is sent by the server
	// and represents an error-reply to a previously sent request
	MsgErrorReply = byte(0)

	// MsgReplyShutdown is sent by the server when a request is received during server shutdown
	// and can't therefore be processed
	MsgReplyShutdown = byte(1)

	// MsgInternalError is sent by the server if an unexpected internal error arose during
	// the processing of a request
	MsgInternalError = byte(2)

	// MsgSessionNotFound is sent by the server in response to an unfilfilled session restoration
	// request due to the session not being found
	MsgSessionNotFound = byte(3)

	// MsgMaxSessConnsReached is sent by the server in response to an authentication request
	// when the maximum number of concurrent connections for a certain session was reached
	MsgMaxSessConnsReached = byte(4)

	// MsgSessionsDisabled is sent by the server in response to a session restoration request
	// if sessions are disabled for the target server
	MsgSessionsDisabled = byte(5)

	// MsgReplyProtocolError is sent by the server in response to an invalid
	// message violating the protocol
	MsgReplyProtocolError = byte(6)

	// MsgSessionCreated is sent by the server
	// to notify the client about the session creation
	MsgSessionCreated = byte(21)

	// MsgSessionClosed is sent by the server
	// to notify the client about the session destruction
	MsgSessionClosed = byte(22)

	// MsgCloseSession is sent by the client
	// and represents a request for the destruction of the currently active session
	MsgCloseSession = byte(31)

	// MsgRestoreSession is sent by the client
	// to request session restoration
	MsgRestoreSession = byte(32)

	// MsgSignalBinary represents a signal with binary payload
	MsgSignalBinary = byte(63)

	// MsgSignalUtf8 represents a signal with UTF8 encoded payload
	MsgSignalUtf8 = byte(64)

	// MsgSignalUtf16 represents a signal with UTF16 encoded payload
	MsgSignalUtf16 = byte(65)

	// MsgRequestBinary represents a request with binary payload
	MsgRequestBinary = byte(127)

	// MsgRequestUtf8 represents a request with a UTF8 encoded payload
	MsgRequestUtf8 = byte(128)

	// MsgRequestUtf16 represents a request with a UTF16 encoded payload
	MsgRequestUtf16 = byte(129)

	// MsgReplyBinary represents a reply with a binary payload
	MsgReplyBinary = byte(191)

	// MsgReplyUtf8 represents a reply with a UTF8 encoded payload
	MsgReplyUtf8 = byte(192)

	// MsgReplyUtf16 represents a reply with a UTF16 encoded payload
	MsgReplyUtf16 = byte(193)
)

Variables

This section is empty.

Functions

func NewEmptyRequestMessage

func NewEmptyRequestMessage(msgType byte, id [8]byte) (msg []byte)

NewEmptyRequestMessage composes a new request message consisting only of the type and identifier and returns its binary representation

func NewErrorReplyMessage

func NewErrorReplyMessage(
	requestIdent [8]byte,
	code,
	message string,
) (msg []byte)

NewErrorReplyMessage composes a new error reply message and returns its binary representation

func NewNamelessRequestMessage

func NewNamelessRequestMessage(reqType byte, id [8]byte, payload []byte) (msg []byte)

NewNamelessRequestMessage composes a new nameless (initially without a name) request message and returns its binary representation

func NewReplyMessage

func NewReplyMessage(requestID [8]byte, payload Payload) (msg []byte)

NewReplyMessage composes a new reply message and returns its binary representation

func NewRequestMessage

func NewRequestMessage(id [8]byte, name string, payload Payload) (msg []byte)

NewRequestMessage composes a new named request message and returns its binary representation

func NewSignalMessage

func NewSignalMessage(name string, payload Payload) (msg []byte)

NewSignalMessage composes a new named signal message and returns its binary representation

func NewSpecialRequestReplyMessage

func NewSpecialRequestReplyMessage(msgType byte, reqIdent [8]byte) []byte

NewSpecialRequestReplyMessage composes a new special request reply message

func SessionInfoToVarMap

func SessionInfoToVarMap(info SessionInfo) map[string]interface{}

SessionInfoToVarMap is a utility function that turns a session info compliant object into a map of variants. This is helpful for serialization of session info objects.

Types

type Client

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

Client represents a client connected to the server

func (*Client) Close

func (clt *Client) Close()

Close marks the client agent for shutdown. It defers closing the connection and removing it from the session registry (if necessary) until all work is finished

func (*Client) CloseSession

func (clt *Client) CloseSession() error

CloseSession disables the currently active session for this client agent and acknowledges the client. The session will be destroyed if this is it's last connection remaining. Does nothing if there's no active session

func (*Client) CreateSession

func (clt *Client) CreateSession(attachment SessionInfo) error

CreateSession creates a new session for this client. It automatically synchronizes the new session to the remote client. The synchronization happens asynchronously using a signal and doesn't block the calling goroutine. Returns an error if there's already another session active

func (*Client) HasSession

func (clt *Client) HasSession() bool

HasSession returns true if the client referred by this client agent instance currently has a session assigned, otherwise returns false

func (*Client) Info

func (clt *Client) Info() ClientInfo

Info returns information about the client agent including the client agent string, the remote address and the time of creation

func (*Client) IsConnected

func (clt *Client) IsConnected() bool

IsConnected returns true if the client is currently connected to the server, thus able to receive signals, otherwise returns false. Disconnected client agents are no longer useful and will be garbage collected

func (*Client) Session

func (clt *Client) Session() *Session

Session returns an exact copy of the session object or nil if there's no session currently assigned to this client

func (*Client) SessionCreation

func (clt *Client) SessionCreation() time.Time

SessionCreation returns the time of creation of the currently assigned session. Warning: be sure to check whether there's a session beforehand as this function will return garbage if there's currently no session assigned to the client this user agent refers to

func (*Client) SessionInfo

func (clt *Client) SessionInfo(name string) interface{}

SessionInfo returns a copy of the session info field value in the form of an empty interface to be casted to either concrete type

func (*Client) SessionKey

func (clt *Client) SessionKey() string

SessionKey returns the key of the currently assigned session of the client this client agent refers to. Returns an empty string if there's no session assigned for this client

func (*Client) Signal

func (clt *Client) Signal(name string, payload Payload) error

Signal sends a named signal containing the given payload to the client

type ClientInfo

type ClientInfo struct {
	ConnectionTime time.Time
	UserAgent      string
	RemoteAddr     net.Addr
}

ClientInfo represents basic information about a client agent

type ConnIncompErr

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

ConnIncompErr represents a connection error type indicating that the server requires an incompatible version of the protocol and can't therefore be connected to.

func NewConnIncompErr

func NewConnIncompErr(requiredVersion, supportedVersion string) ConnIncompErr

NewConnIncompErr constructs and returns a new incompatible protocol version error based on the required and supported protocol versions

func (ConnIncompErr) Error

func (err ConnIncompErr) Error() string

type ConnUpgrader

type ConnUpgrader interface {
	Upgrade(resp http.ResponseWriter, req *http.Request) (Socket, error)
}

ConnUpgrader defines the abstract interface of an HTTP to WebSocket connection upgrader

type DefaultSessionKeyGenerator

type DefaultSessionKeyGenerator struct{}

DefaultSessionKeyGenerator implements the webwire.SessionKeyGenerator interface

func (*DefaultSessionKeyGenerator) Generate

func (gen *DefaultSessionKeyGenerator) Generate() string

Generate implements the webwire.Sessio

type DefaultSessionManager

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

DefaultSessionManager represents a default session manager implementation. It uses files as a persistent storage

func NewDefaultSessionManager

func NewDefaultSessionManager(sessFilesPath string) *DefaultSessionManager

NewDefaultSessionManager constructs a new default session manager instance. Verifies the existence of the given session directory and creates it if it doesn't exist yet

func (*DefaultSessionManager) OnSessionClosed

func (mng *DefaultSessionManager) OnSessionClosed(sessionKey string) error

OnSessionClosed implements the session manager interface. It closes the session by deleting the according session file

func (*DefaultSessionManager) OnSessionCreated

func (mng *DefaultSessionManager) OnSessionCreated(client *Client) error

OnSessionCreated implements the session manager interface. It writes the created session into a file using the session key as file name

func (*DefaultSessionManager) OnSessionLookup

func (mng *DefaultSessionManager) OnSessionLookup(key string) (
	SessionLookupResult,
	error,
)

OnSessionLookup implements the session manager interface. It searches the session file directory for the session file and loads it. It also updates the file by updating the last lookup session field.

type DisconnectedErr

type DisconnectedErr struct {
	Cause error
}

DisconnectedErr represents an error type indicating that the targeted client is disconnected

func NewDisconnectedErr

func NewDisconnectedErr(err error) DisconnectedErr

NewDisconnectedErr constructs a new DisconnectedErr error based on the actual error

func (DisconnectedErr) Error

func (err DisconnectedErr) Error() string

type GenericSessionInfo

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

GenericSessionInfo defines a default webwire.SessionInfo interface implementation type used by the client when no explicit session info parser is used

func (*GenericSessionInfo) Copy

func (sinf *GenericSessionInfo) Copy() SessionInfo

Copy implements the webwire.SessionInfo interface. It deep-copies the object and returns it's exact clone

func (*GenericSessionInfo) Fields

func (sinf *GenericSessionInfo) Fields() []string

Fields implements the webwire.SessionInfo interface. It returns a constant list of the names of all fields of the object

func (*GenericSessionInfo) Value

func (sinf *GenericSessionInfo) Value(fieldName string) interface{}

Value implements the webwire.SessionInfo interface. It returns an exact deep copy of a session info field value

type JSONEncodedSession

type JSONEncodedSession struct {
	Key        string                 `json:"k"`
	Creation   time.Time              `json:"c"`
	LastLookup time.Time              `json:"l"`
	Info       map[string]interface{} `json:"i,omitempty"`
}

JSONEncodedSession represents a JSON encoded session object. This structure is used during session restoration for unmarshalling TODO: move to internal shared package

type MaxSessConnsReachedErr

type MaxSessConnsReachedErr struct{}

MaxSessConnsReachedErr represents an authentication error type indicating that the given session already reached the maximum number of concurrent connections

func (MaxSessConnsReachedErr) Error

func (err MaxSessConnsReachedErr) Error() string

type Message

type Message struct {
	Name    string
	Payload Payload
	// contains filtered or unexported fields
}

Message represents a WebWire protocol message

func (*Message) Identifier

func (msg *Message) Identifier() [8]byte

Identifier returns the message identifier

func (*Message) MessageType

func (msg *Message) MessageType() byte

MessageType returns the type of the message

func (*Message) Parse

func (msg *Message) Parse(message []byte) (parsedMsgType bool, err error)

Parse tries to parse the message from a byte slice. the returned parsedMsgType is set to false if the message type couldn't be determined, otherwise it's set to true.

func (*Message) RequiresReply

func (msg *Message) RequiresReply() bool

RequiresReply returns true if a message of this type requires a reply, otherwise returns false.

func (*Message) RequiresResponse

func (msg *Message) RequiresResponse() bool

RequiresResponse returns true if this type of message requires a response to be sent in return

type OptionValue

type OptionValue = int32

OptionValue represents the setting value of an option

const (
	// OptionUnset represents the default unset value
	OptionUnset OptionValue = iota

	// Disabled disables an option
	Disabled

	// Enabled enables an option
	Enabled
)

type Payload

type Payload struct {
	Encoding PayloadEncoding
	Data     []byte
}

Payload represents an encoded message payload

func (*Payload) Utf8

func (pld *Payload) Utf8() (string, error)

Utf8 returns a UTF8 representation of the payload data

type PayloadEncoding

type PayloadEncoding int

PayloadEncoding represents the type of encoding of the message payload

const (
	// EncodingBinary represents unencoded binary data
	EncodingBinary PayloadEncoding = iota

	// EncodingUtf8 represents UTF8 encoding
	EncodingUtf8

	// EncodingUtf16 represents UTF16 encoding
	EncodingUtf16
)

func (PayloadEncoding) String

func (enc PayloadEncoding) String() string

type ProtocolErr

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

ProtocolErr represents an error type indicating an error in the protocol implementation

func NewProtocolErr

func NewProtocolErr(err error) ProtocolErr

NewProtocolErr constructs a new ProtocolErr error based on the actual error

func (ProtocolErr) Error

func (err ProtocolErr) Error() string

type ReqErr

type ReqErr struct {
	Code    string
	Message string
}

ReqErr represents an error returned in case of a request that couldn't be processed

func (ReqErr) Error

func (err ReqErr) Error() string

type ReqInternalErr

type ReqInternalErr struct{}

ReqInternalErr represents a request error type indicating that the request failed due to an internal server-side error

func (ReqInternalErr) Error

func (err ReqInternalErr) Error() string

type ReqSrvShutdownErr

type ReqSrvShutdownErr struct{}

ReqSrvShutdownErr represents a request error type indicating that the request cannot be processed due to the server currently being shut down

func (ReqSrvShutdownErr) Error

func (err ReqSrvShutdownErr) Error() string

type ReqTimeoutErr

type ReqTimeoutErr struct {
	Target time.Duration
}

ReqTimeoutErr represents a request error type indicating that the server wasn't able to reply within the given time frame causing the request to time out.

func (ReqTimeoutErr) Error

func (err ReqTimeoutErr) Error() string

type ReqTransErr

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

ReqTransErr represents a connection error type indicating that the dialing failed.

func NewReqTransErr

func NewReqTransErr(err error) ReqTransErr

NewReqTransErr constructs and returns a new request transmission error based on the actual error message

func (ReqTransErr) Error

func (err ReqTransErr) Error() string

type Server

type Server interface {
	// ServeHTTP implements the HTTP handler interface
	ServeHTTP(resp http.ResponseWriter, req *http.Request)

	// Run will luanch the webwire server blocking the calling goroutine
	// until the server is either gracefully shut down
	// or crashes returning an error
	Run() error

	// Addr returns the address the webwire server is listening on
	Addr() net.Addr

	// Shutdown appoints a server shutdown and blocks the calling goroutine
	// until the server is gracefully stopped awaiting all currently processed
	// signal and request handlers to return.
	// During the shutdown incoming connections are rejected
	// with 503 service unavailable.
	// Incoming requests are rejected with an error while incoming signals
	// are just ignored
	Shutdown() error

	// ActiveSessionsNum returns the number of currently active sessions
	ActiveSessionsNum() int

	// SessionConnectionsNum implements the SessionRegistry interface
	SessionConnectionsNum(sessionKey string) int

	// SessionConnections implements the SessionRegistry interface
	SessionConnections(sessionKey string) []*Client

	// CloseSession closes the session identified by the given key
	// and returns the number of closed connections.
	// If there was no session found -1 is returned
	CloseSession(sessionKey string) int
}

Server defines the interface of a webwire server instance

func NewHeadlessServer

func NewHeadlessServer(
	implementation ServerImplementation,
	opts ServerOptions,
) (instance Server, err error)

NewHeadlessServer creates a new headless WebWire server instance which relies on an external HTTP server to host it

func NewServer

func NewServer(
	implementation ServerImplementation,
	opts ServerOptions,
) (instance Server, err error)

NewServer creates a new headed WebWire server instance with a built-in HTTP server hosting it

type ServerImplementation

type ServerImplementation interface {
	// OnOptions is invoked when the websocket endpoint is examined by the client
	// using the HTTP OPTION method.
	OnOptions(resp http.ResponseWriter)

	// BeforeUpgrade is invoked right before the upgrade of an incoming HTTP connection request to
	// a WebSocket connection and can be used to intercept or prevent connection attempts.
	// If true is returned then the connection is normally established, though if false is returned
	// then the connection won't be established and will be canceled immediately
	BeforeUpgrade(resp http.ResponseWriter, req *http.Request) bool

	// OnClientConnected is invoked when a new client successfully established a connection
	// to the server.
	//
	// This hook will be invoked by the goroutine serving the client and thus will block the
	// initialization process, detaining the client from starting to listen for incoming messages.
	// To prevent blocking the initialization process it is advised to move any time consuming work
	// to a separate goroutine
	OnClientConnected(client *Client)

	// OnClientDisconnected is invoked when a client closes the connection to the server
	//
	// This hook will be invoked by the goroutine serving the calling client before it's suspended
	OnClientDisconnected(client *Client)

	// OnSignal is invoked when the webwire server receives a signal from a client.
	//
	// This hook will be invoked by the goroutine serving the calling client and will block any
	// other interactions with this client while executing
	OnSignal(ctx context.Context, client *Client, message *Message)

	// OnRequest is invoked when the webwire server receives a request from a client.
	// It must return either a response payload or an error.
	//
	// A webwire.ReqErr error can be returned to reply with an error code and an error message,
	// this is useful when the clients user code needs to be able to understand the error
	// and react accordingly.
	// If a non-webwire error type is returned such as an error created by fmt.Errorf(),
	// a special kind of error (internal server error) is returned to the client as a reply,
	// in this case the error will be logged and the error message will not be sent to the client
	// for security reasons as this might accidentally leak sensitive information to the client.
	//
	// This hook will be invoked by the goroutine serving the calling client and will block any
	// other interactions with this client while executing
	OnRequest(
		ctx context.Context,
		client *Client,
		message *Message,
	) (response Payload, err error)
}

ServerImplementation defines the interface of a webwire server implementation

type ServerOptions

type ServerOptions struct {
	Address               string
	Sessions              OptionValue
	SessionManager        SessionManager
	SessionKeyGenerator   SessionKeyGenerator
	SessionInfoParser     SessionInfoParser
	MaxSessionConnections uint
	WarnLog               *log.Logger
	ErrorLog              *log.Logger
}

ServerOptions represents the options used during the creation of a new WebWire server instance

func (*ServerOptions) SetDefaults

func (srvOpt *ServerOptions) SetDefaults()

SetDefaults sets the defaults for undefined required values

type SessNotFoundErr

type SessNotFoundErr struct{}

SessNotFoundErr represents a session restoration error type indicating that the server didn't find the session to be restored

func (SessNotFoundErr) Error

func (err SessNotFoundErr) Error() string

type Session

type Session struct {
	Key        string
	Creation   time.Time
	LastLookup time.Time
	Info       SessionInfo
}

Session represents a session object. If the key is empty the session is invalid. Info can contain arbitrary attached data

func NewSession

func NewSession(info SessionInfo, generator func() string) Session

NewSession generates a new session object generating a cryptographically random secure key

type SessionFile

type SessionFile struct {
	Creation   time.Time              `json:"c"`
	LastLookup time.Time              `json:"l"`
	Info       map[string]interface{} `json:"i"`
}

SessionFile represents the serialization structure of a default session file

func (*SessionFile) Parse

func (sessf *SessionFile) Parse(filePath string) error

Parse parses the session file from a file

func (*SessionFile) Save

func (sessf *SessionFile) Save(filePath string) error

Save writes the session file to a file on the filesystem

type SessionInfo

type SessionInfo interface {
	// Fields must return the exact names of all fields
	// of the session info object. This getter method must be idempotent,
	// which means that it must always return the same list of names
	Fields() []string

	// Value must return an exact deep copy of the value of a session info
	// object field identified by the given field name.
	//
	// Note that returning a shallow copy (such as shallow copies of
	// maps or slices for example) could lead to potentially dangerous
	// race conditions and undefined behavior
	Value(fieldName string) interface{}

	// Copy must return an exact deep copy of the entire session info object.
	//
	// Note that returning a shallow copy (such as shallow copies of
	// maps or slices for example) could lead to potentially dangerous
	// race conditions and undefined behavior
	Copy() SessionInfo
}

SessionInfo represents a session info object implementation interface. It defines a set of important methods that must be implemented carefully in order to avoid race conditions

func GenericSessionInfoParser

func GenericSessionInfoParser(data map[string]interface{}) SessionInfo

GenericSessionInfoParser represents a default implementation of a session info object parser. It parses the info object into a generic session info type implementing the webwire.SessionInfo interface

type SessionInfoParser

type SessionInfoParser func(map[string]interface{}) SessionInfo

SessionInfoParser represents the type of a session info parser function. The session info parser is invoked during the parsing of a newly assigned session on the client, as well as during the parsing of a saved serialized session. It must return a webwire.SessionInfo compliant object constructed from the data given

type SessionKeyGenerator

type SessionKeyGenerator interface {
	// Generate is invoked when the webwire server creates a new session and requires
	// a new session key to be generated. This hook must not be used except the user
	// knows exactly what he/she does as it would compromise security if implemented improperly
	Generate() string
}

SessionKeyGenerator defines the interface of a webwire servers session key generator. This interface must not be implemented (!) unless the default generator doesn't meet the exact needs of the library user, because the default generator already provides a secure implementation

func NewDefaultSessionKeyGenerator

func NewDefaultSessionKeyGenerator() SessionKeyGenerator

NewDefaultSessionKeyGenerator constructs a new default session key generator implementation

type SessionLookupResult

type SessionLookupResult struct {
	Creation   time.Time
	LastLookup time.Time
	Info       map[string]interface{}
}

SessionLookupResult represents the result of a session lookup

type SessionManager

type SessionManager interface {
	// OnSessionCreated is invoked after the synchronization of the new session
	// to the remote client.
	// The actual created session is retrieved from the provided client agent.
	// If OnSessionCreated returns an error then this error is logged
	// but the session will not be destroyed and will remain active!
	// The only consequence of OnSessionCreation failing is that the server
	// won't be able to restore the session after the client is disconnected.
	//
	// This hook will be invoked by the goroutine calling the
	// client.CreateSession client agent method
	OnSessionCreated(client *Client) error

	// OnSessionLookup is invoked when the server is looking for a specific
	// session given its key.
	// If the session wasn't found it must return a webwire.SessNotFoundErr,
	// otherwise it must first update the LastLookup field of the session
	// to ensure it's not garbage collected and then return
	// a webwire.SessionLookupResult object containing the time of the sessions
	// creation and the exact copy of the session info object.
	//
	// If an error (that's not a webwire.SessNotFoundErr) is returned then
	// it'll be logged and the session restoration will fail.
	//
	// This hook will be invoked by the goroutine serving the associated client
	// and will block any other interactions with this client while executing
	//
	// WARNING: if this hooks doesn't update the LastLookup field of the found
	// session object then the session garbage collection won't work properly
	OnSessionLookup(key string) (result SessionLookupResult, err error)

	// OnSessionClosed is invoked when the session associated with the given key
	// is closed (thus destroyed) either by the server or the client.
	// A closed session must be permanently deleted and must not be discoverable
	// in the OnSessionLookup hook any longer.
	// If an error is returned then the it is logged.
	//
	// This hook is invoked by either a goroutine calling the client.CloseSession()
	// client agent method, or the goroutine serving the associated client,
	// in the case of which it will block any other interactions with
	// this client while executing
	OnSessionClosed(sessionKey string) error
}

SessionManager defines the interface of a webwire server's session manager

type SessionsDisabledErr

type SessionsDisabledErr struct{}

SessionsDisabledErr represents an error type indicating that the server has sessions disabled

func (SessionsDisabledErr) Error

func (err SessionsDisabledErr) Error() string

type SockReadErr

type SockReadErr interface {
	// IsAbnormalCloseErr must return true if the error represents an abnormal closure error
	IsAbnormalCloseErr() bool
}

SockReadErr defines the interface of a webwire.Socket.Read error

type Socket

type Socket interface {
	// Dial must connect the socket to the specified server
	Dial(serverAddr string) error

	// Write must send the given data to the other side of the socket
	// while protecting the connection from concurrent writes
	Write(data []byte) error

	// Read must block the calling goroutine and await an incoming message.
	// When a message arrives or an error occurs Read must return.
	Read() ([]byte, SockReadErr)

	// IsConnected must return true if the given socket maintains an open connection,
	// otherwise return false
	IsConnected() bool

	// RemoteAddr must return the address of the remote client
	// or nil if the client is not connected
	RemoteAddr() net.Addr

	// Close must close the socket
	Close() error
}

Socket defines the abstract socket implementation interface

func NewSocket

func NewSocket() Socket

NewSocket creates a new disconnected gorilla/websocket based socket instance

Jump to

Keyboard shortcuts

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