Categorygithub.com/qbeon/webwire-go
modulepackage
1.0.0-rc1
Repository: https://github.com/qbeon/webwire-go.git
Documentation: pkg.go.dev

# README

License: MIT 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/[email protected]. 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

MaintainerRoleSpecialization
Roman SharkovCore MaintainerDev (Go, JavaScript)
Daniil TrishkinCI MaintainerDevOps

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 [email protected]

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

GenericSessionInfoParser represents a default implementation of a session info object parser.
NewConnIncompErr constructs and returns a new incompatible protocol version error based on the required and supported protocol versions.
NewDefaultSessionKeyGenerator constructs a new default session key generator implementation.
NewDefaultSessionManager constructs a new default session manager instance.
NewDisconnectedErr constructs a new DisconnectedErr error based on the actual error.
NewEmptyRequestMessage composes a new request message consisting only of the type and identifier and returns its binary representation.
NewErrorReplyMessage composes a new error reply message and returns its binary representation.
NewHeadlessServer creates a new headless WebWire server instance which relies on an external HTTP server to host it.
NewNamelessRequestMessage composes a new nameless (initially without a name) request message and returns its binary representation.
NewProtocolErr constructs a new ProtocolErr error based on the actual error.
NewReplyMessage composes a new reply message and returns its binary representation.
NewReqTransErr constructs and returns a new request transmission error based on the actual error message.
NewRequestMessage composes a new named request message and returns its binary representation.
NewServer creates a new headed WebWire server instance with a built-in HTTP server hosting it.
NewSession generates a new session object generating a cryptographically random secure key.
NewSignalMessage composes a new named signal message and returns its binary representation.
NewSocket creates a new disconnected gorilla/websocket based socket instance.
NewSpecialRequestReplyMessage composes a new special request reply message.
SessionInfoToVarMap is a utility function that turns a session info compliant object into a map of variants.

# Constants

Disabled disables an option.
Enabled enables an option.
EncodingBinary represents unencoded binary data.
EncodingUtf16 represents UTF16 encoding.
EncodingUtf8 represents UTF8 encoding.
MsgCloseSession is sent by the client and represents a request for the destruction of the currently active session.
MsgErrorReply is sent by the server and represents an error-reply to a previously sent request.
MsgInternalError is sent by the server if an unexpected internal error arose during the processing of a request.
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.
MsgMinLenCloseSession represents the minimum session destruction request message length Session destruction request message structure: 1.
MsgMinLenErrorReply represents the minimum error reply message length Error reply message structure: 1.
MsgMinLenReply represents the minimum binary/UTF8 encoded reply message length.
MsgMinLenReplyUtf16 represents the minimum UTF16 encoded reply message length UTF16 reply message structure: 1.
MsgMinLenRequest represents the minimum binary/UTF8 encoded request message length.
MsgMinLenRequestUtf16 represents the minimum UTF16 encoded request message length.
MsgMinLenRestoreSession represents the minimum session restoration request message length Session restoration request message structure: 1.
MsgMinLenSessionClosed represents the minimum session creation notification message length Session destruction notification message structure: 1.
MsgMinLenSessionCreated represents the minimum session creation notification message length Session creation notification message structure: 1.
MsgMinLenSignal represents the minimum binary/UTF8 encoded signal message length.
MsgMinLenSignalUtf16 represents the minimum UTF16 encoded signal message length.
MsgReplyBinary represents a reply with a binary payload.
MsgReplyProtocolError is sent by the server in response to an invalid message violating the protocol.
MsgReplyShutdown is sent by the server when a request is received during server shutdown and can't therefore be processed.
MsgReplyUtf16 represents a reply with a UTF16 encoded payload.
MsgReplyUtf8 represents a reply with a UTF8 encoded payload.
MsgRequestBinary represents a request with binary payload.
MsgRequestUtf16 represents a request with a UTF16 encoded payload.
MsgRequestUtf8 represents a request with a UTF8 encoded payload.
MsgRestoreSession is sent by the client to request session restoration.
MsgSessionClosed is sent by the server to notify the client about the session destruction.
MsgSessionCreated is sent by the server to notify the client about the session creation.
MsgSessionNotFound is sent by the server in response to an unfilfilled session restoration request due to the session not being found.
MsgSessionsDisabled is sent by the server in response to a session restoration request if sessions are disabled for the target server.
MsgSignalBinary represents a signal with binary payload.
MsgSignalUtf16 represents a signal with UTF16 encoded payload.
MsgSignalUtf8 represents a signal with UTF8 encoded payload.
OptionUnset represents the default unset value.

# Structs

Client represents a client connected to the server.
ClientInfo represents basic information about a client agent.
ConnIncompErr represents a connection error type indicating that the server requires an incompatible version of the protocol and can't therefore be connected to.
DefaultSessionKeyGenerator implements the webwire.SessionKeyGenerator interface.
DefaultSessionManager represents a default session manager implementation.
DisconnectedErr represents an error type indicating that the targeted client is disconnected.
GenericSessionInfo defines a default webwire.SessionInfo interface implementation type used by the client when no explicit session info parser is used.
JSONEncodedSession represents a JSON encoded session object.
MaxSessConnsReachedErr represents an authentication error type indicating that the given session already reached the maximum number of concurrent connections.
Message represents a WebWire protocol message.
Payload represents an encoded message payload.
ProtocolErr represents an error type indicating an error in the protocol implementation.
ReqErr represents an error returned in case of a request that couldn't be processed.
ReqInternalErr represents a request error type indicating that the request failed due to an internal server-side error.
ReqSrvShutdownErr represents a request error type indicating that the request cannot be processed due to the server currently being shut down.
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.
ReqTransErr represents a connection error type indicating that the dialing failed.
ServerOptions represents the options used during the creation of a new WebWire server instance.
Session represents a session object.
SessionFile represents the serialization structure of a default session file.
SessionLookupResult represents the result of a session lookup.
SessionsDisabledErr represents an error type indicating that the server has sessions disabled.
SessNotFoundErr represents a session restoration error type indicating that the server didn't find the session to be restored.

# Interfaces

ConnUpgrader defines the abstract interface of an HTTP to WebSocket connection upgrader.
Server defines the interface of a webwire server instance.
ServerImplementation defines the interface of a webwire server implementation.
SessionInfo represents a session info object implementation interface.
SessionKeyGenerator defines the interface of a webwire servers session key generator.
SessionManager defines the interface of a webwire server's session manager.
Socket defines the abstract socket implementation interface.
SockReadErr defines the interface of a webwire.Socket.Read error.

# Type aliases

OptionValue represents the setting value of an option.
PayloadEncoding represents the type of encoding of the message payload.
SessionInfoParser represents the type of a session info parser function.