@rinq/websocket
Version:
Rinq in the browser.
423 lines (347 loc) • 9.21 kB
JavaScript
var EventEmitter = require('events').EventEmitter
var RinqFailure = require('./failure/failure')
var types = require('./message-types')
function RinqSession (
sessionId,
send,
receive,
setTimeout,
clearTimeout,
logger,
log
) {
var calls // a map of call ID to call
var callSeq // the most recent call ID, which are sequential integers
var debugSymbol // the Unicode symbol used when logging debug information
var destroyError // the error that caused the session to be destroyed
var emit // a convenience for this.emit, bound to this
var inSymbol // the Unicode symbol used when logging incoming messages
var notificationSymbol // the Unicode symbol used when logging notifications
var outSymbol // the Unicode symbol used when logging outgoing messages
EventEmitter.call(this)
emit = this.emit.bind(this)
destroyError = null
callSeq = 0
calls = {}
debugSymbol = '\uD83D\uDC1E'
inSymbol = '\uD83D\uDCEC'
outSymbol = '\uD83D\uDCEE'
notificationSymbol = '\uD83D\uDCE2'
receive(dispatch, doDestroy)
this.execute = function execute (namespace, command, payload) {
if (destroyError) throw destroyError
if (log) {
logger(
[
'%c%s %s[exec] %s %s',
'color: blue',
outSymbol,
log.prefix,
namespace,
command
],
[[{payload: payload}]]
)
}
emit('execute', namespace, command, payload)
send({
type: types.EXECUTE,
session: sessionId,
namespace: namespace,
command: command,
payload: payload
})
}
this.call = function call (namespace, command, payload, timeout, callback) {
if (destroyError) {
callback(destroyError)
return
}
emit('call', namespace, command, payload, timeout, callback)
if (callback) {
callWait(namespace, command, payload, timeout, callback)
} else {
callAsync(namespace, command, payload, timeout)
}
}
this.destroy = function destroy () {
if (log && log.debug) {
logger(
[
'%c%s %sDestroying session.',
'color: orange',
debugSymbol,
log.prefix
]
)
}
send({type: types.SESSION_DESTROY, session: sessionId})
doDestroy()
}
function callWait (namespace, command, payload, timeout, callback) {
var callId, timeoutId
if (timeout < 0) {
throw new Error(
'Infinite timeouts are not supported when specifying a callback.'
)
}
callId = ++callSeq
if (timeout > 0) {
timeoutId = setTimeout(
function () {
if (log) {
logger(
[
'%c%s %s[recv] [%d] [erro] Call timed out.',
'color: red',
inSymbol,
log.prefix,
callId
]
)
}
delete calls[callId]
callback(new Error(
"Call to '" + command + "' in namespace '" + namespace +
"' timed out after " + timeout + 'ms.'
))
},
timeout
)
}
calls[callId] = {callback: callback, timeout: timeoutId}
if (log) {
logger(
[
'%c%s %s[call] [%d] %s %s',
'color: blue',
outSymbol,
log.prefix,
callId,
namespace,
command
],
[[{payload: payload, timeout: timeout}]]
)
}
send({
type: types.CALL,
session: sessionId,
seq: callId,
namespace: namespace,
command: command,
timeout: timeout,
payload: payload
})
}
function callAsync (namespace, command, payload, timeout) {
if (!timeout) timeout = 0
if (log) {
logger(
[
'%c%s %s[call] [asyn] %s %s',
'color: blue',
outSymbol,
log.prefix,
namespace,
command
],
[[{payload: payload, timeout: timeout}]]
)
}
send({
type: types.CALL_ASYNC,
session: sessionId,
namespace: namespace,
command: command,
timeout: timeout,
payload: payload
})
}
function dispatch (message) {
switch (message.type) {
case types.SESSION_DESTROY: return dispatchSessionDestroy(message)
case types.CALL_ERROR: return dispatchCallWaitError(message)
case types.CALL_FAILURE: return dispatchCallWaitFailure(message)
case types.CALL_SUCCESS: return dispatchCallWaitSuccess(message)
case types.CALL_ASYNC_ERROR: return dispatchCallAsyncError(message)
case types.CALL_ASYNC_FAILURE: return dispatchCallAsyncFailure(message)
case types.CALL_ASYNC_SUCCESS: return dispatchCallAsyncSuccess(message)
case types.NOTIFICATION: return dispatchNotification(message)
}
}
function dispatchSessionDestroy () {
if (log) {
logger(
[
'%c%s %s[recv] session destroy',
'color: orange',
inSymbol,
log.prefix
]
)
}
doDestroy(new Error('Session destroyed remotely.'))
}
function dispatchCallWaitError (message) {
var call
call = calls[message.seq]
if (!call) return
if (log) {
logger(
[
'%c%s %s[recv] [%d] [erro]',
'color: red',
inSymbol,
log.prefix,
message.seq
]
)
}
if (call.timeout) clearTimeout(call.timeout)
delete calls[message.seq]
call.callback(new Error('Server error.'))
}
function dispatchCallWaitFailure (message) {
var call, payload
call = calls[message.seq]
if (!call) return
payload = message.payload()
if (log) {
logger(
[
'%c%s %s[recv] [%d] [fail]',
'color: orange',
inSymbol,
log.prefix,
message.seq
],
[[{payload: payload}]]
)
}
if (call.timeout) clearTimeout(call.timeout)
delete calls[message.seq]
call.callback(
new RinqFailure(message.failureType, message.failureMessage, payload)
)
}
function dispatchCallWaitSuccess (message) {
var call, payload
call = calls[message.seq]
if (!call) return
payload = message.payload()
if (log) {
logger(
[
'%c%s %s[recv] [%d] [succ]',
'color: green',
inSymbol,
log.prefix,
message.seq
],
[[{payload: payload}]]
)
}
if (call.timeout) clearTimeout(call.timeout)
delete calls[message.seq]
call.callback(null, payload)
}
function dispatchCallAsyncError (message) {
var namespace, command
namespace = message.namespace
command = message.command
if (log) {
logger(
[
'%c%s %s[recv] [asyn] [erro] %s %s',
'color: red',
inSymbol,
log.prefix,
namespace,
command
],
[[{namespace: namespace, command: command}]]
)
}
emit('response', new Error('Server error.'), null, namespace, command)
}
function dispatchCallAsyncFailure (message) {
var namespace, command, payload
namespace = message.namespace
command = message.command
payload = message.payload()
if (log) {
logger(
[
'%c%s %s[recv] [asyn] [fail] %s %s',
'color: orange',
inSymbol,
log.prefix,
namespace,
command
],
[[{namespace: namespace, command: command, payload: payload}]]
)
}
emit(
'response',
new RinqFailure(message.failureType, message.failureMessage, payload),
null,
message.namespace,
message.command
)
}
function dispatchCallAsyncSuccess (message) {
var namespace, command, payload
namespace = message.namespace
command = message.command
payload = message.payload()
if (log) {
logger(
[
'%c%s %s[recv] [asyn] [succ] %s %s',
'color: green',
inSymbol,
log.prefix,
namespace,
command
],
[[{namespace: namespace, command: command, payload: payload}]]
)
}
emit('response', null, payload, message.namespace, message.command)
}
function dispatchNotification (message) {
var type, payload
type = message.notificationType
payload = message.payload()
if (log) {
logger(
[
'%c%s %s[recv] [noti] %s',
'color: teal',
notificationSymbol,
log.prefix,
type
],
[[{type: type, payload: payload}]]
)
}
emit('notification', message.notificationType, payload)
}
function doDestroy (error) {
var call
var callId
destroyError = error || new Error('Session destroyed locally.')
for (callId in calls) {
call = calls[callId]
if (call.timeout) clearTimeout(call.timeout)
call.callback(destroyError)
}
calls = {}
emit('destroy', error)
}
}
RinqSession.prototype = Object.create(EventEmitter.prototype)
RinqSession.prototype.constructor = RinqSession
module.exports = RinqSession