@emartech/faye-redis-sharded
Version:
Redis backend engine for Faye with support for sharding
141 lines (112 loc) • 4.2 kB
JavaScript
Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
UNCONNECTED: 1,
CONNECTING: 2,
CONNECTED: 3,
batching: false,
isUsable: function(callback, context) {
this.callback(function() { callback.call(context, true) });
this.errback(function() { callback.call(context, false) });
this.connect();
},
request: function(envelopes) {
this._pending = this._pending || new Faye.Set();
for (var i = 0, n = envelopes.length; i < n; i++) this._pending.add(envelopes[i]);
this.callback(function(socket) {
if (!socket) return;
var messages = Faye.map(envelopes, function(e) { return e.message });
socket.send(Faye.toJSON(messages));
}, this);
this.connect();
},
connect: function() {
if (Faye.Transport.WebSocket._unloaded) return;
this._state = this._state || this.UNCONNECTED;
if (this._state !== this.UNCONNECTED) return;
this._state = this.CONNECTING;
var socket = this._createSocket();
if (!socket) return this.setDeferredStatus('failed');
var self = this;
socket.onopen = function() {
if (socket.headers) self._storeCookies(socket.headers['set-cookie']);
self._socket = socket;
self._state = self.CONNECTED;
self._everConnected = true;
self._ping();
self.setDeferredStatus('succeeded', socket);
};
var closed = false;
socket.onclose = socket.onerror = function() {
if (closed) return;
closed = true;
var wasConnected = (self._state === self.CONNECTED);
socket.onopen = socket.onclose = socket.onerror = socket.onmessage = null;
delete self._socket;
self._state = self.UNCONNECTED;
self.removeTimeout('ping');
self.setDeferredStatus('unknown');
var pending = self._pending ? self._pending.toArray() : [];
delete self._pending;
if (wasConnected) {
self.handleError(pending, true);
} else if (self._everConnected) {
self.handleError(pending);
} else {
self.setDeferredStatus('failed');
}
};
socket.onmessage = function(event) {
var messages = JSON.parse(event.data),
envelopes = [],
envelope;
if (!messages) return;
messages = [].concat(messages);
for (var i = 0, n = messages.length; i < n; i++) {
if (messages[i].successful === undefined) continue;
envelope = self._pending.remove(messages[i]);
if (envelope) envelopes.push(envelope);
}
self.receive(envelopes, messages);
};
},
close: function() {
if (!this._socket) return;
this._socket.close();
},
_createSocket: function() {
var url = Faye.Transport.WebSocket.getSocketUrl(this.endpoint),
options = {headers: Faye.copyObject(this._client.headers), ca: this._client.ca};
options.headers['Cookie'] = this._getCookies();
if (Faye.WebSocket) return new Faye.WebSocket.Client(url, [], options);
if (Faye.ENV.MozWebSocket) return new MozWebSocket(url);
if (Faye.ENV.WebSocket) return new WebSocket(url);
},
_ping: function() {
if (!this._socket) return;
this._socket.send('[]');
this.addTimeout('ping', this._client._advice.timeout/2000, this._ping, this);
}
}), {
PROTOCOLS: {
'http:': 'ws:',
'https:': 'wss:'
},
create: function(client, endpoint) {
var sockets = client.transports.websocket = client.transports.websocket || {};
sockets[endpoint.href] = sockets[endpoint.href] || new this(client, endpoint);
return sockets[endpoint.href];
},
getSocketUrl: function(endpoint) {
endpoint = Faye.copyObject(endpoint);
endpoint.protocol = this.PROTOCOLS[endpoint.protocol];
return Faye.URI.stringify(endpoint);
},
isUsable: function(client, endpoint, callback, context) {
this.create(client, endpoint).isUsable(callback, context);
}
});
Faye.extend(Faye.Transport.WebSocket.prototype, Faye.Deferrable);
Faye.Transport.register('websocket', Faye.Transport.WebSocket);
if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
Faye.Event.on(Faye.ENV, 'beforeunload', function() {
Faye.Transport.WebSocket._unloaded = true;
});