faye
Version:
Simple pub/sub messaging for the web
187 lines (144 loc) • 5.19 kB
JavaScript
'use strict';
var Class = require('../util/class'),
URI = require('../util/uri'),
cookies = require('../util/cookies'),
assign = require('../util/assign'),
Logging = require('../mixins/logging'),
Publisher = require('../mixins/publisher'),
Transport = require('../transport'),
Scheduler = require('./scheduler');
var Dispatcher = Class({ className: 'Dispatcher',
MAX_REQUEST_SIZE: 2048,
DEFAULT_RETRY: 5,
UP: 1,
DOWN: 2,
initialize: function(client, endpoint, options) {
this._client = client;
this.endpoint = URI.parse(endpoint);
this._alternates = options.endpoints || {};
this.cookies = cookies.CookieJar && new cookies.CookieJar();
this._disabled = [];
this._envelopes = {};
this.headers = {};
this.retry = options.retry || this.DEFAULT_RETRY;
this._scheduler = options.scheduler || Scheduler;
this._state = 0;
this.transports = {};
this.wsExtensions = [];
this.proxy = options.proxy || {};
if (typeof this._proxy === 'string') this._proxy = { origin: this._proxy };
var exts = options.websocketExtensions;
if (exts) {
exts = [].concat(exts);
for (var i = 0, n = exts.length; i < n; i++)
this.addWebsocketExtension(exts[i]);
}
this.tls = options.tls || {};
this.tls.ca = this.tls.ca || options.ca;
for (var type in this._alternates)
this._alternates[type] = URI.parse(this._alternates[type]);
this.maxRequestSize = this.MAX_REQUEST_SIZE;
},
endpointFor: function(connectionType) {
return this._alternates[connectionType] || this.endpoint;
},
addWebsocketExtension: function(extension) {
this.wsExtensions.push(extension);
},
disable: function(feature) {
this._disabled.push(feature);
Transport.disable(feature);
},
setHeader: function(name, value) {
this.headers[name] = value;
},
close: function() {
var transport = this._transport;
delete this._transport;
if (transport) transport.close();
},
getConnectionTypes: function() {
return Transport.getConnectionTypes();
},
selectTransport: function(transportTypes) {
Transport.get(this, transportTypes, this._disabled, function(transport) {
this.debug('Selected ? transport for ?', transport.connectionType, transport.endpoint.href);
if (transport === this._transport) return;
if (this._transport) this._transport.close();
this._transport = transport;
this.connectionType = transport.connectionType;
}, this);
},
sendMessage: function(message, timeout, options) {
options = options || {};
var id = message.id,
attempts = options.attempts,
deadline = options.deadline && new Date().getTime() + (options.deadline * 1000),
envelope = this._envelopes[id],
scheduler;
if (!envelope) {
scheduler = new this._scheduler(message, { timeout: timeout, interval: this.retry, attempts: attempts, deadline: deadline });
envelope = this._envelopes[id] = { message: message, scheduler: scheduler };
}
this._sendEnvelope(envelope);
},
_sendEnvelope: function(envelope) {
if (!this._transport) return;
if (envelope.request || envelope.timer) return;
var message = envelope.message,
scheduler = envelope.scheduler,
self = this;
if (!scheduler.isDeliverable()) {
scheduler.abort();
delete this._envelopes[message.id];
return;
}
envelope.timer = global.setTimeout(function() {
self.handleError(message);
}, scheduler.getTimeout() * 1000);
scheduler.send();
envelope.request = this._transport.sendMessage(message);
},
handleResponse: function(reply) {
var envelope = this._envelopes[reply.id];
if (reply.successful !== undefined && envelope) {
envelope.scheduler.succeed();
delete this._envelopes[reply.id];
global.clearTimeout(envelope.timer);
}
this.trigger('message', reply);
if (this._state === this.UP) return;
this._state = this.UP;
this._client.trigger('transport:up');
},
handleError: function(message, immediate) {
var envelope = this._envelopes[message.id],
request = envelope && envelope.request,
self = this;
if (!request) return;
request.then(function(req) {
if (req && req.abort) req.abort();
});
var scheduler = envelope.scheduler;
scheduler.fail();
global.clearTimeout(envelope.timer);
envelope.request = envelope.timer = null;
if (immediate) {
this._sendEnvelope(envelope);
} else {
envelope.timer = global.setTimeout(function() {
envelope.timer = null;
self._sendEnvelope(envelope);
}, scheduler.getInterval() * 1000);
}
if (this._state === this.DOWN) return;
this._state = this.DOWN;
this._client.trigger('transport:down');
}
});
Dispatcher.create = function(client, endpoint, options) {
return new Dispatcher(client, endpoint, options);
};
assign(Dispatcher.prototype, Publisher);
assign(Dispatcher.prototype, Logging);
module.exports = Dispatcher;