Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

diffusion

Package Overview
Dependencies
Maintainers
1
Versions
191
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

diffusion - npm Package Compare versions

Comparing version 5.7.5 to 5.8.0

src/node_modules/conversation/delegating-conversation-set.js

2

package.json
{
"name": "diffusion",
"version": "5.7.5",
"version": "5.8.0",
"description": "Diffusion Javascript UCI client",

@@ -5,0 +5,0 @@ "keywords" : ["diffusion", "reappt", "websockets", "data"],

@@ -29,5 +29,5 @@ var SessionImpl = require('session/session-impl');

*/
version : '5.7.5',
version : '5.8.0',
build : '5_dev#internal',
build : '0_dev#internal',

@@ -77,3 +77,3 @@ /**

connect : function(options) {
var sessionImpl = new SessionImpl();
var sessionImpl = new SessionImpl(options);
var session = sessionImpl.get();

@@ -111,3 +111,3 @@

sessionImpl.connect(options);
sessionImpl.connect();

@@ -114,0 +114,0 @@ return r;

@@ -6,3 +6,3 @@ var interface = require('util/interface').interface;

* An aggregate view of topics and their values, reflected as a single structure
* that is updated in real-time.
* that is updated in real time.
* <P>

@@ -9,0 +9,0 @@ * Because {@link View Views} are bound to a particular topic selector,

@@ -105,2 +105,54 @@ var interface = require('util/interface').interface;

'setSessionPropertiesListener',
/**
* Event types used within {@link Session.clients.SessionPropertiesListener#onSessionEvent}.
*
* <table class="table striped">
* <thead>
* <tr>
* <th>Event type</th>
* <th>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><code>UPDATED</code></td>
* <td>One or more relevant session properties have been updated.</td>
* </tr>
* <tr>
* <td><code>DISCONNECTED</code></td>
* <td>A session has disconnected</td>
* </tr>
* <tr>
* <td><code>RECONNECTED</code></td>
* <td>A session has reconnected</td>
* </tr>
* <tr>
* <td><code>FAILED_OVER</code></td>
* <td>A session has failed over from one server to another in a cluster.</td>
* </tr>
* </tbody>
* </table>
*
* @example
* session.clients.setSessionPropertiesListener(props, {
* // ...
*
* onSessionEvent : function(sessionID, event, properties, previous) {
* switch (event) {
* case session.clients.SessionEventType.DISCONNECTED :
* console.log(sessionID + " has disconnected");
* break;
* case session.clients.SessionEventType.RECONNECTED :
* console.log(sessionID + " has reconnected");
* break;
* }
* }
*
* // ...
* });
*
* @property {Object} Session.clients.SessionEventType
*/
'SessionEventType'
]);

@@ -107,0 +159,0 @@

@@ -246,2 +246,3 @@ var interface = require('util/interface').interface;

* @return {String} The script
* @function SystemAuthenticationScriptBuilder#build
*/

@@ -257,2 +258,3 @@ 'build',

* @return {ScriptBuilder} A new builder containing the changed roles
* @function SystemAuthenticationScriptBuilder#assignRoles
*/

@@ -269,2 +271,3 @@ 'assignRoles',

* @return {ScriptBuilder} A new builder containing the new principal
* @function SystemAuthenticationScriptBuilder#addPrincipal
*/

@@ -294,2 +297,3 @@ 'addPrincipal',

* @return {ScriptBuilder} A new builder containing the verification command
* @function SystemAuthenticationScriptBuilder#verifyPassword
*/

@@ -304,2 +308,3 @@ 'verifyPassword',

* @return {ScriptBuilder} A new builder containing the remove command
* @function SystemAuthenticationScriptBuilder#removePrincipal
*/

@@ -314,2 +319,3 @@ 'removePrincipal',

* @return {ScriptBuilder} A new builder containing the allow anonymous connections command.
* @function SystemAuthenticationScriptBuilder#allowAnonymousConnections
*/

@@ -323,2 +329,3 @@ 'allowAnonymousConnections',

* @return {ScriptBuilder} A new builder containing the deny anonymous connections command.
* @function SystemAuthenticationScriptBuilder#denyAnonymousConnections
*/

@@ -333,2 +340,3 @@ 'denyAnonymousConnections',

* @return {ScriptBuilder} A new builder containing the abstain anonymous connections command.
* @function SystemAuthenticationScriptBuilder#abstainAnonymousConnections
*/

@@ -335,0 +343,0 @@ 'abstainAnonymousConnections'

@@ -22,3 +22,12 @@ var interface = require('util/interface').interface;

