UNPKG

faye

Version:

Simple pub/sub messaging for the web

218 lines (164 loc) 6.12 kB
'use strict'; var Class = require('../util/class'), Cookie = require('../util/cookies').Cookie, Promise = require('../util/promise'), array = require('../util/array'), assign = require('../util/assign'), Logging = require('../mixins/logging'), Timeouts = require('../mixins/timeouts'), Channel = require('../protocol/channel'); var Transport = assign(Class({ className: 'Transport', DEFAULT_PORTS: { 'http:': 80, 'https:': 443, 'ws:': 80, 'wss:': 443 }, MAX_DELAY: 0, batching: true, initialize: function(dispatcher, endpoint) { this._dispatcher = dispatcher; this.endpoint = endpoint; this._outbox = []; this._proxy = assign({}, this._dispatcher.proxy); if (!this._proxy.origin) this._proxy.origin = this._findProxy(); }, close: function() {}, encode: function(messages) { return ''; }, sendMessage: function(message) { this.debug('Client ? sending message to ?: ?', this._dispatcher.clientId, this.endpoint.href, message); if (!this.batching) return Promise.resolve(this.request([message])); this._outbox.push(message); this._flushLargeBatch(); if (message.channel === Channel.HANDSHAKE) return this._publish(0.01); if (message.channel === Channel.CONNECT) this._connectMessage = message; return this._publish(this.MAX_DELAY); }, _makePromise: function() { var self = this; this._requestPromise = this._requestPromise || new Promise(function(resolve) { self._resolvePromise = resolve; }); }, _publish: function(delay) { this._makePromise(); this.addTimeout('publish', delay, function() { this._flush(); delete this._requestPromise; }, this); return this._requestPromise; }, _flush: function() { this.removeTimeout('publish'); if (this._outbox.length > 1 && this._connectMessage) this._connectMessage.advice = { timeout: 0 }; this._resolvePromise(this.request(this._outbox)); this._connectMessage = null; this._outbox = []; }, _flushLargeBatch: function() { var string = this.encode(this._outbox); if (string.length < this._dispatcher.maxRequestSize) return; var last = this._outbox.pop(); this._makePromise(); this._flush(); if (last) this._outbox.push(last); }, _receive: function(replies) { if (!replies) return; replies = [].concat(replies); this.debug('Client ? received from ? via ?: ?', this._dispatcher.clientId, this.endpoint.href, this.connectionType, replies); for (var i = 0, n = replies.length; i < n; i++) this._dispatcher.handleResponse(replies[i]); }, _handleError: function(messages, immediate) { messages = [].concat(messages); this.debug('Client ? failed to send to ? via ?: ?', this._dispatcher.clientId, this.endpoint.href, this.connectionType, messages); for (var i = 0, n = messages.length; i < n; i++) this._dispatcher.handleError(messages[i]); }, _getCookies: function() { var cookies = this._dispatcher.cookies, url = this.endpoint.href; if (!cookies) return ''; return array.map(cookies.getCookiesSync(url), function(cookie) { return cookie.cookieString(); }).join('; '); }, _storeCookies: function(setCookie) { var cookies = this._dispatcher.cookies, url = this.endpoint.href, cookie; if (!setCookie || !cookies) return; setCookie = [].concat(setCookie); for (var i = 0, n = setCookie.length; i < n; i++) { cookie = Cookie.parse(setCookie[i]); cookies.setCookieSync(cookie, url); } }, _findProxy: function() { if (typeof process === 'undefined') return undefined; var protocol = this.endpoint.protocol; if (!protocol) return undefined; var name = protocol.replace(/:$/, '').toLowerCase() + '_proxy', upcase = name.toUpperCase(), env = process.env, keys, proxy; if (name === 'http_proxy' && env.REQUEST_METHOD) { keys = Object.keys(env).filter(function(k) { return /^http_proxy$/i.test(k) }); if (keys.length === 1) { if (keys[0] === name && env[upcase] === undefined) proxy = env[name]; } else if (keys.length > 1) { proxy = env[name]; } proxy = proxy || env['CGI_' + upcase]; } else { proxy = env[name] || env[upcase]; if (proxy && !env[name]) console.warn('The environment variable ' + upcase + ' is discouraged. Use ' + name + '.'); } return proxy; } }), { get: function(dispatcher, allowed, disabled, callback, context) { var endpoint = dispatcher.endpoint; array.asyncEach(this._transports, function(pair, resume) { var connType = pair[0], klass = pair[1], connEndpoint = dispatcher.endpointFor(connType); if (array.indexOf(disabled, connType) >= 0) return resume(); if (array.indexOf(allowed, connType) < 0) { klass.isUsable(dispatcher, connEndpoint, function() {}); return resume(); } klass.isUsable(dispatcher, connEndpoint, function(isUsable) { if (!isUsable) return resume(); var transport = klass.hasOwnProperty('create') ? klass.create(dispatcher, connEndpoint) : new klass(dispatcher, connEndpoint); callback.call(context, transport); }); }, function() { throw new Error('Could not find a usable connection type for ' + endpoint.href); }); }, register: function(type, klass) { this._transports.push([type, klass]); klass.prototype.connectionType = type; }, getConnectionTypes: function() { return array.map(this._transports, function(t) { return t[0] }); }, disable: function(feature) { if (feature !== 'autodisconnect') return; for (var i = 0; i < this._transports.length; i++) this._transports[i][1]._unloaded = false; }, _transports: [] }); assign(Transport.prototype, Logging); assign(Transport.prototype, Timeouts); module.exports = Transport;