mirror of
https://github.com/Hopiu/postal.js.git
synced 2026-03-19 23:40:28 +00:00
298 lines
No EOL
9.5 KiB
JavaScript
298 lines
No EOL
9.5 KiB
JavaScript
/*
|
|
postal.socket
|
|
Author: Jim Cowart
|
|
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
|
Version 0.1.0
|
|
*/
|
|
|
|
(function ( root, doc, factory ) {
|
|
if ( typeof define === "function" && define.amd ) {
|
|
// AMD. Register as an anonymous module.
|
|
define( [ "underscore", "machina", "postal" ], function ( _, machina, postal ) {
|
|
return factory( _, machina, postal, root, doc );
|
|
} );
|
|
} else {
|
|
// Browser globals
|
|
factory( root._, root.machina, root.postal, root, doc );
|
|
}
|
|
}( this, document, function ( _, machina, postal, global, document, undefined ) {
|
|
|
|
/*
|
|
adding a socket namespace to postal
|
|
which provides the following members:
|
|
1.) config - provides values used to manage the socket connection
|
|
2.) goOffline() - tells the manager to close the connection intentionally
|
|
3.) goOnline() - tells the manager to try to connect.
|
|
4.) manifest - an array of objects describing the subscriptions that have been
|
|
set up on the remote end.
|
|
5.) publish() - takes a valid postal envelope and pushes it through the socket
|
|
to be published to the server instance of postal.
|
|
6.) socketMgr - the FSM managing the socket connection
|
|
7.) socketNamespace - exposed currently for debugging only
|
|
8.) subscribe() - passes an object through the socket to the server
|
|
which contains data necessary to set up a remote subscription. The
|
|
options passed to the socket would look similar to this:
|
|
{
|
|
"channel":"SomeChannel",
|
|
"topic":"my.topic",
|
|
"correlationId":"2073383865318591267"
|
|
}
|
|
The "correlationId" is used on the remote side to apply a constraint to
|
|
the subscription, enabling just this specific client to be targeted on
|
|
and otherwise public channel.
|
|
9.) unsubscribe() - passes an object through the socket to the server
|
|
which contains data necessary to remove a remote subscription. The options
|
|
passed would look similar to the example above in #8.
|
|
*/
|
|
postal.connections = postal.connections || {};
|
|
|
|
var postalSocket = postal.connections.socket = (function () {
|
|
var socketNamespace,
|
|
fsm = new machina.Fsm( {
|
|
retryFn : undefined,
|
|
|
|
session : undefined,
|
|
|
|
wireUpSocketEvents : function () {
|
|
var self = this;
|
|
_.each( [ "connect", "connecting", "connect_failed", "disconnect", "reconnect", "reconnect_failed",
|
|
"reconnecting", "postal.socket.remote", "postal.socket.identified", "postal.socket.migration" ],
|
|
function ( evnt ) {
|
|
socketNamespace.on( evnt, function ( data ) {
|
|
self.handle( evnt, data );
|
|
} );
|
|
} );
|
|
},
|
|
|
|
states : {
|
|
uninitialized : {
|
|
tryConnect : function () {
|
|
this.transition( "initializing" );
|
|
}
|
|
},
|
|
initializing : {
|
|
_onEnter : function () {
|
|
socketNamespace = io.connect( postalSocket.config.url, { "auto connect" : false } );
|
|
this.wireUpSocketEvents();
|
|
this.transition( "probing" )
|
|
},
|
|
socketTransmit : function () {
|
|
this.deferUntilTransition( "online" );
|
|
}
|
|
},
|
|
probing : {
|
|
_onEnter : function () {
|
|
clearTimeout( this.retryFn );
|
|
if ( !socketNamespace.socket.connecting && !socketNamespace.socket.reconnecting ) {
|
|
socketNamespace.socket.connect();
|
|
}
|
|
else {
|
|
this.transition( "settingSessionInfo" );
|
|
}
|
|
},
|
|
connect : function () {
|
|
this.transition( "settingSessionInfo" );
|
|
},
|
|
connect_failed : function () {
|
|
this.transition( "disconnected" );
|
|
},
|
|
maxAttempts : function () {
|
|
this.transition( "offline" );
|
|
},
|
|
"postal.socket.remote" : function () {
|
|
this.deferUntilTransition( "online" );
|
|
},
|
|
reconnect : function () {
|
|
this.transition( "settingSessionInfo" );
|
|
},
|
|
reconnect_failed : function () {
|
|
this.transition( "disconnected" );
|
|
},
|
|
socketTransmit : function () {
|
|
this.deferUntilTransition( "online" );
|
|
}
|
|
},
|
|
settingSessionInfo : {
|
|
_onEnter : function () {
|
|
var self = this;
|
|
postal.utils.setSessionId( socketNamespace.socket.sessionid, function ( session ) {
|
|
self.session = session;
|
|
self.transition( "identifying" );
|
|
} );
|
|
}
|
|
},
|
|
identifying : {
|
|
_onEnter : function () {
|
|
var self = this;
|
|
self.retryFn = setTimeout( function () {
|
|
self.handle( "timeout.identifying" );
|
|
}, postalSocket.config.reconnectInterval );
|
|
self.handle( "client.identifier" );
|
|
},
|
|
"client.identifier" : function () {
|
|
clearTimeout( this.retryFn );
|
|
socketNamespace.emit( "postal.clientId", { id : this.session.id, lastId : this.session.lastId } );
|
|
},
|
|
connect_failed : function () {
|
|
this.transition( "disconnected" );
|
|
},
|
|
disconnect : function () {
|
|
this.transition( "probing" );
|
|
},
|
|
"postal.socket.identified" : function ( data ) {
|
|
this.transition( "online" );
|
|
},
|
|
"postal.socket.migration" : function () {
|
|
_.each( postal.connections.socket.manifest, function ( sub ) {
|
|
fsm.handle( "socketTransmit", "postal.subscribe", sub );
|
|
} );
|
|
socketNamespace.emit( "postal.migrationComplete", {} );
|
|
},
|
|
"postal.socket.remote" : function () {
|
|
this.deferUntilTransition( "online" );
|
|
},
|
|
reconnect_failed : function () {
|
|
this.transition( "disconnected" );
|
|
},
|
|
socketTransmit : function ( evntName, envelope ) {
|
|
if ( evntName === "postal.subscribe" ) {
|
|
// we risk mutating the message here, so extend
|
|
// and add the correlationId to the extended copy
|
|
var socketEnv = _.extend( {}, envelope );
|
|
socketEnv.correlationId = this.session.id;
|
|
socketNamespace.emit( evntName, socketEnv );
|
|
}
|
|
else {
|
|
this.deferUntilTransition( "online" );
|
|
}
|
|
},
|
|
"timeout.identifying" : function () {
|
|
this.transition( "probing" );
|
|
}
|
|
},
|
|
online : {
|
|
disconnect : function () {
|
|
this.transition( "probing" );
|
|
},
|
|
goOffline : function () {
|
|
this.transition( "offline" );
|
|
},
|
|
"postal.socket.remote" : function ( envelope ) {
|
|
postal.publish( envelope );
|
|
},
|
|
socketTransmit : function ( evntName, envelope ) {
|
|
// we risk mutating the message here, so extend
|
|
// and add the correlationId to the extended copy
|
|
var socketEnv = _.extend( {}, envelope );
|
|
socketEnv.correlationId = this.session.id;
|
|
socketNamespace.emit( evntName, socketEnv );
|
|
}
|
|
},
|
|
offline : {
|
|
_onEnter : function () {
|
|
socketNamespace.socket.disconnect();
|
|
},
|
|
socketTransmit : function () {
|
|
this.deferUntilTransition( "online" );
|
|
},
|
|
"tryConnect" : function () {
|
|
this.transition( "probing" );
|
|
}
|
|
},
|
|
disconnected : {
|
|
_onEnter : function () {
|
|
var self = this;
|
|
self.retryFn = setTimeout( function () {
|
|
self.transition( "probing" );
|
|
}, postalSocket.config.reconnectInterval );
|
|
},
|
|
connecting : function () {
|
|
this.transition( "probing" );
|
|
},
|
|
reconnecting : function () {
|
|
this.transition( "probing" );
|
|
},
|
|
socketTransmit : function () {
|
|
this.deferUntilTransition( "online" );
|
|
}
|
|
}
|
|
}
|
|
} );
|
|
postal.subscribe( {
|
|
channel : "postal",
|
|
topic : "sessionId.changed",
|
|
callback : function () {
|
|
fsm.handle( "postal.session.changed" );
|
|
}
|
|
} );
|
|
return {
|
|
config : {
|
|
url : window.location.origin,
|
|
reconnectInterval : 4000
|
|
},
|
|
goOffline : function () {
|
|
fsm.handle( "goOffline" );
|
|
},
|
|
goOnline : function () {
|
|
fsm.handle( "tryConnect" );
|
|
},
|
|
manifest : [],
|
|
publish : function ( envelope ) {
|
|
fsm.handle( "socketTransmit", "postal.publish", envelope );
|
|
},
|
|
subscribe : function ( options ) {
|
|
options.channel = options.channel || postal.configuration.DEFAULT_CHANNEL;
|
|
options.topic = options.topic || "*";
|
|
if ( !_.any( this.manifest, function ( item ) {
|
|
return item.channel === options.channel && item.topic === options.topic;
|
|
} ) ) {
|
|
this.manifest.push( options );
|
|
fsm.handle( "socketTransmit", "postal.subscribe", options );
|
|
}
|
|
},
|
|
socketMgr : fsm,
|
|
socketNamespace : socketNamespace,
|
|
unsubscribe : function ( options ) {
|
|
options.channel = options.channel || postal.configuration.DEFAULT_CHANNEL;
|
|
options.topic = options.topic || "*";
|
|
if ( !postal.getSubscribersFor( options.channel, options.topic ).length ) {
|
|
fsm.handle( "socketTransmit", "postal.unsubscribe", options );
|
|
}
|
|
}
|
|
}
|
|
})();
|
|
|
|
postal.connections.socket.goOnline();
|
|
var SocketChannel = postal.channelTypes.websocket = function ( channelName, defaultTopic ) {
|
|
var channel = postal.channel( channelName, defaultTopic ),
|
|
localSubscribe = channel.subscribe,
|
|
localPublish = channel.publish,
|
|
localTopic = channel.topic;
|
|
|
|
channel.publish = function () {
|
|
postalSocket.publish( localPublish.apply( channel, arguments ) );
|
|
};
|
|
|
|
channel.subscribe = function () {
|
|
var sub = localSubscribe.apply( channel, arguments ),
|
|
origUnsubscribe;
|
|
origUnsubscribe = sub.unsubscribe;
|
|
sub.unsubscribe = function () {
|
|
origUnsubscribe.call( sub );
|
|
postalSocket.unsubscribe( { channel : sub.channel, topic : sub.topic } );
|
|
};
|
|
postalSocket.subscribe( { channel : sub.channel, topic : sub.topic } );
|
|
return sub;
|
|
};
|
|
|
|
channel.topic = function ( topic ) {
|
|
if ( topic === channel._topic ) {
|
|
return this;
|
|
}
|
|
return new SocketChannel( this.channel, topic );
|
|
};
|
|
|
|
return channel;
|
|
};
|
|
|
|
} )); |