* page.
* <P>
* When calling {@link Session#subscribe}, a {@link Subscription} object is
* returned to bind event listeners to. This object may be closed via
* {@link Subscription#close}, which will remove all event listeners and emit
* no further events. Closing a {@link Subscription} object is different to
* unsubscribing via {@link Session#unsubscribe} as it is a purely client-side
* operation that only affects the single {@link Subscription} returned from the
* initial {@link Session#subscribe} call.
*
*
* @example

@@ -69,3 +78,5 @@ * // Subscribe to a single topic with a callback function for updates

* associated {@link Subscription}s to emit an <code>unsubscribe</code>
* event.
* event. Any {@link Subscription} objects produced from {@link Session#subscribe}
* or {@link Session#stream} will remain open, and will continue to emit
* updates for topics that the session has not been unsubscribed from.
*

@@ -85,4 +96,4 @@ * @example

* This method operates in a similar way to {@link Session#subscribe}, except it will not cause the server to
* send any topic updates. This allows the registration of listeners prior to actually subscribing, or to
* add/remove listeners independently of subscriptions on the server.
* send any topic updates unless already subscribed. This allows the registration of listeners prior to actually
* subscribing, or to add/remove listeners independently of subscriptions on the server.
* <P>

@@ -106,2 +117,12 @@ * If no selector is provided, the {@link Subscription} stream created will receive all topic events that have not

* @example
* // Maintain a single listener independently of subscriptions/unsubscriptions
* session.stream('?foo/.*').on('update', function(update, topic) {
* console.log('Received an update for : ' + topic, update);
* });
*
* // The session can subscribe and unsubscribe from specific selectors separately from receiving updates.
* session.subscribe('foo/bar');
* session.unsubscribe('foo/baz');
*
* @example
* // Receive topic events for topics that aren't handled by any other listeners

@@ -108,0 +129,0 @@ * session.stream().on({

@@ -30,3 +30,3 @@ var Emitter = require('events/emitter');

function InternalSession(conversationSet, serviceRegistry, connectionFactory) {
function InternalSession(conversationSet, serviceRegistry, connectionFactory, opts) {
var emitter = Emitter.assign(this);

@@ -100,3 +100,3 @@

var connection = connectionFactory.create(Aliases.create(), transports);
var connection = connectionFactory.create(Aliases.create(), transports, opts.reconnect.timeout, 256);

@@ -111,3 +111,3 @@ var serviceAdapter = new ServiceAdapter(this, serialisers, connection.send);

serviceRegistry.addListener(serviceAdapter.addService);
// Close the session

@@ -129,3 +129,7 @@ function close(reason) {

var request = ConnectionRequest.reconnect(token);
var request = ConnectionRequest.reconnect(
token,
connection.getAvailableSequence(),
connection.lastReceivedSequence);
connection.connect(request, opts, opts.reconnect.timeout);

@@ -147,3 +151,7 @@ } else {

this.connect = function(opts) {
function replaceConversationSet(err) {
conversationSet.replace(err);
}
this.connect = function() {
if (fsm.change('connecting')) {

@@ -199,3 +207,3 @@ // Timeout applied for reconnect attempts from initial disconnect

}
});
});

@@ -211,6 +219,17 @@ // Handle connect response

emitter.emit('connect', response.identity);
} else if (response.response === ResponseCode.RECONNECTED) {
} else if (
response.response === ResponseCode.RECONNECTED ||
response.response === ResponseCode.RECONNECTED_WITH_MESSAGE_LOSS) {
clearTimeout(reconnectTimeout);
log.info('Reconnected session');
if (response.response === ResponseCode.RECONNECTED_WITH_MESSAGE_LOSS) {
connection.resetSequences();
replaceConversationSet(new Error("Peer is disconnected"));
log.info("Reconnected session, but messages may have been lost");
} else {
log.info('Reconnected session');
}
sessionActivityMonitor.onNewConnection(connection, response);

@@ -217,0 +236,0 @@ emitter.emit('reconnect');

@@ -26,5 +26,5 @@ var implements = require('util/interface').implements;

var getProperties = serviceLocator.obtain(Services.GET_SESSION_PROPERTIES);
var registration = serviceLocator.obtain(Services.SESSION_PROPERTIES_REGISTRATION);
var registration = serviceLocator.obtain(Services.SESSION_PROPERTIES_REGISTRATION_2);
internal.getServiceRegistry().add(Services.SESSION_PROPERTIES_EVENT, {
internal.getServiceRegistry().add(Services.SESSION_PROPERTIES_EVENT_2, {
onRequest : function(internal, message, callback) {

@@ -36,2 +36,9 @@ conversationSet.respond(message.cid, message);

this.SessionEventType = {
UPDATED : 0,
RECONNECTED : 1,
FAILED_OVER : 2,
DISCONNECTED : 3
};
this.getSessionProperties = function(sid, propertyKeys) {

@@ -82,21 +89,25 @@ var emitter = new Emitter();

respond : function(message) {
switch (message.type) {
case SessionPropertiesEventType.OPEN:
handler.onSessionOpen(message.sessionId, message.oldProperties);
break;
case SessionPropertiesEventType.UPDATE:
handler.onSessionEvent(
message.sessionId,
message.updateType,
message.newProperties,
message.oldProperties);
break;
case SessionPropertiesEventType.CLOSE:
handler.onSessionClose(
message.sessionId,
message.oldProperties,
message.closeReason);
break;
default :
logger.debug('Unknown event type received for session properties listener', message.type);
for (var i = 0; i < message.events.length; ++i) {
var event = message.events[i];
switch (event.type) {
case SessionPropertiesEventType.OPEN:
handler.onSessionOpen(event.sessionId, event.oldProperties);
break;
case SessionPropertiesEventType.UPDATE:
handler.onSessionEvent(
event.sessionId,
event.updateType,
event.newProperties,
event.oldProperties);
break;
case SessionPropertiesEventType.CLOSE:
handler.onSessionClose(
event.sessionId,
event.oldProperties,
event.closeReason);
break;
default :
logger.debug('Unknown event type received for session properties listener', event.type);
}
}

@@ -103,0 +114,0 @@ },

@@ -263,3 +263,3 @@ var implements = require('util/interface').implements;

var conversations = internal.getConversationSet();
var cid = conversations.new(updateResponseHandler(internal, path, handler));
var cid = conversations.new(updateResponseHandler(internal, valueCache, path, handler));

@@ -266,0 +266,0 @@ if (internal.checkConnected(emitter)) {

@@ -17,4 +17,10 @@ var Services = require('services/services');

if (valueCache[topic]) {
var delta = datatype.deltaType().diff(valueCache[topic], value);
var deltaType = datatype.deltaType("binary");
var delta = deltaType.diff(valueCache[topic], value);
if (delta === deltaType.noChange()) {
callback(null, {});
return;
}
DELTA_SERVICE.send({

@@ -31,3 +37,5 @@ id : 0,

}
valueCache[topic] = value;
};
};

@@ -48,4 +48,4 @@ var implements = require('util/interface').implements;

self.on('unsubscribe', function(reason) {
e.emit('unsubscribe', reason);
self.on('unsubscribe', function(reason, topic) {
e.emit('unsubscribe', reason, topic);
});

@@ -52,0 +52,0 @@

var Services = require('services/services');
var DataTypes = require('data/datatypes');
var Emitter = require('events/emitter');

@@ -7,2 +7,8 @@ var Result = require('events/result');

var util = require('metadata/util');
function dataToBytes(d) {
return d.$buffer.slice(d.$offset, d.$length);
}
function Updater(cid, dispatch) {

@@ -24,3 +30,3 @@ var self = this;

} else {
dispatch(emitter, cid, topic, new Update.Update(value));
dispatch(emitter, cid, topic, value);
}

@@ -32,21 +38,55 @@

module.exports = function UpdateResponseHandler(internal, topic, handler) {
module.exports = function UpdateResponseHandler(internal, valueCache, topic, handler) {
var UPDATE_SOURCE_DEREGISTRATION = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_DEREGISTRATION);
var UPDATE_SOURCE_UPDATE = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_UPDATE);
var UPDATE_SOURCE_DELTA = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_DELTA);
var UPDATE_SOURCE_SET = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_SET);
var dispatch = function(emitter, cid, path, update) {
if (internal.checkConnected(emitter)) {
UPDATE_SOURCE_UPDATE.send({
cid : cid,
path : path,
update : update
}, function(err, result) {
if (err) {
emitter.error(err);
} else if (result.error) {
emitter.error(new Error("Topic update error for topic " + path + " : " + result.error));
var dispatch = function(emitter, cid, path, content) {
var callback = function(err, result) {
if (err) {
emitter.error(err);
} else if (result.error) {
emitter.error(new Error("Topic update error for topic " + path + " : " + result.error));
} else {
emitter.emit('complete');
}
};
if (internal.checkConnected(emitter)) {
if (util.isMetadataValue(content)) {
UPDATE_SOURCE_UPDATE.send({
cid : cid,
path : path,
update : new Update.Update(content)
}, callback);
} else {
var datatype = DataTypes.get(content);
var value = datatype.from(content);
if (valueCache[path]) {
var deltaType = datatype.deltaType("binary");
var delta = deltaType.diff(valueCache[path], value);
if (delta === deltaType.noChange()) {
callback(null, {});
return;
}
UPDATE_SOURCE_DELTA.send({
id : 0,
cid : cid,
path : path,
bytes : dataToBytes(delta)
}, callback);
} else {
emitter.emit('complete');
UPDATE_SOURCE_SET.send({
cid : cid,
path : path,
bytes : dataToBytes(value)
}, callback);
}
});
valueCache[path] = value;
}
}

@@ -53,0 +93,0 @@ };

@@ -9,3 +9,3 @@ var consts = require('protocol/consts');

type : consts.TYPE,
version : consts.PROTOCOL_VERSION,
version : consts.CURRENT_VERSION,
capabilities : consts.CAPABILITIES

@@ -15,5 +15,8 @@ };

function createReconnectionRequest(token) {
function createReconnectionRequest(token, availableClientSequence, lastServerSequence) {
var req = createConnectionRequest();
req.token = token;
req.availableClientSequence = availableClientSequence;
req.lastServerSequence = lastServerSequence;

@@ -20,0 +23,0 @@ return req;

@@ -22,5 +22,5 @@ var BufferInputStream = require('io/buffer-input-stream');

var version = input.read();
if (version < consts.PROTOCOL_VERSION) {
if (version < consts.CURRENT_VERSION) {
throw new Error('Unrecognised protocol version: ' + version);
} else if (version > consts.PROTOCOL_VERSION) {
} else if (version > consts.CURRENT_VERSION) {
throw new Error('Unsupported protocol version: ' + version);

@@ -37,2 +37,8 @@ }

var recoverySequence = 0;
if (responseCode === ResponseCode.RECONNECTED) {
recoverySequence = input.readInt32();
}
return {

@@ -44,3 +50,4 @@ response : responseCode,

version : version,
success : true
success : true,
sequence : recoverySequence
};

@@ -54,3 +61,4 @@ } else {

systemPingPeriod : null,
success : false
success : false,
sequence : 0
};

@@ -57,0 +65,0 @@ }

@@ -5,3 +5,3 @@ module.exports = {

PROTOCOL_BYTE : 35,
PROTOCOL_VERSION : 7
CURRENT_VERSION : 8
};

@@ -8,19 +8,28 @@ function code(id, message) {

var ResponseCode = {
OK : code(100, "Connected successfully"),
DOWNGRADE : code(102, "Server does not support the requested protocol level"),
RECONNECTED : code(105, "Reconnected successfully"),
REJECTED : code(111, "Connection rejected"),
CONNECTION_UNSUPPORTED : code(112, "Connection type not supported by connector"),
LICENSE_EXCEEDED : code(113, "Connection rejected due to license limit"),
RECONNECTION_UNSUPPORTED : code(114, "Reconnection not supported by connector"),
CONNECTION_PROTOCOL_ERROR : code(115, "Connection failed - protocol error"),
AUTHENTICATION_FAILED : code(116, "Connection failed - authentication failed"),
UNKNOWN_SESSION : code(117, "Reconnection failed - the session is unknown"),
ERROR : code(127, "Connection failed due to server error")
OK : code(100, "Connected successfully"),
DOWNGRADE : code(102, "Server does not support the requested protocol level"),
RECONNECTED : code(105, "Reconnected successfully"),
RECONNECTED_WITH_MESSAGE_LOSS : code(106, "Reconnected with message loss"),
REJECTED : code(111, "Connection rejected"),
CONNECTION_UNSUPPORTED : code(112, "Connection type not supported by connector"),
LICENSE_EXCEEDED : code(113, "Connection rejected due to license limit"),
RECONNECTION_UNSUPPORTED : code(114, "Reconnection not supported by connector"),
CONNECTION_PROTOCOL_ERROR : code(115, "Connection failed - protocol error"),
AUTHENTICATION_FAILED : code(116, "Connection failed - authentication failed"),
UNKNOWN_SESSION : code(117, "Reconnection failed - the session is unknown"),
RECONNECTION_FAILED_MESSAGE_LOSS : code(118, "Reconnection failed due to message loss"),
ERROR : code(127, "Connection failed due to server error")
};
ResponseCode.isSuccess = function isSuccess(code) {
return code === ResponseCode.OK || code === ResponseCode.RECONNECTED;
switch (code) {
case ResponseCode.OK :
case ResponseCode.RECONNECTED :
case ResponseCode.RECONNECTED_WITH_MESSAGE_LOSS :
return true;
default :
return false;
}
};
module.exports = ResponseCode;

@@ -13,3 +13,2 @@ var ClientControlOptions = require('services/control/client-control-options');

var result = {
cid : ConversationIdSerialiser.read(input),
sessionId : SessionIdSerialiser.read(input),

@@ -16,0 +15,0 @@ type : BEES.read(input, SessionPropertiesEventType)

@@ -156,2 +156,6 @@ var HashMap = require('hashmap');

serialisers.set(
require('services/control/session-properties-event-batch'),
require('services/control/session-properties-event-batch-serialiser')
);
serialisers.set(
require('services/topic-update/update-source-registration-request'),

@@ -189,2 +193,10 @@ require('services/topic-update/update-source-registration-request-serialiser')

serialisers.set(
require('services/topic-update/update-source-set-request'),
require('services/topic-update/update-source-set-request-serialiser')
);
serialisers.set(
require('services/topic-update/update-source-delta-request'),
require('services/topic-update/update-source-delta-request-serialiser')
);
serialisers.set(
require('services/missing-topic/missing-topic-request'),

@@ -191,0 +203,0 @@ require('services/missing-topic/missing-topic-request-serialiser')

@@ -33,2 +33,4 @@ var TopicSelector = require('../../selectors/topic-selector');

var UpdateSourceUpdate = require('services/topic-update/update-source-update');
var UpdateSourceSetRequest = require('services/topic-update/update-source-set-request');
var UpdateSourceDeltaRequest = require('services/topic-update/update-source-delta-request');
var UpdateSourceUpdateResponse = require('services/topic-update/update-source-update-response');

@@ -54,2 +56,3 @@ var UpdateSourceStateRequest = require('services/topic-update/update-source-state-request');

var SessionPropertiesEvent = require('services/control/session-properties-event');
var SessionPropertiesEventBatch = require('services/control/session-properties-event-batch');

@@ -259,3 +262,3 @@ var MissingTopicRequest = require('services/missing-topic/missing-topic-request');

id : 69,
name : "Set session properties listener",
name : "Set session properties listener - legacy, see SESSION_PROPERTIES_REGISTRATION_2",
request : SetSessionPropertiesListener,

@@ -266,6 +269,18 @@ response : null

id : 70,
name : "Session properties event",
name : "Session properties event - legacy, see SESSION_PROPERTIES_EVENT_2",
request : SessionPropertiesEvent,
response : null
},
UPDATE_SOURCE_SET : {
id : 77,
name : "Update source set",
request : UpdateSourceSetRequest,
response : UpdateSourceUpdateResponse
},
UPDATE_SOURCE_DELTA : {
id : 78,
name : "Update source delta",
request : UpdateSourceDeltaRequest,
response : UpdateSourceUpdateResponse
},
UPDATE_TOPIC_SET : {

@@ -282,3 +297,15 @@ id : 79,

response : UpdateTopicResponse
},
SESSION_PROPERTIES_REGISTRATION_2 : {
id : 81,
name : "Session Properties registration 2",
request : SetSessionPropertiesListener,
response : null
},
SESSION_PROPERTIES_EVENT_2 : {
id : 82,
name : "Session Properties event 2",
request : SessionPropertiesEventBatch,
response : null
}
};

@@ -6,4 +6,4 @@ // Internal components

var ServiceRegistry = require('client/service-registry');
var ConversationSet = require('conversation/conversation-set');
var ConnectionFactory = require('v4-stack/connection-factory');
var DelegatingConversationSet = require('conversation/delegating-conversation-set');

@@ -24,10 +24,17 @@ // Service implementations

*/
function SessionImpl() {
function SessionImpl(options) {
var emitter = new Emitter(undefined, undefined, ['connect', 'reconnect', 'disconnect']);
var serviceRegistry = new ServiceRegistry();
var conversationSet = ConversationSet(function(cid, err) {
var conversationSet = new DelegatingConversationSet(function(cid, err) {
emitter.error(err);
});
// Merge defaults
if (typeof options === 'string') {
options = new Options({ host : options });
} else {
options = new Options(options);
}
// Assign default service implementations

@@ -41,5 +48,10 @@ serviceRegistry.add(Services.USER_PING, PingService);

var internalSession =
new InternalSession(conversationSet, serviceRegistry, ConnectionFactory);
var session = new Session(internalSession, emitter);
new InternalSession(conversationSet, serviceRegistry, ConnectionFactory, options);
// Clone the options so that we can safely modify and expose
// Remove credentials so bad actors cannot access password
var session = new Session(internalSession, emitter, options.with({
credentials : undefined
}));
// Bind session to internal session

@@ -67,15 +79,4 @@ session.on('error', internalSession.close);

// Connect the session and return it
this.connect = function(options) {
// Merge defaults
if (typeof options === 'string') {
options = new Options({ host : options });
} else {
options = new Options(options);
}
// Clone the options so that we can safely modify and expose
session.options = options.with({});
session.options.credentials = undefined;
internalSession.connect(options);
this.connect = function() {
internalSession.connect();
};

@@ -82,0 +83,0 @@

@@ -56,2 +56,5 @@

/**
* The subtransports will be looked up by transport name.
*/
module.exports = {

@@ -61,7 +64,15 @@ /**

*/
ws : websocketSubTransportFactoryProvider(),
WS : websocketSubTransportFactoryProvider(),
/**
* XHR subtransport. Supports detection of availability and construction when available.
* HTTP polling subtransport. Supports detection of availability and construction when available.
*/
xhr : xhrSubTransportFactoryProvider()
XHR : xhrSubTransportFactoryProvider(),
/**
* WebSocket subtransport. Supports detection of availability and construction when available.
*/
WEBSOCKET : websocketSubTransportFactoryProvider(),
/**
* HTTP polling subtransport. Supports detection of availability and construction when available.
*/
HTTP_POLLING : xhrSubTransportFactoryProvider()
};

@@ -0,1 +1,2 @@

var WSTransport = require('transports/ws');

@@ -10,9 +11,60 @@ var XHRTransport = require('transports/xhr');

var standardSubtransports = require('transports/subtransports');
var parsing = require('transports/parsing');
var logger = require('util/logger').create('Cascading');
/**
* The known transports. Will be looked up by name.
*/
var knownTransports = {
WS : WSTransport,
XHR : XHRTransport,
WEBSOCKET : WSTransport,
HTTP_POLLING : XHRTransport
};
/**
* Filter a list of transport names of any unknown or disabled transports.
* @param {Array} requestedTransports - Array of transport names
* @param {Subtransports} subtransports - The available subtransports
* @returns {Array} - Filtered array of transport names
*/
function filterTransports(requestedTransports, subtransports) {
return requestedTransports.filter(function(name) {
return name && knownTransports.hasOwnProperty(name);
}).filter(function(name) {
return subtransports[name].enabled;
});
}
/**
* @returns {boolean} If the client should attempt to cascade on receiving a response
*/
function isCascadableResponse(response) {
return response === ResponseCode.ERROR;
}
/**
* Create the selected transport if enabled.
* @param {Subtransports} subtransports - The subtransports that can be made
* @param {SessionOptions} options - The options used to connect
* @param {String} name - The name of the subtransport to use
* @returns {*} an available transport if enabled
*/
function createTransport(subtransports, options, name) {
var transport = knownTransports[name];
var subtransport = subtransports[name];
if (transport && subtransport && subtransport.enabled) {
return new transport(options, subtransport.constructor);
} else {
// Any unknown transports should have been filtered out by the
// filterTransports function
throw new Error('Unknown transport name: ' + name);
}
}
/**
* Encapsulate the set of available transport mechanisms and expose them in an
* implementation independent manner.
*/
function Transports(subtransports) {
function CascadeDriver(subtransports, opts, transports, cascadeNotifications) {
var emitter = Emitter.assign(this);

@@ -25,16 +77,3 @@ var self = this;

this.name = null;
/**
* The connection options.
*/
var opts = null;
/**
* The transports queue for cascading.
*/
var transports = null;
/**
* The current transport object.
*/
var transport = null;
/**
* The connection request.

@@ -46,5 +85,13 @@ */

*/
var hand = null;
var onHandshakeSuccess = null;
var onHandshakeError = null;
var responseReceived = false;
var transportFactory = createTransport.bind(null, subtransports, opts);
/**
* The current transport object.
*/
var transport = selectTransport();
/**
* Called when the transport emits a data event.

@@ -60,2 +107,9 @@ */

function onClose(reason) {
if (!responseReceived && cascade(true)) {
// Attempting to cascade
return;
}
// Transport closed without receiving any response
// close event suppressed to prevent reentering this handler
emitter.emit('close', reason);

@@ -72,2 +126,28 @@ }

/**
* Called when the connection response is received.
*/
function onConnectionResponse(response) {
responseReceived = true;
if (isCascadableResponse(response.response)) {
if (cascade()) {
// Attempting to cascade
return;
}
// Transport closed and close event emitted
}
return onHandshakeSuccess(response);
}
/**
* Called when the connection response cannot be parsed.
*/
function onParsingError(error) {
responseReceived = true;
if (!cascade()) {
// Transports exhausted
return onHandshakeError(error);
}
}
/**
* Close the current transport quietly, without generating any events.

@@ -86,17 +166,4 @@ */

/**
* Create the selected transport if enabled.
* @returns {*} an available transport if enabled
*/
function createTransport() {
if (self.name === 'WS' && subtransports.ws.enabled) {
return new WSTransport(opts, subtransports.ws.constructor);
} else if (self.name === 'XHR' && subtransports.xhr.enabled) {
return new XHRTransport(opts, subtransports.xhr.constructor);
} else {
return null;
}
}
/**
* Select and create the next enabled transport.
* @returns the selected transport
*/

@@ -108,67 +175,67 @@ function selectTransport(suppressClose) {

logger.debug('Transports exhausted');
emitter.emit('transports-exhausted');
cascadeNotifications.emit('transports-exhausted');
if (!suppressClose) {
emitter.emit('close');
}
return;
return null;
}
self.name = transports.shift();
transport = createTransport();
transport = transportFactory(self.name);
if (transport === null) {
if (transports.length > 0) {
selectTransport();
} else {
self.name = null;
transport = null;
logger.debug('Transports exhausted');
emitter.emit('transports-exhausted');
if (!suppressClose) {
emitter.emit('close');
}
logger.debug('Selecting transport', self.name);
cascadeNotifications.emit('transport-selected', self.name);
return transport;
}
/**
* @returns true if attempting to connect
*/
function internalConnect() {
responseReceived = false;
logger.debug('Attempting to connect');
cascadeNotifications.emit('cascading-connect');
transport.on('data', onData);
transport.on('close', onClose);
transport.on('error', onError);
try {
transport.connect(
req,
parsing.connectionResponse.bind(null, onConnectionResponse, onParsingError)
);
}
catch(e) {
if (!cascade()) {
// Transports exhausted
throw e;
}
} else {
logger.debug('Selecting transport', self.name);
emitter.emit('transport-selected', self.name);
}
return true;
}
this.get = function get(options) {
opts = options;
transports = opts.transports.slice();
selectTransport();
return self;
};
this.cascade = function cascade(suppressClose) {
/**
* @returns true if attempting to connect, false if transports exhausted
*/
function cascade(suppressClose) {
closeQuietly();
selectTransport(suppressClose);
if (transport === null) {
return self;
if (!selectTransport(suppressClose)) {
return false;
}
emitter.emit('cascade');
cascadeNotifications.emit('cascade');
self.connect(req, hand);
return internalConnect();
}
return self;
};
this.connect = function connect(request, handshake, handshakeError) {
req = request;
this.connect = function connect(request, handshake) {
if (transport !== null) {
logger.debug('Attempting to connect');
emitter.emit('cascading-connect');
onHandshakeSuccess = handshake;
onHandshakeError = handshakeError;
req = request;
hand = handshake;
transport.on('data', onData);
transport.on('close', onClose);
transport.on('error', onError);
transport.connect(request, handshake);
}
internalConnect();
};

@@ -186,2 +253,3 @@

if (transport !== null) {
responseReceived = true; // To prevent cascading on connection timeout
// A close event will be emitted by the transport after the close is complete

@@ -195,2 +263,20 @@ transport.close();

/**
* Encapsulate the set of available transport mechanisms and expose them in an
* implementation independent manner.
*/
function Transports(subtransports) {
var emitter = Emitter.assign(this);
this.get = function get(options) {
var validTransports = filterTransports(options.transports, subtransports);
if (validTransports.length === 0) {
return null;
}
return new CascadeDriver(subtransports, options, validTransports, emitter);
};
}
/**
* Create a new transports object.

@@ -197,0 +283,0 @@ */

@@ -1,4 +0,2 @@

var encodeAsString = require('v4-stack/credential-tunnel').encodeAsString;
var Emitter = require('events/emitter');

@@ -26,2 +24,4 @@

uri += '&c=' + encodeURIComponent(req.token);
uri += "&cs=" + req.availableClientSequence;
uri += "&ss=" + req.lastServerSequence;
}

@@ -50,3 +50,3 @@

};
var socket;

@@ -60,2 +60,3 @@

* @param {Function} handshake - The handshake handler function
* @param {Function} dataWritten - The data written handler
*/

@@ -76,2 +77,3 @@ this.connect = function connect(req, handshake) {

socket.onmessage = handler;
handshake(new Buffer(new Uint8Array(msg.data)));

@@ -91,4 +93,10 @@ };

log.trace("Sending websocket message");
socket.send(message);
return true;
try {
socket.send(message);
return true;
} catch (err) {
log.error("Websocket send error", err);
return false;
}
};

@@ -95,0 +103,0 @@

@@ -30,2 +30,4 @@ var encodeAsString = require('v4-stack/credential-tunnel').encodeAsString;

headers.c = encodeURIComponent(req.token);
headers.cs = req.availableClientSequence;
headers.ss =req.lastServerSequence;
}

@@ -54,3 +56,3 @@

// Message queue
// Message message-queue
var queue = Queue.create();

@@ -75,2 +77,9 @@ var constructor = xhr;

var serverResponse = handshake(new Buffer(handShakeData, 'binary'));
if (!serverResponse) {
// If the handshake could not be parsed or there was some sort of error return
// The transport will be closed by the error handler
return;
}
clientId = serverResponse.identity;

@@ -102,12 +111,2 @@

/**
* Send a messages to the server.
*
* @param {Message} message The message to be sent
*/
this.dispatch = function dispatch(message) {
this.sendMessage(message);
return true;
};
/**
* Aborting any opening poll request.

@@ -190,3 +189,3 @@ */

/**
* Queue message to be sent to the message queue.
* Queue message to be sent to the message message-queue.
*

@@ -197,5 +196,5 @@ * Delegate the actual request sending to #flush().

*/
this.sendMessage = function sendMessage(message) {
this.dispatch = function dispatch(message) {
if (aborted) {
return;
return false;
}

@@ -206,3 +205,3 @@

if (isSending) {
return;
return true;
}

@@ -214,3 +213,3 @@

/**
* Flush all pending messages from the queue and send upstream.
* Flush all pending messages from the message-queue and send upstream.
*/

@@ -217,0 +216,0 @@ this.flush = function flush() {

@@ -18,2 +18,37 @@ /**

/**
* Fill an array with a given value, within an optional range
*
* @param array {Array} - The array to fill
* @param value {*} - The value to fill the array with
* @param start {Number} [0] - Optional range start
* @param end {Number} [array.length] - Optional range end
*/
function fill(array, value, start, end) {
start = start || 0;
end = end || array.length;
for (var i = start; i < end; ++i) {
array[i] = value;
}
}
/**
* Create an array of a given size, optionally pre-filled with a specified initial value.
*
* @param size {Number} - The size of the array to create
* @param initialValue {*} [] - The initial value to assign to each element
*/
function ofSize(size, initialValue) {
var a = [];
a.length = size;
if (initialValue !== undefined) {
fill(a, initialValue);
}
return a;
}
var s = Array.prototype.slice;

@@ -32,4 +67,6 @@

module.exports = {
fill : fill,
ofSize : ofSize,
remove : remove,
argumentsToArray : argumentsToArray
};
/**
* Cheap approximation to a square root.
* @return a power of two that approximates the square root of a value
* @return {Number} a power of two that approximates the square root of a value
*/

@@ -21,2 +21,25 @@ module.exports.approximateSquareRoot = function(value) {

return result;
};
/**
* Find the next integer, equal or higher to the value, which is a power of two.
*
* @param {number} value the value to search from
*/
module.exports.findNextPowerOfTwo = function(value) {
if (value < 0 || value > 1 << 30) {
throw new Error("Illegal argument: " + value);
}
// See: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value++;
return value;
};

@@ -12,2 +12,9 @@ /**

/**
* @returns {Number} number of queued items
*/
this.length = function() {
return messages.length;
};
/**

@@ -14,0 +21,0 @@ * Add a message to the messages queue.

@@ -14,5 +14,6 @@ function reason(id, message, canReconnect) {

CONNECTION_ERROR : reason(7, "A connection to the server was unable to be established", true),
IDLE_CONNECTION : reason(8, "The activity monitor detected the connection was idle", true)
IDLE_CONNECTION : reason(8, "The activity monitor detected the connection was idle", true),
LOST_MESSAGES : reason(16, "Loss of messages has been detected", false)
};
module.exports = CloseReason;

@@ -1,5 +0,4 @@

var connectionResponseDeserialiser = require('protocol/connection-response-deserialiser');
var RecoveryBuffer = require('message-queue/recovery-buffer');
var BufferOutputStream = require('io/buffer-output-stream');
var ResponseCode = require('protocol/response-code');
var BufferOutputStream = require('io/buffer-output-stream');
var CloseReason = require('v4-stack/close-reason');

@@ -19,2 +18,5 @@ var Message = require('v4-stack/message');

// The default recovery buffer index size.
var RECOVERY_BUFFER_INDEX_SIZE = global.DIFFUSION_RECOVERY_BUFFER_INDEX_SIZE || 8;
/**

@@ -28,3 +30,3 @@ * The layer that abstracts across transports and handles the connection-level

*/
function Connection(aliases, transports) {
function Connection(aliases, transports, reconnectTimeout, recoveryBufferSize) {
var emitter = Emitter.assign(this);

@@ -40,5 +42,13 @@

var lastSentSequence = 0;
this.lastReceivedSequence = 0;
var recoveryBuffer = new RecoveryBuffer(recoveryBufferSize, RECOVERY_BUFFER_INDEX_SIZE);
var scheduledRecoveryBufferTrim;
var scheduledClose;
var closeReason;
var transport = null;
var self = this;

@@ -59,2 +69,5 @@ fsm.on('change', function(previous, current) {

}
self.lastReceivedSequence++;
emitter.emit('data', message);

@@ -74,13 +87,4 @@ } catch (e) {

function onClose(reason) {
if (fsm.state === 'connecting' && closeReason === CloseReason.CONNECTION_ERROR) {
transport = transport.cascade(true);
if (transport !== null && transport.name !== null) {
// Attempting cascade
return;
}
// Transport closed, close event suppressed to prevent reentering this handler
}
if (fsm.change('disconnected') || fsm.change('closed')) {
clearInterval(scheduledRecoveryBufferTrim);
clearTimeout(scheduledClose);

@@ -100,47 +104,33 @@

function isCascadableResponse(response) {
return response === ResponseCode.ERROR ||
response === ResponseCode.UNKNOWN_SESSION ||
response === ResponseCode.LICENSE_EXCEEDED;
}
/**
* Handle connection responses.
* @param response {ConnectionResponse} Parsed connection response
*/
function onHandshakeSuccess(response) {
if (response.response === ResponseCode.RECONNECTED) {
var messageDelta = lastSentSequence - response.sequence + 1;
// Handle connection responses
function handshake(message) {
logger.trace('Received connection handshake response');
if (recoveryBuffer.recover(messageDelta, dispatch)) {
recoveryBuffer.clear();
lastSentSequence = response.sequence;
} else {
var outboundLoss = lastSentSequence - recoveryBuffer.size() - response.sequence + 1;
var response;
logger.warn("Unable to reconnect due to lost messages (" + outboundLoss + ")");
try {
response = connectionResponseDeserialiser(message);
} catch (e) {
closeReason = CloseReason.HANDSHAKE_ERROR;
if (fsm.change('disconnected')) {
closeReason = CloseReason.LOST_MESSAGES;
transport.close();
}
transport = transport.cascade();
if (transport !== null && transport.name !== null) {
// Attempting cascade
return;
return response;
}
// Transport closed and close event emitted
clearTimeout(scheduledClose);
logger.trace('Unable to deserialise handshake response', e);
return;
}
logger.trace('Connection response: ', response.response);
if (isCascadableResponse(response.response)) {
closeReason = CloseReason.HANDSHAKE_REJECTED;
transport = transport.cascade();
if (transport !== null && transport.name !== null) {
// Attempting cascade
return;
}
// Transport closed and close event emitted
} else if (response.success && fsm.change('connected')) {
if (response.success && fsm.change('connected')) {
logger.trace('Connection response: ', response.response);
closeReason = CloseReason.TRANSPORT_ERROR;
emitter.emit('connect', response);
} else {
logger.debug('Connection response: ', response.response);
closeReason = CloseReason.HANDSHAKE_REJECTED;

@@ -155,2 +145,17 @@ transport.close();

/**
* Handle connection response parsing errors.
* @param response {Error} Error thrown parsing the response
*/
function onHandshakeError(error) {
closeReason = CloseReason.HANDSHAKE_ERROR;
// Transport closed and close event emitted
clearTimeout(scheduledClose);
logger.trace('Unable to deserialise handshake response', error);
return;
}
/**
* Establish a connection with a provided connection request and connection options.

@@ -175,12 +180,4 @@ */

try {
// Attempt to connect the transport
transport.connect(request, handshake);
}
catch (e) {
transport = transport.cascade();
if (transport === null || transport.name === null) {
throw e;
}
}
// Attempt to connect the transport
transport.connect(request, onHandshakeSuccess, onHandshakeError);

@@ -194,11 +191,33 @@ // Ensure we will emit a close reason if we're unable to connect within a timeout

}, timeout);
scheduledRecoveryBufferTrim = setInterval(function() {
if (fsm.state === 'connected') {
recoveryBuffer.flush(Date.now() - reconnectTimeout);
}
}, reconnectTimeout);
}
};
this.resetSequences = function() {
recoveryBuffer.clear();
lastSentSequence = 0;
this.lastReceivedSequence = 0;
};
this.getAvailableSequence = function() {
return lastSentSequence + 1 - recoveryBuffer.size();
};
// Internal dispatch method for converting a message into a buffer
function dispatch(message) {
var bos = new BufferOutputStream();
Message.writeToBuffer(message, bos);
var bos = new BufferOutputStream();
Message.writeToBuffer(message, bos);
return transport.dispatch(bos.getBuffer());
lastSentSequence += 1;
recoveryBuffer.put(message);
recoveryBuffer.markTime(Date.now());
transport.dispatch(bos.getBuffer());
}

@@ -205,0 +224,0 @@

@@ -48,3 +48,3 @@ var DEFAULT_HOST = 'localhost';

var DEFAULT_TRANSPORTS = ['WS'];
var DEFAULT_TRANSPORTS = ['WEBSOCKET'];

@@ -54,10 +54,75 @@ /**

* <P>
* Reconnection will be automatically enabled, and accepts several option values. A boolean will set whether it is
* enabled or disabled, using default settings. Passing a <code>number</code> will specify the timeout value, or a
* <code>function</code> will specify the reconnection strategy. An object containing both <code>timeout</code> and
* <code>strategy</code> keys can be used to specify both values.
* <h5>Reconnection:</h5>
* Reconnection is enabled by default, and accepts several different option values.
* <table class="table striped">
* <thead>
* <tr>
* <th>Option type</th>
* <th>Default value</th>
* <th>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><code>boolean</code></td>
* <td><code>true</code></td>
* <td>Enables or disables reconnection. If set to <code>true</code>, reconnection will be enabled using the default
* timeout value and a periodic back-off strategy.</td>
* </tr>
* <tr>
* <td><code>number</code></td>
* <td><code>60000</code></td>
* <td>Passing a number will enable reconnection with the default strategy and the reconnection timeout set to the
* specified value. The reconnection timeout determines how long, in milliseconds, the client will remain in a
* <code>disconnected</code> state before the client is closed.</td>
* </tr>
* <tr>
* <td><code>function</code></td>
* <td><code>function(reconnect, abort) {
* setTimeout(reconnect, 5000);
* }</code></td>
* <td>A strategy function that will be called when the client enters a <code>disconnected</code> state, and
* subsequently if attempts to reconnect fail. Two arguments are provided, <code>reconnect</code> and <code>abort</code>
* - these are functions to be called within the strategy. The <code>reconnect</code> argument will initiate a
* reconnect attempt. <code>abort</code> may be called to abort reconnection, in which case the client will be closed.
* </td>
* </tr>
* <tr>
* <td><code>{ timeout : <number>, strategy : <function> }</code></td>
* <td><code>{ timeout : 60000, strategy : function(reconnect, abort) {
* setTimeout(reconnect, 5000);
* } }</code></td>
* <td>An object containing both the timeout and strategy options as specified above, allowing both to be set together.
* </td>
* </tr>
* </tbody>
* </table>
* <P>
* The reconnection strategy should be a function that accepts two arguments; the first will be a function that can
* be called to attempt to reconnect, the second a function that can be called to abort. If reconnection is aborted,
* or times out, the session will close.
* <h5>Reconnection:</h5>
* The <code>transports</code> property configures how the session should connect. It can be set to either a
* <code>string</code>, or an <code>array</code> of strings to provide a transport cascading capability.
* <table class="table striped">
* <thead>
* <tr>
* <th>Transport key</th>
* <th>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><code>ws</code>, <code>WS</code>, <code>WEBSOCKET</code></td>
* <td>The websocket transport. A single, long-lived websocket connection will be used to send and receive data.</td>
* </tr>
* <tr>
* <td><code>xhr</code>, <code>XHR</code>, <code>HTTP_POLLING</code></td>
* <td>An XHR-based polling transport. Data will be queued on the client and server, and sent in batches.</td>
* </tr>
* </tbody>
* </table>
* <P>
* The client will use the transports in the order provided, for example:
* <code>transports: ['WS', 'XHR']</code> indicates that the client will attempt to connect with the WebSocket
* transport, and if the connection fails, the client will attempt to connect with the HTTP Polling transport. When no
* <code>transports</code> value is provided the client will default to using the WebSocket transport. Any string values
* that do not have an associated transport will be ignored.
*

@@ -71,2 +136,3 @@ * @typedef Session.Options

* @property {Boolean|Number|Function|Object} [reconnect=true] - Reconnection options.
* @property {String|Array} [transports=["WEBSOCKET"]] - The transports to be used for connection establishment.
*/

@@ -141,3 +207,5 @@ function Options(options) {

this.transports = [options.transports];
} else if (typeof options.transports === 'object' && options.transports instanceof Array) {
} else if (typeof options.transports === 'object' &&
options.transports instanceof Array &&
options.transports.length > 0) {
this.transports = options.transports.slice();

@@ -164,2 +232,6 @@ } else {

for (k in this) {
o[k] = this[k];
}
for (k in options) {

@@ -169,8 +241,2 @@ o[k] = options[k];

for (k in this) {
if (o[k] === undefined) {
o[k] = this[k];
}
}
return new Options(o);

@@ -177,0 +243,0 @@ };

@@ -98,3 +98,3 @@ // Namespaced features

*/
function Session(internalSession, emitter) {
function Session(internalSession, emitter, options) {
/**

@@ -109,4 +109,3 @@ * @property {String} sessionID - The unique id assigned to this session by the server.

*/
// Will be set by SessionImpl#connect
this.options = undefined;
this.options = options;

@@ -139,3 +138,8 @@ var self = this;

/**
* @return {boolean} Whether the session is currently connected or not
* Indicates if this session is currently connected.
* <P>
* This is orthogonal to {@link Session#isClosed}, as a session may
* be disconnected and attempting to reconnect.
*
* @return {boolean} Whether the session is currently connected or not.
*/

@@ -147,2 +151,16 @@ this.isConnected = function() {

/**
* Indicates if this session is currently closed, or in the process of
* closing.
* <P>
* This will not return <code>true</code> if the session is disconnected
* but attempting to reconnect.
*
* @return {boolean} Whether the session is currently closed.
*/
this.isClosed = function() {
var state = internalSession.getState();
return state === "closing" || state === "closed";
};
/**
* Exposes system authentication capabilities via a {@link Session.security}.

@@ -149,0 +167,0 @@ * @property Session.security

@@ -75,3 +75,19 @@

/**
* Deprecated
* Slave Topic.
* <P>
* A topic that references another topic (the master topic) which has data
* (i.e. an alias). It effectively allows a topic's data to be shared across
* more than one topic node.
* <P>
* A client cannot tell that it is subscribed to a slave topic. A client
* requesting details of a slave topic will receive the details of the
* master topic. A client subscribing to the slave topic will receive all
* updates to the master topic. The slave topic itself may not be updated.
* <P>
* Any number of slave topics may reference the same master topic.
* <P>
* If a topic is removed that referenced by slave topics, all such slave
* topics are also automatically removed.
* <P>
* Slave topics are unable to be created by the Javascript client, but may safely be subscribed to.
*/

@@ -78,0 +94,0 @@ SLAVE : type(7, true, false),

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc