Websockets, while they're cool as hell, have the problem of being low enough level that we need to wrap them up in order to avoid rewriting code all over the place. Libraries, such as Socket.io are useful, but implementing socket.io in other languages is not very straight forward, and seem to be fraught with difficulty, as evidenced by the lack of feature-comparable non-node.js server side libraries that exist.
So, I want to write my own. This doesn't have any fall-back support; instead it is tailored to websockets. Here's a list of targeted features:
- Event-based
- Replies
- Channels (ex: '/chat', '/foobar', etc)
Since this is WebSocket based, we simply need a nice JSON structure for handling all of this.
{
"name": "..." // The name of the event.
"channel": "..." // Either '/', '', or undefined for messages on the root channel, or '/channel-name' for channeled messages.
"replyWith": "..." // (optional) Indicates that this message expects a reply.
"replyTo": "..." // (optional) Indicated that this is a reply message.
"data": [ ... ] // (optional) Any additional data being sent with the message.
}
This format is used for both client to server and server to client messages.
The name of the event. It is determined by the user on either the client or server. It is a string, and can contain any valid unicode characters.
The name of the channel the message is being sent over. It is either '/'
, ''
(empty string), or undefined
if the message is intended for the root channel, other wise it is any valid slug
. (See here.)
An identifier generated by the client, only when a reply is desired. (Hence, this is optional.) The identifier is sent back to the client by the server when it is responding. (During a response, replyTo
is populated with this value, and name
and channel
are also preserved.) It is up to the client to ensure that it can match the identifier of the reply to the originating message. Generally, this means the identifier must be unique across the set of all messages waiting on a reply. It is a string, and can contain any valid unicode characters.
An identifier originally sent by the client, to the server. This indicates the message is a reply. It is a string, and can contain any valid unicode characters.
A list of additional JSON-able objects to be sent with the request. This is always a list, or undefined. The objects contained within must be able to be serialized as json.
When a client connects, first the WebSocket connection is finished. Then, the client sends a 'connect'
event on the '$control'
channel. Other than name
and channel
, this message is also expected to contain a replyWith
field. The server then replies with a message containing a data
list with a single configuration object. The client is expected to use this as the configuration for the session.
Messages sent on a channel
are automatically filtered by both the client and the server. This is intended to allow users to handle only channels they are interested in.
If a message is sent without a channel specified (or on the '/'
channel), it is assumed to be the root channel.
The '$control'
channel is a special channel reserved for special messages, such as connection.
Events that expect a reply must send the replyWith
field. That field contains an identifier generated by the sender. The receiver is expect to send a reply with the same name
, channel
, and the replyTo
field populated with the identifier from the replWith
field of the originating message. This reply is always expected, even if the reply is empty. The timeout on this is 30 seconds (this is configurable on the server side). If there is a timeout, the sender is expected to generate an error.