WebWire for Go
An asynchronous duplex messaging library
webwire-go-client provides a client implementation for the open-source webwire protocol.
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-client
.
If you're using dep, just use dep ensure to add a specific version of webwire-go-client including all its transitive dependencies to your project: dep ensure -add github.com/qbeon/[email protected]
.
You can also use go get: go get github.com/qbeon/webwire-go-client
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 of any kind is always welcome and appreciated, check out our Contribution Guidelines for more information!
Maintainer | Role | Specialization |
---|---|---|
Roman Sharkov | Core Maintainer | Dev (Go, JavaScript) |
Daniil Trishkin | CI Maintainer | DevOps |
WebWire is built for speed and portability implementing an open source binary protocol (see here for further details).
-
Echo - Demonstrates a simple request-reply implementation.
-
Pub-Sub - Demonstrantes a simple publisher-subscriber tolopology implementation.
-
Chat Room - Demonstrates advanced use of the library. The corresponding JavaScript Chat Room Client is implemented with the Vue.js framework.
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,
// this will block the goroutine until either a reply is received
// or the default timeout triggers (if there is one)
reply, err := client.Request(
context.Background(), // No cancelation, default timeout
nil, // No name
wwr.Payload{
Data: []byte("sudo rm -rf /"), // Binary request payload
},
)
defer reply.Close() // Close the reply
if err != nil {
// Oh oh, the request failed for some reason!
}
reply.PayloadUtf8() // Here we go!
Requests will respect cancelable contexts and deadlines
cancelableCtx, cancel := context.WithCancel(context.Background())
defer cancel()
timedCtx, cancelTimed := context.WithTimeout(cancelableCtx, 1*time.Second)
defer cancelTimed()
// Send a cancelable request to the server with a 1 second deadline
// will block the goroutine for 1 second at max
reply, err := client.Request(timedCtx, nil, wwr.Payload{
Encoding: wwr.EncodingUtf8,
Data: []byte("hurry up!"),
})
defer reply.Close()
// Investigate errors manually...
switch err.(type) {
case wwr.ErrCanceled:
// Request was prematurely canceled by the sender
case wwr.ErrDeadlineExceeded:
// Request timed out, server didn't manage to reply
// within the user-specified context deadline
case wwr.TimeoutErr:
// Request timed out, server didn't manage to reply
// within the specified default request timeout duration
case nil:
// Replied successfully
}
// ... or check for a timeout error the easier way:
if err != nil {
if wwr.IsTimeoutErr(err) {
// Timed out due to deadline excess or default timeout
} else {
// Unexpected error
}
}
reply // Just in time!
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(
[]byte("eventA"),
wwr.Payload{
Encoding: wwr.EncodingUtf8,
Data: []byte("something"),
},
)
The server also can send signals to individual connected clients.
OnSignal implements the wwrclt.Implementation interface
func (c *ClientImplementation) OnSignal(msg wwr.Message) {
// A server-side signal was received
msg.PayloadEncoding() // Signal payload encoding
msg.Payload() // Signal payload data
}
Different kinds of requests and signals can be differentiated using the builtin namespacing feature.
reply, err := client.Request(
context.Background(),
[]byte("request name"),
wwr.Payload{},
)
OnSignal implements the wwrclt.Implementation interface
func (c *ClientImplementation) OnSignal(msg wwr.Message) {
switch msg.Name() {
case "event A":
// handle signal A
case "event B":
// handle signal B
}
}
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.
OnSessionCreated implements the wwrclt.Implementation interface
func (c *ClientImplementation) OnSessionCreated(newSession *wwr.Session) {
// A session was created on this connection
}
OnDisconnected implements the wwrclt.Implementation interface
func (c *ClientImplementation) OnDisconnected() {
// The session of this connection was closed
// by either the client, or the server
}
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"))
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
andclient.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 aErrDisconnected
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
and client.RestoreSession
to immediately return a ErrDisconnected
error when there's no connection at the time the request is made.
If the webwire server is using a TLS protected transport implementation the client can establish a secure connection to prevent man-in-the-middle attacks as well as to verify the identity of the server. To connect the client to a webwire server that's using a TLS protected transport implementation such as webwire-go-gorilla over TLS the appropriate transport implementation needs to be used:
connection, err := wwrclt.NewClient(
clientImplementation,
wwrclt.Options{/*...*/},
&wwrgorilla.ClientTransport{
ServerAddress: serverAddr,
Dialer: gorillaws.Dialer{
TLSClientConfig: &tls.Config{},
},
},
)
© 2018 Roman Sharkov [email protected]