Rinq in the browser.
The core module centers around single-use connections. It does not implement any reconnection logic, or application-level state. First a connection must be made, from which a session is created in order to communicate:
var rinq = require('@rinq/websocket')
var connection = rinq.connection('ws://example.org/')
var session = rinq.session()
connection.on('open', function () {
session.execute('namespace', 'command', 'payload')
})
With this approach, once the connection is closed, or the session is destroyed, these objects must be discarded, and new ones created. It is up to the user to manage any application state that depends upon access to a Rinq connection or session.
The managed module implements some higher-level constructs that simplify the management of transient communication issues, such as network dropouts. First a connection manager is created, then a session manager, and finally contexts, which provide similar functionality to a session:
var rinq = require('@rinq/websocket/managed')
var connectionManager = rinq.connectionManager({url: 'ws://example.org/'})
var sessionManager = connectionManager.sessionManager()
var context = sessionManager.context()
context.on('ready', function () {
context.execute('namespace', 'command', 'payload')
})
context.start()
With this approach, transient communication issues are managed by Rinq. This means it is safe to store references to the connection manager, session manager, and context, across the lifetime of the application.
Additionally, contexts provide some basic application-level state management, as they can specify an initialization function that must execute before the context is "ready":
var context = sessionManager.context({
initialize: function (done, session) {
// listen for notifications
session.on('notification', onNotification)
session.once('destroy', function () {
session.removeListener('notification', onNotification)
})
// perform an authentication request
session.call('auth.1', 'token', 'U53R-70K3N', 10000, done)
}
})
context.on('ready', function () {
context.execute('namespace', 'command', 'payload')
})
context.start()
require('@rinq/websocket')
The core module contains only the essential functionality for communicating via the Rinq protocol:
Connection
connection
(url[, options])
Creates a new Rinq connection to url
.
The options
are represented as a generic object, and may specify:
Option | Description | Type | Example | Default |
---|---|---|---|---|
CBOR |
A reference to the @rinq/cbor module. | object | require('@rinq/cbor') |
(none) |
log |
A set of logging options. | object | {debug: true} |
(none) |
Specifying CBOR
is recommended, as it enables messages to be serialized with
CBOR rather than JSON:
var c = connection('ws://example.org/', {CBOR: CBOR})
boolean
isFailure
(error)
Returns true
if error
is a Rinq failure. This function can be used to
assist in handling errors returned by Rinq calls:
session.call('namespace', 'command', 'payload', 3000, function (error, response) {
if (error) {
if (isFailure(error)) {
// handle failures
} else {
// handle other errors
}
}
// proceed as normal
})
boolean
isFailureType
(type, error)
Returns true
if error
is a Rinq failure of type type
. This function
can be used to assist in handling errors returned by Rinq calls:
session.call('namespace', 'command', 'payload', 3000, function (error, response) {
if (error) {
if (isFailureType('type-a', error)) {
// handle type a failures
} else if (isFailureType('type-b', error)) {
// handle type b failures
} else {
// handle other errors
}
}
// proceed as normal
})
Represents a Rinq connection, and allows the creation of sessions for communication:
Session
connection.session
([options])
Creates a new session.
The options
are represented as a generic object, and may specify:
Option | Description | Type | Example | Default |
---|---|---|---|---|
log |
A set of logging options. | object | {debug: true} |
(none) |
connection.session({log: {prefix: '[session-a] '}})
void
connection.close
()
Closes the connection.
Once a connection is closed, it cannot be re-opened.
connection.on(
'open'
, function () {})
This event is emitted once the connection is open and ready to communicate.
The handler for this event accepts no arguments.
connection.on(
'close'
, function ([error]) {})
This event is emitted once the connection is closed.
The handler for this event accepts a single, optional error
argument. If the
connection was closed normally, via close()
, error
will
be undefined
.
Represents a session, and allows for multiple channels of communication over a single Rinq connection:
void
session.execute
(namespace, command, payload)
Sends a Rinq command, for which no response is expected.
Both namespace
and command
are strings used to dispatch the command to the
appropriate server. The payload
can be any JSON serializable value.
void
session.call
(namespace, command, payload[, timeout][, function (error, response) {}])
Sends a Rinq command, and handles the response.
Both namespace
and command
are strings used to dispatch the command to the
appropriate server. The payload
can be any JSON serializable value.
The timeout
value is used in the Rinq protocol to determine when an
unprocessed command can be discarded due to its age. In addition, if a handler
function is supplied, a client-side timeout will cause the handler function to
be called with a timeout error as its error
argument.
The timeout
value is specified as an integer. A positive timeout
value
indicates the number of milliseconds before timeout occurs. A timeout
of 0
indicates that the server-side default timeout should be used. A negative
timeout
value indicates that no timeout should be used, but this is only
allowed when no handler function is specified.
The last argument is an optional handler that accepts an error
as the first
argument, and the response
as the second. If error
is non-empty, the
response
value should be ignored.
If no handler function is specified, the response to the call will instead be emitted from the session as a response event.
Errors supplied to the handler, or emitted via a response event will typically be Rinq failures, which are sent by the server handling the command, but they can also be regular JavaScript errors for unexpected circumstances.
Generally speaking, specific handling should exist for any relevant failures, and a single catch-all for unexpected errors should also exist. To differentiate the errors, use the isFailure() and isFailureType() functions.
If no error
is supplied, the response
value can be any plain JavaScript
value sent by the server, including any values that can be unserialized from
JSON.
void
session.destroy
()
Destroys the session.
Once a session is destroyed, it cannot be re-used.
session.on(
'execute'
, function (...args) {})
This event is emitted when execute() is called.
The handler for this event accepts the arguments passed to execute().
session.on(
'call'
, function (...args) {})
This event is emitted when call() is called.
The handler for this event accepts the arguments passed to call().
session.on(
'notification'
, function (type, payload) {})
This event is emitted when a notification is received.
The handler for this event accepts the notification's type
string as the first
argument, and its payload
value as the second argument. The payload
value
can be any plain JavaScript value sent by the server, including any values that
can be unserialized from JSON.
Errors thrown while handling this event will cause disconnection. To avoid this, implement error handling inside the event handler.
session.on(
'response'
, function (error, response, namespace, command) {})
This event is emitted when a response is received, and no handler function was specified in the originating call.
The handler for this event accepts the same error
and response
values as
would normally be passed to a handler function supplied to
call(). In addition to these arguments, namespace
and
command
are provided, which supply the namespace
and command
values
specified in the originating call.
Errors thrown while handling this event will cause disconnection. To avoid this, implement error handling inside the event handler.
session.on(
'destroy'
, function ([error]) {})
This event is emitted once the session is destroyed.
The handler for this event accepts a single, optional error
argument. If the
session was destroyed normally, via destroy()
, error
will be undefined
.
Represents a failure response sent by a server. Failures typically represent "expected" error cases that may need to be handled by the client. Some examples of failures might be:
- Resource not found
- Input validation failures
- Unauthorized
Failures are normal JavaScript errors, with the following properties:
Property | Description | Type | Example |
---|---|---|---|
type |
A type used to categorize the failure. | string | 'not-found' |
message |
A message describing the failure. | string | 'The specified user does not exist.' |
data |
An optional value populated with additional data by the server. | (any) | {username: 'jsmith'} |
require('@rinq/websocket/managed')
The managed module contains higher-lever tools for managing Rinq connections and sessions in an environment where connection to the server is transient, and dependent on network connectivity and availability of servers:
ConnectionManager
connectionManager
([options])
Creates a new Rinq connection manager.
The options
are represented as a generic object, and may specify:
Option | Description | Type | Example | Default |
---|---|---|---|---|
url |
The URL to connect to. | string | 'ws://example.org/' |
(none) |
delay |
A function for calculating the delay before reconnecting. | function | (see below) | (see below) |
CBOR |
A reference to the @rinq/cbor module. | object | require('@rinq/cbor') |
(none) |
log |
A set of logging options. | object | {debug: true} |
(none) |
The url
is optional, because it is sometimes necessary to determine this
information based upon the outcome of some asynchronous action, such as fetching
some external configuration. The URL can also be set later via the
connectionManager.url property.
The delay
option allows customization of the amount of time between a
disconnection, and the subsequent reconnection attempt, based upon the number of
consecutive disconnections. The supplied function should take a single argument
representing the number of disconnects, and return a delay time in milliseconds.
For example, the default delay
function is:
function delay (disconnects) {
return Math.min(Math.pow(2, disconnects - 1) * 1000, 32000)
}
Which produces the following delay times:
Disconnects | Delay (seconds) |
---|---|
1 | 1 |
2 | 2 |
3 | 4 |
4 | 8 |
5 | 16 |
6+ | 32 |
Specifying CBOR
is recommended, as it enables messages to be serialized with
CBOR rather than JSON.
Represents a transient Rinq connection, and allows the creation of session managers:
SessionManager
connectionManager.sessionManager
([options])
Creates a new session manager.
The options
are represented as a generic object, and may specify:
Option | Description | Type | Example | Default |
---|---|---|---|---|
log |
A set of logging options. | object | {debug: true} |
(none) |
connectionManager.sessionManager({log: {prefix: '[session-a] '}})
void
connectionManager.start
()
Starts the connection manager.
While the connection manager is started, it will attempt to maintain a connection. It will also monitor network availability, and avoid attempting to reconnect when the network is down.
void
connectionManager.stop
()
Stops the connection manager.
When the connection manager is stopped, it will close the current connection if it is open, and will not attempt to reconnect until started again.
connectionManager.on(
'connection'
, function (connection) {})
This event is emitted when a new open connection is available.
The handler for this event accepts a single connection
argument, which is a
Rinq connection. The handler is only called when the connection is open,
and ready for communication.
This event will fire multiple times (interspersed with
error
events) as transient communication
problems arise, and are resolved. The latest connection should always replace
any previous connections.
connectionManager.on(
'error'
, function (error) {})
This event is emitted when communication issues arise.
The handler for this event accepts a single error
argument. Upon handling this
event, no further communication should be attempted until a new connection is
received via the next connection
event.
Represents a transient Rinq session, and allows the creation of contexts:
- execute()
- call()
- context()
- start()
- stop()
- session event
- notification event
- execute event
- call event
- response event
- error event
Context
sessionManager.context
([options])
Creates a new context.
The options
are represented as a generic object, and may specify:
Option | Description | Type | Example | Default |
---|---|---|---|---|
initialize |
A function that must complete before the context is ready. | function | (see below) | (none) |
log |
A set of logging options. | object | {debug: true} |
(none) |
The initialize
option allows for the situation where a context is not ready
for use until some initialization logic has been performed. This initialization
may involve asynchronous operations, and can include communication over a
Rinq session.
The function supplied for the initialize
option should accept a done
callback as the first argument, that must be executed in order for the context
to be considered "ready", and a Rinq session as the second argument:
var context = sessionManager.context({
initialize: function (done) {
done()
}
})
The done
callback accepts an optional error which, if supplied, will cause the
context to emit an error
event. An error
event will also be emitted if the
initialize
function throws, using the thrown value as the error. The context
will not proceed to the "ready" state, unless the done
callback is called
without an error argument.
Context initialization can be used to hook up notification event listeners. Remember to clean up listeners as appropriate:
var context = sessionManager.context({
initialize: function (done, session) {
session.on('notification', onNotification)
session.once('destroy', function () {
session.removeListener('notification', onNotification)
})
}
})
Another common use case for context initialization is authentication. For example, this initialization function demonstrates authenticating via a Rinq service:
var context = sessionManager.context({
initialize: function (done, session) {
session.call('auth.1', 'token', 'U53R-70K3N', 10000, done)
}
})
void
sessionManager.execute
(namespace, command, payload)
Sends a Rinq command, for which no response is expected.
Functionally equivalent to session.execute.
void
sessionManager.call
(namespace, command, payload, timeout, function (error, response) {})
Sends a Rinq command, and handles the response.
Functionally equivalent to session.call..
void
sessionManager.start
()
Starts the session manager, and the connection manager from which it was created.
While the session manager is started, it will attempt to maintain a session.
void
sessionManager.stop
()
Stops the session manager.
When the session manager is stopped, it will destroy the current session if it is open, and will not attempt to create a new session until started again.
sessionManager.on(
'session'
, function (session) {})
This event is emitted when a new session is available.
The handler for this event accepts a single session
argument, which is a
Rinq session.
This event will fire multiple times (interspersed with
error
events) as transient communication
problems arise, and are resolved. The latest session should always replace any
previous sessions.
sessionManager.on(
'execute'
, function (...args) {})
This event is emitted when an underlying session emits an
execute
event.
sessionManager.on(
'call'
, function (...args) {})
This event is emitted when an underlying session emits an
call
event.
sessionManager.on(
'notification'
, function (type, payload) {})
This event is emitted when an underlying session emits a
notification
event.
sessionManager.on(
'response'
, function (error, response, namespace, command) {})
This event is emitted when an underlying session emits a
response
event.
sessionManager.on(
'error'
, function (error) {})
This event is emitted when communication issues arise.
The handler for this event accepts a single error
argument. Upon handling this
event, no further communication should be attempted until a new connection is
received via the next session
event.
Allows communication over a transient Rinq session, with the option of asynchronous initialization logic before communication can commence:
void
context.start
()
Starts the context, and the session manager and connection manager from which it was created.
While the context is started, it will attempt to maintain a "ready" state.
void
context.stop
()
Stops the context.
When the context is stopped, it will not attempt to maintain a "ready" state.
void
context.execute
(namespace, command, payload)
Sends a Rinq command, for which no response is expected.
Functionally equivalent to session.execute.
void
context.call
(namespace, command, payload, timeout, function (error, response) {})
Sends a Rinq command, and handles the response.
Functionally equivalent to session.call, except that both
timeout
, and the handler function are mandatory.
void
context.whenReady
(function (error) {}[, timeout])
Calls the supplied callback when the context is ready, or immediately if the context is already ready.
If a timeout
value is specificed, the callback will be called with an error as
the first argument after timeout
milliseconds.
context.on(
'ready'
, function () {})
This event is emitted when the context has completed any initialization steps, and is ready for communication.
The handler for this event accepts no arguments.
This event will fire multiple times (interspersed with
error
events) as transient communication
problems arise, and are resolved.
context.on(
'error'
, function (error) {})
This event is emitted when communication issues arise.
The handler for this event accepts a single error
argument. Upon handling this
event, no further communication should be attempted until the next
ready
event.
Logging options are represented as a generic object, and may specify:
Option | Description | Type | Example | Default |
---|---|---|---|---|
prefix |
A prefix to use when logging. | string | '[context-a] ' |
'' |
debug |
Specifies whether to log debug information. | boolean | true |
false |
If logging options are omitted entirely, no logging will take place.