Comparing version 1.5.2 to 2.0.0
66
index.js
'use strict'; | ||
var EventEmitter = require('events').EventEmitter | ||
, PrimusError = require('./errors').PrimusError | ||
var PrimusError = require('./errors').PrimusError | ||
, EventEmitter = require('eventemitter3') | ||
, Transformer = require('./transformer') | ||
@@ -20,2 +20,7 @@ , Spark = require('./spark') | ||
if (!(this instanceof Primus)) return new Primus(server, options); | ||
if ('object' !== typeof server) { | ||
var message = 'The first argument of the constructor must be ' + | ||
'an HTTP or HTTPS server instance'; | ||
throw new PrimusError(message, this); | ||
} | ||
@@ -30,5 +35,8 @@ options = options || {}; | ||
this.sparks = 0; // Increment id for connection ids. | ||
this.connected = 0; // Connection counter . | ||
this.connected = 0; // Connection counter. | ||
this.connections = Object.create(null); // Connection storage. | ||
this.ark = Object.create(null); // Plugin storage. | ||
this.timeout = 'timeout' in options // The timeout used to detect zombie sparks. | ||
? options.timeout | ||
: 35000; | ||
this.whitelist = []; // Forwarded-for whitelisting. | ||
@@ -103,3 +111,3 @@ this.options = options; // The configuration. | ||
__filename: 'primus.js', | ||
__dirname: __dirname | ||
__dirname: process.cwd() | ||
}).Primus; | ||
@@ -351,3 +359,3 @@ } | ||
var client = '(function (name, context, definition) {' | ||
+ ' context[name] = definition();' | ||
+ ' context[name] = definition.call(context);' | ||
+ ' if (typeof module !== "undefined" && module.exports) {' | ||
@@ -373,2 +381,12 @@ + ' module.exports = context[name];' | ||
// | ||
// As we're given a timeout value on the server side, we need to update the | ||
// `ping` interval of the client to ensure that we've sent the server | ||
// a message before the timeout gets triggered and we get disconnected. | ||
// | ||
if ('number' === typeof this.timeout) { | ||
var timeout = this.timeout - 10000; | ||
client = client.replace('options.ping : 25e3;', 'options.ping : '+ timeout +';'); | ||
} | ||
// | ||
// Add the parser inside the closure, to prevent global leaking. | ||
@@ -401,3 +419,13 @@ // | ||
// | ||
return client +' return Primus; });'+ library.filter(Boolean).join('\n'); | ||
return client +' return Primus; });' + library | ||
.filter(Boolean) | ||
.map(function wrap(lib) { | ||
// | ||
// Wrap the libraries in a closure, they could expose variables or | ||
// override variables that are used by the application. A good example of | ||
// this is the use of the `require` function which requirejs, browserify | ||
// and others are using. | ||
// | ||
return '(function library(Primus, global) {'+ lib +'}(this.Primus, this));'; | ||
}).join('\n'); | ||
}; | ||
@@ -582,2 +610,28 @@ | ||
/** | ||
* Checks if the given event is an emitted event by Primus. | ||
* | ||
* @param {String} evt The event name. | ||
* @returns {Boolean} | ||
* @api public | ||
*/ | ||
Primus.prototype.reserved = function reserved(evt) { | ||
return (/^(incoming|outgoing)::/).test(evt) | ||
|| evt in reserved.events; | ||
}; | ||
/** | ||
* The actual events that are used by Primus. | ||
* | ||
* @type {Object} | ||
* @api public | ||
*/ | ||
Primus.prototype.reserved.events = { | ||
disconnection: 1, | ||
initialised: 1, | ||
connection: 1, | ||
close: 1, | ||
log: 1 | ||
}; | ||
/** | ||
* Add a createSocket interface so we can create a Server client with the | ||
@@ -584,0 +638,0 @@ * specified `transformer` and `parser`. |
{ | ||
"name": "primus", | ||
"version": "1.5.2", | ||
"version": "2.0.0", | ||
"description": "Primus is a simple abstraction around real-time frameworks. It allows you to easily switch between different frameworks without any code changes.", | ||
@@ -28,4 +28,6 @@ "main": "index.js", | ||
"dependencies": { | ||
"extendable": "0.0.x", | ||
"load": "1.0.x" | ||
"load": "1.0.x", | ||
"predefine": "0.0.x", | ||
"eventemitter3": "0.1.x", | ||
"forwarded-for": "0.0.x" | ||
}, | ||
@@ -32,0 +34,0 @@ "devDependencies": { |
318
primus.js
/*globals require, define */ | ||
'use strict'; | ||
// | ||
// Sets the default connection URL, it uses the default origin of the browser | ||
// when supported but degrades for older browsers. In Node.js, we cannot guess | ||
// where the user wants to connect to, so we just default to localhost. | ||
// | ||
var defaultUrl; | ||
try { | ||
if (location.origin) { | ||
defaultUrl = location.origin; | ||
} else { | ||
defaultUrl = location.protocol +'//'+ location.hostname + (location.port ? ':'+ location.port : ''); | ||
} | ||
} catch (e) { | ||
defaultUrl = 'http://127.0.0.1'; | ||
} | ||
/** | ||
@@ -40,3 +23,3 @@ * Minimal EventEmitter interface that is molded against the Node.js | ||
EventEmitter.prototype.listeners = function listeners(event) { | ||
return (this._events[event] || []).slice(0); | ||
return Array.apply(this, this._events[event] || []); | ||
}; | ||
@@ -52,8 +35,8 @@ | ||
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { | ||
if (!this._events[event]) return false; | ||
if (!this._events || !this._events[event]) return false; | ||
var listeners = this._events[event] | ||
, length = listeners.length | ||
, handler = listeners[0] | ||
, len = arguments.length | ||
, fn = listeners[0] | ||
, args | ||
@@ -65,18 +48,18 @@ , i; | ||
case 1: | ||
handler.call(this); | ||
fn.call(fn.context || this); | ||
break; | ||
case 2: | ||
handler.call(this, a1); | ||
fn.call(fn.context || this, a1); | ||
break; | ||
case 3: | ||
handler.call(this, a1, a2); | ||
fn.call(fn.context || this, a1, a2); | ||
break; | ||
case 4: | ||
handler.call(this, a1, a2, a3); | ||
fn.call(fn.context || this, a1, a2, a3); | ||
break; | ||
case 5: | ||
handler.call(this, a1, a2, a3, a4); | ||
fn.call(fn.context || this, a1, a2, a3, a4); | ||
break; | ||
case 6: | ||
handler.call(this, a1, a2, a3, a4, a5); | ||
fn.call(fn.context || this, a1, a2, a3, a4, a5); | ||
break; | ||
@@ -89,4 +72,6 @@ | ||
handler.apply(this, args); | ||
fn.apply(fn.context || this, args); | ||
} | ||
if (fn.once) this.removeListener(event, fn); | ||
} else { | ||
@@ -97,4 +82,5 @@ for (i = 1, args = new Array(len -1); i < len; i++) { | ||
for (i = 0; i < length; i++) { | ||
listeners[i].apply(this, args); | ||
for (i = 0; i < length; fn = listeners[++i]) { | ||
fn.apply(fn.context || this, args); | ||
if (fn.once) this.removeListener(event, fn); | ||
} | ||
@@ -111,6 +97,10 @@ } | ||
* @param {Functon} fn Callback function. | ||
* @param {Mixed} context The context of the function. | ||
* @api public | ||
*/ | ||
EventEmitter.prototype.on = function on(event, fn) { | ||
EventEmitter.prototype.on = function on(event, fn, context) { | ||
if (!this._events) this._events = {}; | ||
if (!this._events[event]) this._events[event] = []; | ||
fn.context = context; | ||
this._events[event].push(fn); | ||
@@ -126,14 +116,8 @@ | ||
* @param {Function} fn Callback function. | ||
* @param {Mixed} context The context of the function. | ||
* @api public | ||
*/ | ||
EventEmitter.prototype.once = function once(event, fn) { | ||
var ee = this; | ||
function eject() { | ||
ee.removeListener(event, eject); | ||
fn.apply(ee, arguments); | ||
} | ||
eject.fn = fn; | ||
return ee.on(event, eject); | ||
EventEmitter.prototype.once = function once(event, fn, context) { | ||
fn.once = true; | ||
return this.on(event, fn, context); | ||
}; | ||
@@ -155,3 +139,3 @@ | ||
for (var i = 0, length = listeners.length; i < length; i++) { | ||
if (!(!fn || listeners[i] === fn || listeners[i].fn === fn)) { | ||
if (fn && listeners[i] !== fn && listeners[i].fn !== fn) { | ||
events.push(listeners[i]); | ||
@@ -177,2 +161,4 @@ } | ||
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { | ||
if (!this._events) return this; | ||
if (event) this._events[event] = null; | ||
@@ -184,2 +170,15 @@ else this._events = {}; | ||
// | ||
// Alias methods names because people roll like that. | ||
// | ||
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; | ||
EventEmitter.prototype.addListener = EventEmitter.prototype.on; | ||
// | ||
// This function doesn't apply anymore. | ||
// | ||
EventEmitter.prototype.setMaxListeners = function setMaxListeners() { | ||
return this; | ||
}; | ||
/** | ||
@@ -198,6 +197,26 @@ * Context assertion, ensure that some of our public Primus methods are called | ||
if (!self.listeners('error').length) throw failure; | ||
if ('function' !== typeof self.listeners || !self.listeners('error').length) { | ||
throw failure; | ||
} | ||
self.emit('error', failure); | ||
} | ||
// | ||
// Sets the default connection URL, it uses the default origin of the browser | ||
// when supported but degrades for older browsers. In Node.js, we cannot guess | ||
// where the user wants to connect to, so we just default to localhost. | ||
// | ||
var defaultUrl; | ||
try { | ||
if (location.origin) { | ||
defaultUrl = location.origin; | ||
} else { | ||
defaultUrl = location.protocol +'//'+ location.hostname + (location.port ? ':'+ location.port : ''); | ||
} | ||
} catch (e) { | ||
defaultUrl = 'http://127.0.0.1'; | ||
} | ||
/** | ||
@@ -217,2 +236,3 @@ * Primus in a real-time library agnostic framework for establishing real-time | ||
* - transport, Transport options. | ||
* - url, uri, The URL to use connect with the server. | ||
* | ||
@@ -222,3 +242,3 @@ * @constructor | ||
* @param {Object} options The configuration. | ||
* @api private | ||
* @api public | ||
*/ | ||
@@ -228,13 +248,29 @@ function Primus(url, options) { | ||
if ('object' === typeof url) { | ||
options = url; | ||
url = options.url || options.uri || defaultUrl; | ||
} else { | ||
options = options || {}; | ||
} | ||
var primus = this; | ||
options = options || {}; | ||
options.timeout = +options.timeout || 10e3; // Connection timeout duration. | ||
options.reconnect = options.reconnect || {}; // Stores the back off configuration. | ||
options.ping = +options.ping || 25e3; // Heartbeat ping interval. | ||
options.pong = +options.pong || 10e3; // Heartbeat pong response timeout. | ||
options.strategy = 'strategy' in options // Reconnect strategies, supplying | ||
? options.strategy | ||
: []; | ||
// Connection timeout duration. | ||
options.timeout = 'timeout' in options ? options.timeout : 10e3; | ||
// Stores the back off configuration. | ||
options.reconnect = 'reconnect' in options ? options.reconnect : {}; | ||
// Heartbeat ping interval. | ||
options.ping = 'ping' in options ? options.ping : 25e3; | ||
// Heartbeat pong response timeout. | ||
options.pong = 'pong' in options ? options.pong : 10e3; | ||
// Reconnect strategies. | ||
options.strategy = 'strategy' in options ? options.strategy : []; | ||
// Custom transport options. | ||
options.transport = 'transport' in options ? options.transport : {}; | ||
primus.buffer = []; // Stores premature send data. | ||
@@ -249,2 +285,3 @@ primus.writable = true; // Silly stream compatibility. | ||
primus.socket = null; // Reference to the internal connection. | ||
primus.latency = 0; // Latency between messages. | ||
primus.transport = options.transport || {}; // Transport options. | ||
@@ -373,6 +410,6 @@ primus.transformers = { // Message transformers. | ||
// change some parsed values. This is required if we ever want to override | ||
// a port number etc (as browsers remove port 443 and 80 from the urls). | ||
// a port number etc. (as browsers remove port 443 and 80 from the URL's). | ||
// | ||
for (key in a) { | ||
if (a[key] && ('string' === typeof a[key] || +a[key])) { | ||
if ('string' === typeof a[key] || 'number' === typeof a[key]) { | ||
data[key] = a[key]; | ||
@@ -383,2 +420,15 @@ } | ||
// | ||
// If we don't obtain a port number (e.g. when using zombie) then try | ||
// and guess at a value from the 'href' value | ||
// | ||
if (!data.port) { | ||
if (!data.href) data.href = ''; | ||
if ((data.href.match(/\:/g) || []).length > 1) { | ||
data.port = data.href.split(':')[2].split('/')[0]; | ||
} else { | ||
data.port = ('https' === data.href.substr(0, 5)) ? 443 : 80; | ||
} | ||
} | ||
// | ||
// Browsers do not parse authorization information, so we need to extract | ||
@@ -429,3 +479,6 @@ // that from the URL. | ||
try { | ||
if (Primus.prototype.NETWORK_EVENTS = 'onLine' in navigator && (window.addEventListener || document.body.attachEvent)) { | ||
if ( | ||
Primus.prototype.NETWORK_EVENTS = 'onLine' in navigator | ||
&& (window.addEventListener || document.body.attachEvent) | ||
) { | ||
if (!navigator.onLine) { | ||
@@ -468,2 +521,34 @@ Primus.prototype.online = false; | ||
/** | ||
* Checks if the given event is an emitted event by Primus. | ||
* | ||
* @param {String} evt The event name. | ||
* @returns {Boolean} | ||
* @api public | ||
*/ | ||
Primus.prototype.reserved = function reserved(evt) { | ||
return (/^(incoming|outgoing)::/).test(evt) | ||
|| evt in reserved.events; | ||
}; | ||
/** | ||
* The actual events that are used by the client. | ||
* | ||
* @type {Object} | ||
* @api public | ||
*/ | ||
Primus.prototype.reserved.events = { | ||
readyStateChange: 1, | ||
reconnecting: 1, | ||
reconnect: 1, | ||
offline: 1, | ||
timeout: 1, | ||
online: 1, | ||
error: 1, | ||
close: 1, | ||
open: 1, | ||
data: 1, | ||
end: 1 | ||
}; | ||
/** | ||
* Initialise the Primus and setup all parsers and internal listeners. | ||
@@ -475,6 +560,14 @@ * | ||
Primus.prototype.initialise = function initalise(options) { | ||
var primus = this; | ||
var primus = this | ||
, start; | ||
primus.on('outgoing::open', function opening() { | ||
var readyState = primus.readyState; | ||
primus.readyState = Primus.OPENING; | ||
if (readyState !== Primus.OPENING) { | ||
primus.emit('readyStateChange'); | ||
} | ||
start = +new Date(); | ||
}); | ||
@@ -485,3 +578,9 @@ | ||
var readyState = primus.readyState; | ||
primus.readyState = Primus.OPEN; | ||
if (readyState !== Primus.OPEN) { | ||
primus.emit('readyStateChange'); | ||
} | ||
primus.emit('open'); | ||
@@ -497,2 +596,4 @@ primus.clearTimeout('ping', 'pong').heartbeat(); | ||
} | ||
primus.latency = +new Date() - start; | ||
}); | ||
@@ -503,2 +604,4 @@ | ||
primus.clearTimeout('pong').heartbeat(); | ||
primus.latency = (+new Date()) - time; | ||
}); | ||
@@ -537,14 +640,6 @@ | ||
// | ||
// The server is closing the connection, forcefully disconnect so we don't | ||
// reconnect again. | ||
// Handle all "primus::" prefixed protocol messages. | ||
// | ||
if ('primus::server::close' === data) return primus.end(); | ||
if (primus.protocol(data)) return; | ||
// | ||
// We received a pong message from the server, return the id. | ||
// | ||
if ('string' === typeof data && data.indexOf('primus::pong::') === 0) { | ||
return primus.emit('incoming::pong', data.slice(14)); | ||
} | ||
for (var i = 0, length = primus.transformers.incoming.length; i < length; i++) { | ||
@@ -585,2 +680,6 @@ var packet = { data: data }; | ||
primus.readyState = Primus.CLOSED; | ||
if (readyState !== Primus.CLOSED) { | ||
primus.emit('readyStateChange'); | ||
} | ||
if (primus.timers.connect) primus.end(); | ||
@@ -643,5 +742,7 @@ if (readyState !== Primus.OPEN) return; | ||
function offline() { | ||
primus.online = false; | ||
primus.emit('offline'); | ||
primus.end(); | ||
if (!primus.online) return; // Already or still offline, bailout. | ||
primus.online = false; | ||
primus.emit('offline'); | ||
primus.end(); | ||
} | ||
@@ -655,2 +756,4 @@ | ||
function online() { | ||
if (primus.online) return; // Already or still online, bailout | ||
primus.online = true; | ||
@@ -674,2 +777,61 @@ primus.emit('online'); | ||
/** | ||
* Really dead simple protocol parser. We simply assume that every message that | ||
* is prefixed with `primus::` could be used as some sort of protocol definition | ||
* for Primus. | ||
* | ||
* @param {String} msg The data. | ||
* @returns {Boolean} Is a protocol message. | ||
* @api private | ||
*/ | ||
Primus.prototype.protocol = function protocol(msg) { | ||
if ( | ||
'string' !== typeof msg | ||
|| msg.indexOf('primus::') !== 0 | ||
) return false; | ||
var last = msg.indexOf(':', 8) | ||
, value = msg.slice(last + 2); | ||
switch (msg.slice(8, last)) { | ||
case 'pong': | ||
this.emit('incoming::pong', value); | ||
break; | ||
case 'server': | ||
// | ||
// The server is closing the connection, forcefully disconnect so we don't | ||
// reconnect again. | ||
// | ||
if ('close' === value) this.end(); | ||
break; | ||
case 'id': | ||
this.emit('incoming::id', value); | ||
break; | ||
// | ||
// Unknown protocol, somebody is probably sending `primus::` prefixed | ||
// messages. | ||
// | ||
default: | ||
return false; | ||
} | ||
return true; | ||
}; | ||
/** | ||
* Retrieve the current id from the server. | ||
* | ||
* @param {Function} fn Callback function. | ||
* @api public | ||
*/ | ||
Primus.prototype.id = function id(fn) { | ||
if (this.socket && this.socket.id) return fn(this.socket.id); | ||
this.write('primus::id::'); | ||
return this.once('incoming::id', fn); | ||
}; | ||
/** | ||
* Establish a connection with the server. When this function is called we | ||
@@ -762,3 +924,3 @@ * assume that we don't have any open connections. If you do call it when you | ||
// | ||
if (primus.online) return; | ||
if (!primus.online) return; | ||
@@ -950,3 +1112,8 @@ primus.online = false; | ||
this.writable = false; | ||
var readyState = this.readyState; | ||
this.readyState = Primus.CLOSED; | ||
if (readyState !== Primus.CLOSED) { | ||
this.emit('readyStateChange'); | ||
} | ||
@@ -1001,3 +1168,3 @@ for (var timeout in this.timers) { | ||
* @returns {Object} Parsed connection. | ||
* @api public | ||
* @api private | ||
*/ | ||
@@ -1011,3 +1178,3 @@ Primus.prototype.parse = parse; | ||
* @returns {Object} Parsed query string. | ||
* @api public | ||
* @api private | ||
*/ | ||
@@ -1122,3 +1289,5 @@ Primus.prototype.querystring = function querystring(query) { | ||
if (!(type in this.transformers)) throw new Error('Invalid transformer type'); | ||
if (!(type in this.transformers)) { | ||
return this.critical(new Error('Invalid transformer type')); | ||
} | ||
@@ -1137,3 +1306,6 @@ this.transformers[type].push(fn); | ||
Primus.prototype.critical = function critical(err) { | ||
if (this.listeners('error').length) return this.emit('error', err); | ||
if (this.listeners('error').length) { | ||
this.emit('error', err); | ||
return this; | ||
} | ||
@@ -1140,0 +1312,0 @@ throw err; |
127
README.md
# Primus | ||
[![Build Status](https://travis-ci.org/primus/primus.png)](https://travis-ci.org/primus/primus) | ||
[![Build Status](https://travis-ci.org/primus/primus.png?branch=master)](https://travis-ci.org/primus/primus) | ||
[![NPM version](https://badge.fury.io/js/primus.png)](http://badge.fury.io/js/primus) | ||
@@ -18,3 +18,3 @@ | ||
2. Built-in reconnect, it just works. The reconnect is controlled by a | ||
randomized exponential backoff algorithm to reduce server stress. | ||
randomised exponential back-off algorithm to reduce server stress. | ||
3. Offline detection, Primus is smart enough to detect when users drop their | ||
@@ -62,2 +62,3 @@ internet connection (switching WIFI points/cell towers for example) and | ||
- [Events](#events) | ||
- [Heartbeats and latency](#heartbeats-and-latency) | ||
- [Supported real-time frameworks](#supported-real-time-frameworks) | ||
@@ -77,4 +78,5 @@ - [Engine.IO](#engineio) | ||
- [FAQ](#FAQ) | ||
- [Scaling](#scaling) | ||
- [Express](#express) | ||
- [Scaling](#what-is-the-best-way-to-scale-primus) | ||
- [Express](#how-do-i-use-primus-with-express-3) | ||
- [RequireJS](#is-requirejs-supported) | ||
- [Versioning](#versioning) | ||
@@ -111,10 +113,16 @@ - [History](#history) | ||
Name | Description | Default | ||
--------------------|-----------------------------------------|--------------- | ||
authorization | Authorization handler | `null` | ||
pathname | The URL namespace that Primus can own | `/primus` | ||
parser | Message encoder for all communication | `JSON` | ||
transformer | The tranformer we should use internally | `websockets` | ||
plugin | The plugins that should be applied | `{}` | ||
Name | Description | Default | ||
--------------------|-------------------------------------------|--------------- | ||
authorization | Authorization handler | `null` | ||
pathname | The URL namespace that Primus can own | `/primus` | ||
parser | Message encoder for all communication | `JSON` | ||
transformer | The tranformer we should use internally | `websockets` | ||
plugin | The plugins that should be applied | `{}` | ||
timeout | The heartbeat timeout | `35000` | ||
The heartbeat timeout is used to forcefully disconnect a spark if no data is | ||
received from the client within the specified amount of time. It is possible | ||
to completly disable the heartbeat timeout by setting the value of the `timeout` | ||
option to `false`. | ||
In addition to support different frameworks we've also made it possible to use | ||
@@ -161,3 +169,3 @@ custom encoding and decoding libraries. We're using `JSON` by default but you | ||
extra route to the supplied HTTP server, this will serve the library for you so | ||
you don't have to save it. Please note, that this route isn't optimized for | ||
you don't have to save it. Please note, that this route isn't optimised for | ||
serving static assets and should only be used during development. In your HTML | ||
@@ -525,3 +533,3 @@ page add: | ||
When the connection goes down unexpectedly a automatic reconnect process is | ||
started. It's using a randomized exponential backoff algorithm to prevent | ||
started. It's using a randomised exponential back-off algorithm to prevent | ||
clients from DDoSing your server when you reboot as they will all be re-connecting at | ||
@@ -604,3 +612,3 @@ different times. The reconnection can be configured using the `options` argument | ||
But there are always use cases where reconnection is not advice for your | ||
But there are always use cases where reconnection is not advised for your | ||
application. In these cases we've provided a way to completely disable the | ||
@@ -612,8 +620,6 @@ reconnection, this is done by setting the `strategy` to `false`: | ||
``` | ||
If you want to manually control the reconnection you can call `primus.end()` | ||
to close the connection and `primus.open()` to enstablish a new one. **Don't | ||
do manual reconnection if you haven't set the strategy to false**. | ||
If you want manually control the reconnecting you can should just call | ||
`primus.end()` to close the connection and `primus.open()` to start a | ||
connection. **Don't do manual reconnection if you haven't set the strategy to | ||
false**. | ||
[reconnect]: #reconnect | ||
@@ -826,7 +832,61 @@ [strategy]: #strategy | ||
`log` | **public** | server | Log messages. | ||
`readyStateChange` | **public** | client/spark | The readyState has changed. | ||
As a rule of thumb assume that every event that is prefixed with `incoming::` or | ||
`outgoing::` is reserved for internal use only and that emitting such events your | ||
self will most likely result in c̮̫̞͚͉̮̙͕̳̲͉̤̗̹̮̦̪̖̱h̛͍͙̖̟͕̹͕̙̦̣̲̠̪̯̳͖̝̩a̴̝̦͇̥̠̟͚̳̤̹̗̻̭͍͖͕͓̻o̥̹̮̙͔̗͍͚͓̗̦̹͈͙͕̘̮͖̝ș̗̲̤̗̮͈̙͈̹̼̣̹̖̱̤̼̺̤ ̻͙̗̥̠̱͇̱̝̟̺͍̺̼͆̅̓̓̇a̜̖͈͇͎͙̲̙̗͇̫̘̖̹͖͓͔̺̱n̹͓̮͇̯̜̤̗͍̯̰̫̫̖̰ͬ͌ͬͫd͚̪͚̭͚̥̰̤̟͎̝̲̯̭̹̭̙̼̤ ͖̞̙̹͈͚̥̦͚͉͖̼̬͓͚̳͉͙͎d̴͚̱̮̗͍̩̻̰̣̫͉͈̞̲͉̫̞͔ẻͩͦ̃͌̿̐ͪͩ̌̇͂̆̑͐ͣ ҉̲͉͔͎̤̼̘͇̮̥̻̜̹̥͚̲̻̖s̶̗̻̫̼̠̳̗̺̤̗̳͈̪̮̗̝͇͈t̙͇͕̺̱̼̤̗̰̬̣͌ͬͧ͊́ͧͩ͌r͌̐̓̃ͥ̄ͤ͑̈ͬ͆ͬ͂̇̿̅ ҉̙̼̳̭̙͍̻̱̠͈̮̺̣̝̱̙̺͉ư̳͎̻͔̯̪̝͕͚̣̜̼̞͇̠̘̠̪c̨̫͙͙̬̰̰̫̐͋͊͑̌̾̉͆t͚̗͕̝̤̗͕̲̮̝̼̺͙͚̟͓̣̥͍ĭ͙̘̩̖͇͎̆̍̿̾ͤ̔̉̈̂̾̈ͭo̬̠̝͈̺̙̮̬̗̪̤͕͇͕̰̮͖͉̬n̙̪̤̝̹͖͖̻̬̹͙̞̗͓̞̭̜̠̟ | ||
self will most likely result in c̮̫̞͚͉̮̙͕̳̲͉̤̗̹̮̦̪̖̱h̛͍͙̖̟͕̹͕̙̦̣̲̠̪̯̳͖̝̩a̴̝̦͇̥̠̟͚̳̤̹̗̻̭͍͖͕͓̻o̥̹̮̙͔̗͍͚͓̗̦̹͈͙͕̘̮͖̝ș̗̲̤̗̮͈̙͈̹̼̣̹̖̱̤̼̺̤ ̻͙̗̥̠̱͇̱̝̟̺͍̺̼͆̅̓̓̇a̜̖͈͇͎͙̲̙̗͇̫̘̖̹͖͓͔̺̱n̹͓̮͇̯̜̤̗͍̯̰̫̫̖̰ͬ͌ͬͫd͚̪͚̭͚̥̰̤̟͎̝̲̯̭̹̭̙̼̤ ͖̞̙̹͈͚̥̦͚͉͖̼̬͓͚̳͉͙͎d̴͚̱̮̗͍̩̻̰̣̫͉͈̞̲͉̫̞͔ẻͩͦ̃͌̿̐ͪͩ̌̇͂̆̑͐ͣ ҉̲͉͔͎̤̼̘͇̮̥̻̜̹̥͚̲̻̖s̶̗̻̫̼̠̳̗̺̤̗̳͈̪̮̗̝͇͈t̙͇͕̺̱̼̤̗̰̬̣͌ͬͧ͊́ͧͩ͌r͌̐̓̃ͥ̄ͤ͑̈ͬ͆ͬ͂̇̿̅ ҉̙̼̳̭̙͍̻̱̠͈̮̺̣̝̱̙̺͉ư̳͎̻͔̯̪̝͕͚̣̜̼̞͇̠̘̠̪c̨̫͙͙̬̰̰̫̐͋͊͑̌̾̉͆t͚̗͕̝̤̗͕̲̮̝̼̺͙͚̟͓̣̥͍ĭ͙̘̩̖͇͎̆̍̿̾ͤ̔̉̈̂̾̈ͭo̬̠̝͈̺̙̮̬̗̪̤͕͇͕̰̮͖͉̬n̙̪̤̝̹͖͖̻̬̹͙̞̗͓̞̭̜̠̟. | ||
To make it easier for developers to emit events on primus it self we've added an | ||
small helper function that checks if the event you want to emit is reserved for | ||
Primus only. This would be all `incoming::` and `outgoing::` prefixed events and | ||
the events listed above. This method is called `<class>.reserved()` and it's | ||
implemented on the `Spark`: | ||
```js | ||
primus.on('connection', function connection(spark) { | ||
spark.on('data', function (data) { | ||
// | ||
// Just imagine that we receive an array of arguments from the client which | ||
// first argument is the name of the event that we need to emit and the | ||
// second argument are the arguments for function. | ||
// | ||
if (spark.reserved(data.args[0])) return; | ||
spark.emit.apply(spark, data.args[0]); | ||
}); | ||
}); | ||
``` | ||
But also the client: | ||
```js | ||
var primus = new Primus('http://example.bar'); | ||
primus.on('data', function (data) { | ||
if (primus.reserved(data.args[0])) return; | ||
primus.emit.apply(primus, data.args); | ||
}); | ||
``` | ||
And of course the `Primus` instance as well. | ||
### Heartbeats and latency | ||
Heartbeats are used in Primus to figure out if we still have an active, working | ||
and reliable connection with the server. These heartbeats are send from the | ||
**client** to the server. | ||
the heartbeats will only be send when there is an idle connection, so there is | ||
very little to no overhead at all. The main reason for this is that we already | ||
know that the connection is alive when we receive data from the server. | ||
The heartbeat package that we send over the connection is | ||
`primus::ping::<timestamp>`. The server will echo back the exact same package. | ||
This allows Primus to also calculate the latency between messages by simply | ||
getting the `<timestamp>` from echo and comparing it with the local time. This | ||
heartbeat is then stored in a `primus.latency` properly. The initial value of | ||
the `primus.latency` is to the time it took to send an `open` package and to | ||
actually receive a confirmation that the connection has been opened. | ||
### Supported Real-time Frameworks | ||
@@ -1040,3 +1100,3 @@ | ||
1. A reference to the initialized Primus server. | ||
1. A reference to the initialised Primus server. | ||
2. The options that were passed in the `new Primus(server, { options })` | ||
@@ -1047,3 +1107,3 @@ constructor. So the plugin can be configured through the same interface. | ||
1. A reference to the initialized Primus client. | ||
1. A reference to the initialised Primus client. | ||
2. The options that were passed in the `new Primus(url, { options })` | ||
@@ -1263,3 +1323,18 @@ constructor. So the plugin can be configured through the same interface. | ||
<dl> | ||
<dt><a href="https://github.com/zeMirco/primus-express-session">primus-express-session</a></dt> | ||
<dd> | ||
Share a user session between Express and Primus. | ||
</dd> | ||
<dd> | ||
<a href="https://travis-ci.org/zeMirco/primus-express-session"> | ||
<img src="https://travis-ci.org/zeMirco/primus-express-session.png?branch=master" alt="Build Status" /> | ||
</a> | ||
<a href="http://badge.fury.io/js/primus-express-session"> | ||
<img src="https://badge.fury.io/js/primus-express-session.png" alt="NPM version" /> | ||
</a> | ||
</dd> | ||
</dl> | ||
In addition to these community provided plugins, the Primus project also | ||
@@ -1324,3 +1399,3 @@ provides the following plugins: | ||
#### Scaling | ||
#### What is the best way to scale Primus | ||
@@ -1336,3 +1411,3 @@ Scaling Primus is as simple as sticking it behind a load balancer that supports | ||
#### Express | ||
#### How do I use Primus with Express 3 | ||
@@ -1360,3 +1435,3 @@ Express 3's `express()` instance isn't a valid HTTP server. In order to make it | ||
#### Require.js | ||
#### Is require.js supported | ||
@@ -1411,3 +1486,3 @@ Require.js is supported to a certain degree. The `primus.js` core file should be | ||
There isn't a steady or monthly release cycle. I usually release a new | ||
There isn't a steady or monthly release cycle. We usually release a new | ||
version when: | ||
@@ -1414,0 +1489,0 @@ |
222
spark.js
@@ -5,3 +5,4 @@ 'use strict'; | ||
, parse = require('querystring').parse | ||
, forwarded = require('./forwarded') | ||
, forwarded = require('forwarded-for') | ||
, predefine = require('predefine') | ||
, u2028 = /\u2028/g | ||
@@ -24,11 +25,14 @@ , u2029 = /\u2029/g; | ||
function Spark(primus, headers, address, query, id) { | ||
this.primus = primus; // References to the primus. | ||
this.headers = headers || {}; // The request headers. | ||
this.remote = address || {}; // The remote address location. | ||
this.query = query || {}; // The query string. | ||
this.id = id || this.uuid(); // Unique id for socket. | ||
this.readyState = Spark.OPEN; // The readyState of the connection. | ||
var readable = predefine(this, predefine.READABLE) | ||
, writable = predefine(this, predefine.WRITABLE) | ||
, spark = this; | ||
this.writable = true; // Silly stream compatibility. | ||
this.readable = true; // Silly stream compatibility. | ||
readable('primus', primus); // References to Primus. | ||
readable('headers', headers || {}); // The request headers. | ||
readable('remote', address || {}); // The remote address location. | ||
readable('id', id || this.uuid()); // Unique id for socket. | ||
readable('writable', true); // Silly stream compatibility. | ||
readable('readable', true); // Silly stream compatibility. | ||
writable('query', query || {}); // The query string. | ||
writable('timeout', null); // Heartbeat timeout. | ||
@@ -38,8 +42,14 @@ // | ||
// | ||
if ('string' === typeof this.query) this.query = parse(this.query); | ||
if ('string' === typeof this.query) { | ||
this.query = parse(this.query); | ||
} | ||
this.initialise(); | ||
this.heartbeat().__initialise.forEach(function execute(initialise) { | ||
initialise.call(spark); | ||
}); | ||
} | ||
Spark.prototype.__proto__ = require('stream').prototype; | ||
Spark.readable = predefine(Spark.prototype, predefine.READABLE); | ||
Spark.writable = predefine(Spark.prototype, predefine.WRITABLE); | ||
@@ -49,21 +59,106 @@ // | ||
// | ||
Spark.OPEN = 1; | ||
Spark.CLOSED = 2; | ||
Spark.OPENING = 1; // Only here for primus.js readyState number compatibility. | ||
Spark.CLOSED = 2; // The connection is closed. | ||
Spark.OPEN = 3; // The connection is open. | ||
// | ||
// Make sure that we emit `readyState` change events when a new readyState is | ||
// checked. This way plugins can correctly act according to this. | ||
// | ||
Spark.readable('readyState', { | ||
get: function get() { | ||
return this.__readyState; | ||
}, | ||
set: function set(readyState) { | ||
if (this.__readyState === readyState) return readyState; | ||
this.__readyState = readyState; | ||
this.emit('readyStateChange'); | ||
return readyState; | ||
} | ||
}, true); | ||
Spark.writable('__readyState', Spark.OPEN); | ||
// | ||
// Lazy parse interface for IP address information. As nobody is always | ||
// interested in this, we're going to defer parsing until it's actually needed. | ||
// | ||
Object.defineProperty(Spark.prototype, 'address', { | ||
get: function address() { | ||
return forwarded(this.remote, this.headers, this.primus.whitelist); | ||
} | ||
Spark.readable('address', { get: function address() { | ||
return forwarded(this.remote, this.headers, this.primus.whitelist); | ||
}}, true); | ||
/** | ||
* Set a timer to forcibly disconnect the spark if no data is received from the | ||
* client within the given timeout. | ||
* | ||
* @api private | ||
*/ | ||
Spark.readable('heartbeat', function heartbeat() { | ||
clearTimeout(this.timeout); | ||
if ('number' !== typeof this.primus.timeout) return this; | ||
var spark = this; | ||
this.timeout = setTimeout(function timeout() { | ||
if (spark.readyState === Spark.CLOSED) return; | ||
spark.readyState = Spark.CLOSED; | ||
spark.emit('outgoing::end'); | ||
spark.emit('end'); | ||
}, this.primus.timeout); | ||
return this; | ||
}); | ||
/** | ||
* Checks if the given event is an emitted event by Primus. | ||
* | ||
* @param {String} evt The event name. | ||
* @returns {Boolean} | ||
* @api public | ||
*/ | ||
Spark.readable('reserved', function reserved(evt) { | ||
return (/^(incoming|outgoing)::/).test(evt) | ||
|| evt in reserved.events; | ||
}); | ||
/** | ||
* The actual events that are used by the Spark. | ||
* | ||
* @type {Object} | ||
* @api public | ||
*/ | ||
Spark.prototype.reserved.events = { | ||
readyStateChange: 1, | ||
error: 1, | ||
data: 1, | ||
end: 1 | ||
}; | ||
/** | ||
* Allows for adding initialise listeners without people overriding our default | ||
* initializer. If they are feeling adventures and really want want to hack it | ||
* up, they can remove it from the __initialise array. | ||
* | ||
* @returns {Function} The last added initialise hook. | ||
* @api public | ||
*/ | ||
Spark.readable('initialise', { | ||
get: function get() { | ||
return this.__initialise[this.__initialise.length - 1]; | ||
}, | ||
set: function set(initialise) { | ||
if ('function' === typeof initialise) this.__initialise.push(initialise); | ||
} | ||
}, true); | ||
/** | ||
* Attach hooks and automatically announce a new connection. | ||
* | ||
* @type {Array} | ||
* @api private | ||
*/ | ||
Spark.prototype.initialise = function initialise() { | ||
Spark.readable('__initialise', [function initialise() { | ||
var primus = this.primus | ||
@@ -73,5 +168,17 @@ , spark = this; | ||
// | ||
// Prevent double initialization of the spark. If we already have an | ||
// `incoming::data` handler we assume that all other cases are handled as well. | ||
// | ||
if (this.listeners('incoming::data').length) return; | ||
// | ||
// We've received new data from our client, decode and emit it. | ||
// | ||
spark.on('incoming::data', function message(raw) { | ||
// | ||
// New data has arrived so we're certain that the connection is still alive, | ||
// so it's save to restart the heartbeat sequence. | ||
// | ||
spark.heartbeat(); | ||
primus.decoder(raw, function decoding(err, data) { | ||
@@ -85,7 +192,5 @@ // | ||
// | ||
// Handle client-side heartbeats by answering them as fast as possible. | ||
// Handle "primus::" prefixed protocol messages. | ||
// | ||
if ('string' === typeof data && data.indexOf('primus::ping::') === 0) { | ||
return spark.write('primus::pong::'+ data.slice(14)); | ||
} | ||
if (spark.protocol(data)) return; | ||
@@ -115,2 +220,4 @@ for (var i = 0, length = primus.transformers.incoming.length; i < length; i++) { | ||
spark.on('incoming::end', function disconnect() { | ||
if (spark.readyState === Spark.CLOSED) return; | ||
spark.readyState = Spark.CLOSED; | ||
@@ -138,3 +245,4 @@ spark.emit('end'); | ||
// | ||
spark.on('end', function () { | ||
spark.on('end', function end() { | ||
clearTimeout(spark.timeout); | ||
spark.removeAllListeners(); | ||
@@ -150,15 +258,53 @@ primus.emit('disconnection', spark); | ||
}); | ||
}; | ||
}]); | ||
/** | ||
* Generate a unique uuid. | ||
* Generate a unique UUID. | ||
* | ||
* @returns {String} uuid. | ||
* @returns {String} UUID. | ||
* @api private | ||
*/ | ||
Spark.prototype.uuid = function uuid() { | ||
Spark.readable('uuid', function uuid() { | ||
return Date.now() +'$'+ this.primus.sparks++; | ||
}; | ||
}); | ||
/** | ||
* Really dead simple protocol parser. We simply assume that every message that | ||
* is prefixed with `primus::` could be used as some sort of protocol definition | ||
* for Primus. | ||
* | ||
* @param {String} msg The data. | ||
* @returns {Boolean} Is a protocol message. | ||
* @api private | ||
*/ | ||
Spark.readable('protocol', function protocol(msg) { | ||
if ( | ||
'string' !== typeof msg | ||
|| msg.indexOf('primus::') !== 0 | ||
) return false; | ||
var last = msg.indexOf(':', 8) | ||
, value = msg.slice(last + 2); | ||
switch (msg.slice(8, last)) { | ||
case 'ping': | ||
this._write('primus::pong::'+ value); | ||
break; | ||
case 'id': | ||
this._write('primus::id::'+ this.id); | ||
break; | ||
// | ||
// Unknown protocol, somebody is probably sending `primus::` prefixed | ||
// messages. | ||
// | ||
default: | ||
return false; | ||
} | ||
return true; | ||
}); | ||
/** | ||
* Simple emit wrapper that returns a function that emits an event once it's | ||
@@ -172,3 +318,3 @@ * called. This makes it easier for transports to emit specific events. The | ||
*/ | ||
Spark.prototype.emits = function emits(event, parser) { | ||
Spark.readable('emits', function emits(event, parser) { | ||
var spark = this; | ||
@@ -181,3 +327,3 @@ | ||
}; | ||
}; | ||
}); | ||
@@ -191,3 +337,3 @@ /** | ||
*/ | ||
Spark.prototype.write = function write(data) { | ||
Spark.readable('write', function write(data) { | ||
var primus = this.primus | ||
@@ -218,3 +364,3 @@ , packet; | ||
return true; | ||
}; | ||
}); | ||
@@ -227,3 +373,3 @@ /** | ||
*/ | ||
Spark.prototype._write = function _write(data) { | ||
Spark.readable('_write', function _write(data) { | ||
var primus = this.primus | ||
@@ -262,3 +408,3 @@ , spark = this; | ||
}); | ||
}; | ||
}); | ||
@@ -271,3 +417,5 @@ /** | ||
*/ | ||
Spark.prototype.end = function end(data) { | ||
Spark.readable('end', function end(data) { | ||
if (Spark.CLOSED === this.readyState) return this; | ||
var spark = this; | ||
@@ -287,4 +435,6 @@ | ||
}); | ||
}; | ||
return this; | ||
}); | ||
// | ||
@@ -291,0 +441,0 @@ // Expose the module. |
'use strict'; | ||
var querystring = require('querystring').parse | ||
, EventEmitter = require('eventemitter3') | ||
, url = require('url').parse; | ||
@@ -15,3 +16,3 @@ | ||
* @constructor | ||
* @param {Primus} primus Reference to the Primus. | ||
* @param {Primus} primus Reference to the Primus instance. | ||
* @api public | ||
@@ -27,6 +28,7 @@ */ | ||
EventEmitter.call(this); | ||
this.initialise(); | ||
} | ||
Transformer.prototype.__proto__ = require('events').EventEmitter.prototype; | ||
Transformer.prototype.__proto__ = EventEmitter.prototype; | ||
@@ -68,5 +70,10 @@ // | ||
server.listeners('request').map(this.on.bind(this, 'previous::request')); | ||
server.listeners('upgrade').map(this.on.bind(this, 'previous::upgrade')); | ||
server.listeners('request').forEach(function each(fn) { | ||
transformer.on('previous::request', fn); | ||
}); | ||
server.listeners('upgrade').forEach(function each(fn) { | ||
transformer.on('previous::upgrade', fn); | ||
}); | ||
// | ||
@@ -226,3 +233,3 @@ // Remove the old listeners as we want to be the first request handler for all | ||
// | ||
Transformer.extend = require('extendable'); | ||
Transformer.extend = require('predefine').extend; | ||
@@ -229,0 +236,0 @@ // |
'use strict'; | ||
var http = require('http'); | ||
/** | ||
@@ -47,8 +49,11 @@ * Minimum viable Browserchannel server for Node.js that works through the primus | ||
// | ||
this.on('request', function request(req, res, next) { | ||
this.on('request', function request(req, res) { | ||
// | ||
// The browser.channel returns a middleware layer. | ||
// | ||
this.service(req, res, next); | ||
this.service(req, res, function next() { | ||
res.writeHead(404, {'content-type': 'text/plain'}); | ||
res.end(http.STATUS_CODES[404]); | ||
}); | ||
}); | ||
}; |
@@ -45,2 +45,10 @@ 'use strict'; | ||
// | ||
// Nuke a growing memory leak as engine.io pushes instances in to an exposed | ||
// `sockets` array. | ||
// | ||
if (factory.sockets && factory.sockets.length) { | ||
factory.sockets.length = 0; | ||
} | ||
// | ||
// Setup the Event handlers. | ||
@@ -47,0 +55,0 @@ // |
@@ -61,8 +61,8 @@ !function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.eio=e():"undefined"!=typeof global?global.eio=e():"undefined"!=typeof self&&(self.eio=e())}(function(){var define,module,exports; | ||
var util = require('./util') | ||
, transports = require('./transports') | ||
, Emitter = require('./emitter') | ||
, debug = require('debug')('engine.io-client:socket') | ||
, index = require('indexof') | ||
, parser = require('engine.io-parser'); | ||
var util = require('./util'); | ||
var transports = require('./transports'); | ||
var Emitter = require('./emitter'); | ||
var debug = require('debug')('engine.io-client:socket'); | ||
var index = require('indexof'); | ||
var parser = require('engine.io-parser'); | ||
@@ -87,3 +87,3 @@ /** | ||
function noop () {}; | ||
function noop(){} | ||
@@ -103,3 +103,3 @@ /** | ||
if ('object' == typeof uri) { | ||
if (uri && 'object' == typeof uri) { | ||
opts = uri; | ||
@@ -138,3 +138,3 @@ uri = null; | ||
this.timestampParam = opts.timestampParam || 't'; | ||
this.timestampRequests = !!opts.timestampRequests; | ||
this.timestampRequests = opts.timestampRequests; | ||
this.flashPath = opts.flashPath || ''; | ||
@@ -147,7 +147,4 @@ this.transports = opts.transports || ['polling', 'websocket', 'flashsocket']; | ||
this.open(); | ||
} | ||
Socket.sockets.push(this); | ||
Socket.sockets.evs.emit('add', this); | ||
}; | ||
/** | ||
@@ -168,9 +165,2 @@ * Mix in `Emitter`. | ||
/** | ||
* Static EventEmitter. | ||
*/ | ||
Socket.sockets = []; | ||
Socket.sockets.evs = new Emitter; | ||
/** | ||
* Expose deps for legacy compatibility | ||
@@ -242,4 +232,5 @@ * and standalone browser access. | ||
Socket.prototype.open = function () { | ||
var transport = this.transports[0]; | ||
this.readyState = 'opening'; | ||
var transport = this.createTransport(this.transports[0]); | ||
var transport = this.createTransport(transport); | ||
transport.open(); | ||
@@ -255,7 +246,8 @@ this.setTransport(transport); | ||
Socket.prototype.setTransport = function (transport) { | ||
Socket.prototype.setTransport = function(transport){ | ||
debug('setting transport %s', transport.name); | ||
var self = this; | ||
if (this.transport) { | ||
debug('clearing existing transport'); | ||
debug('clearing existing transport %s', this.transport.name); | ||
this.transport.removeAllListeners(); | ||
@@ -269,14 +261,14 @@ } | ||
transport | ||
.on('drain', function () { | ||
self.onDrain(); | ||
}) | ||
.on('packet', function (packet) { | ||
self.onPacket(packet); | ||
}) | ||
.on('error', function (e) { | ||
self.onError(e); | ||
}) | ||
.on('close', function () { | ||
self.onClose('transport close'); | ||
}); | ||
.on('drain', function(){ | ||
self.onDrain(); | ||
}) | ||
.on('packet', function(packet){ | ||
self.onPacket(packet); | ||
}) | ||
.on('error', function(e){ | ||
self.onError(e); | ||
}) | ||
.on('close', function(){ | ||
self.onClose('transport close'); | ||
}); | ||
}; | ||
@@ -328,3 +320,3 @@ | ||
err.transport = transport.name; | ||
self.emit('error', err); | ||
self.emit('upgradeError', err); | ||
} | ||
@@ -349,4 +341,4 @@ }); | ||
self.emit('error', error); | ||
}; | ||
self.emit('upgradeError', error); | ||
} | ||
@@ -675,5 +667,5 @@ transport.open(); | ||
var util = require('./util') | ||
, parser = require('engine.io-parser') | ||
, Emitter = require('./emitter'); | ||
var util = require('./util'); | ||
var parser = require('engine.io-parser'); | ||
var Emitter = require('./emitter'); | ||
@@ -703,3 +695,3 @@ /** | ||
this.agent = opts.agent || false; | ||
}; | ||
} | ||
@@ -816,2 +808,3 @@ /** | ||
},{"./emitter":2,"./util":12,"engine.io-parser":16}],6:[function(require,module,exports){ | ||
/** | ||
@@ -821,5 +814,5 @@ * Module dependencies. | ||
var WS = require('./websocket') | ||
, util = require('../util') | ||
, debug = require('debug')('engine.io-client:flashsocket'); | ||
var WS = require('./websocket'); | ||
var util = require('../util'); | ||
var debug = require('debug')('engine.io-client:flashsocket'); | ||
@@ -850,7 +843,7 @@ /** | ||
function FlashWS (options) { | ||
function FlashWS(options){ | ||
WS.call(this, options); | ||
this.flashPath = options.flashPath; | ||
this.policyPort = options.policyPort; | ||
}; | ||
} | ||
@@ -877,3 +870,3 @@ /** | ||
FlashWS.prototype.doOpen = function () { | ||
FlashWS.prototype.doOpen = function(){ | ||
if (!this.check()) { | ||
@@ -885,3 +878,3 @@ // let the probe timeout | ||
// instrument websocketjs logging | ||
function log (type) { | ||
function log(type){ | ||
return function(){ | ||
@@ -891,10 +884,10 @@ var str = Array.prototype.join.call(arguments, ' '); | ||
}; | ||
}; | ||
} | ||
WEB_SOCKET_LOGGER = { log: log('debug'), error: log('error') }; | ||
WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true; | ||
WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; | ||
global.WEB_SOCKET_LOGGER = { log: log('debug'), error: log('error') }; | ||
global.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true; | ||
global.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; | ||
if ('undefined' == typeof WEB_SOCKET_SWF_LOCATION) { | ||
WEB_SOCKET_SWF_LOCATION = this.flashPath + 'WebSocketMainInsecure.swf'; | ||
if (!global.WEB_SOCKET_SWF_LOCATION) { | ||
global.WEB_SOCKET_SWF_LOCATION = this.flashPath + 'WebSocketMainInsecure.swf'; | ||
} | ||
@@ -905,3 +898,3 @@ | ||
if ('undefined' == typeof swfobject) { | ||
if (!global.swfobject) { | ||
deps.unshift(this.flashPath + 'swfobject.js'); | ||
@@ -912,4 +905,4 @@ } | ||
load(deps, function () { | ||
self.ready(function () { | ||
load(deps, function(){ | ||
self.ready(function(){ | ||
WebSocket.__addTask(function () { | ||
@@ -929,6 +922,6 @@ self.socket = new WebSocket(self.uri()); | ||
FlashWS.prototype.doClose = function () { | ||
FlashWS.prototype.doClose = function(){ | ||
if (!this.socket) return; | ||
var self = this; | ||
WebSocket.__addTask(function() { | ||
WebSocket.__addTask(function(){ | ||
WS.prototype.doClose.call(self); | ||
@@ -944,5 +937,5 @@ }); | ||
FlashWS.prototype.write = function() { | ||
FlashWS.prototype.write = function(){ | ||
var self = this, args = arguments; | ||
WebSocket.__addTask(function () { | ||
WebSocket.__addTask(function(){ | ||
WS.prototype.write.apply(self, args); | ||
@@ -958,9 +951,9 @@ }); | ||
FlashWS.prototype.ready = function (fn) { | ||
FlashWS.prototype.ready = function(fn){ | ||
if (typeof WebSocket == 'undefined' || | ||
!('__initialize' in WebSocket) || !swfobject) { | ||
!('__initialize' in WebSocket) || !global.swfobject) { | ||
return; | ||
} | ||
if (swfobject.getFlashPlayerVersion().major < 10) { | ||
if (global.swfobject.getFlashPlayerVersion().major < 10) { | ||
return; | ||
@@ -970,7 +963,8 @@ } | ||
function init () { | ||
// Only start downloading the swf file when the checked that this browser | ||
// actually supports it | ||
// only start downloading the swf file when | ||
// we checked that this browser actually supports it | ||
if (!FlashWS.loaded) { | ||
if (843 != self.policyPort) { | ||
WebSocket.loadFlashPolicyFile('xmlsocket://' + self.hostname + ':' + self.policyPort); | ||
var policy = 'xmlsocket://' + self.hostname + ':' + self.policyPort; | ||
WebSocket.loadFlashPolicyFile(policy); | ||
} | ||
@@ -1000,3 +994,3 @@ | ||
FlashWS.prototype.check = function () { | ||
FlashWS.prototype.check = function(){ | ||
if ('undefined' == typeof window) { | ||
@@ -1046,3 +1040,3 @@ return false; | ||
function create (path, fn) { | ||
function create(path, fn){ | ||
if (scripts[path]) return fn(); | ||
@@ -1054,3 +1048,3 @@ | ||
debug('loading "%s"', path); | ||
el.onload = el.onreadystatechange = function () { | ||
el.onload = el.onreadystatechange = function(){ | ||
if (loaded || scripts[path]) return; | ||
@@ -1072,3 +1066,3 @@ var rs = el.readyState; | ||
head.insertBefore(el, head.firstChild); | ||
}; | ||
} | ||
@@ -1082,4 +1076,4 @@ /** | ||
function load (arr, fn) { | ||
function process (i) { | ||
function load(arr, fn){ | ||
function process(i){ | ||
if (!arr[i]) return fn(); | ||
@@ -1089,6 +1083,6 @@ create(arr[i], function () { | ||
}); | ||
}; | ||
} | ||
process(0); | ||
}; | ||
} | ||
@@ -1101,7 +1095,7 @@ },{"../util":12,"./websocket":11,"debug":14,"global":19}],7:[function(require,module,exports){ | ||
var XHR = require('./polling-xhr') | ||
var XMLHttpRequest = require('xmlhttprequest') | ||
, XHR = require('./polling-xhr') | ||
, JSONP = require('./polling-jsonp') | ||
, websocket = require('./websocket') | ||
, flashsocket = require('./flashsocket') | ||
, util = require('../util'); | ||
@@ -1145,3 +1139,4 @@ /** | ||
xhr = util.request(xd, opts); | ||
opts.xdomain = xd; | ||
xhr = new XMLHttpRequest(opts); | ||
@@ -1155,3 +1150,3 @@ if (xhr && !opts.forceJSONP) { | ||
},{"../util":12,"./flashsocket":6,"./polling-jsonp":8,"./polling-xhr":9,"./websocket":11,"global":19}],8:[function(require,module,exports){ | ||
},{"./flashsocket":6,"./polling-jsonp":8,"./polling-xhr":9,"./websocket":11,"global":19,"xmlhttprequest":13}],8:[function(require,module,exports){ | ||
@@ -1162,4 +1157,4 @@ /** | ||
var Polling = require('./polling') | ||
, util = require('../util'); | ||
var Polling = require('./polling'); | ||
var util = require('../util'); | ||
@@ -1231,3 +1226,3 @@ /** | ||
this.query.j = this.index; | ||
}; | ||
} | ||
@@ -1241,15 +1236,2 @@ /** | ||
/** | ||
* Opens the socket. | ||
* | ||
* @api private | ||
*/ | ||
JSONPPolling.prototype.doOpen = function () { | ||
var self = this; | ||
util.defer(function () { | ||
Polling.prototype.doOpen.call(self); | ||
}); | ||
}; | ||
/** | ||
* Closes the socket | ||
@@ -1291,5 +1273,5 @@ * | ||
script.src = this.uri(); | ||
script.onerror = function(e){ | ||
self.onError('jsonp poll error',e); | ||
} | ||
script.onerror = function(e){ | ||
self.onError('jsonp poll error',e); | ||
}; | ||
@@ -1347,3 +1329,3 @@ var insertAt = document.getElementsByTagName('script')[0]; | ||
fn(); | ||
}; | ||
} | ||
@@ -1373,3 +1355,3 @@ function initIframe () { | ||
self.iframe = iframe; | ||
}; | ||
} | ||
@@ -1401,6 +1383,7 @@ initIframe(); | ||
var Polling = require('./polling') | ||
, util = require('../util') | ||
, Emitter = require('../emitter') | ||
, debug = require('debug')('engine.io-client:polling-xhr'); | ||
var XMLHttpRequest = require('xmlhttprequest'); | ||
var Polling = require('./polling'); | ||
var util = require('../util'); | ||
var Emitter = require('../emitter'); | ||
var debug = require('debug')('engine.io-client:polling-xhr'); | ||
@@ -1424,3 +1407,3 @@ /** | ||
var xobject = global[['Active'].concat('Object').join('X')]; | ||
var hasAttachEvent = global.document && global.document.attachEvent; | ||
@@ -1455,3 +1438,3 @@ /** | ||
} | ||
}; | ||
} | ||
@@ -1465,15 +1448,2 @@ /** | ||
/** | ||
* Opens the socket | ||
* | ||
* @api private | ||
*/ | ||
XHR.prototype.doOpen = function(){ | ||
var self = this; | ||
util.defer(function(){ | ||
Polling.prototype.doOpen.call(self); | ||
}); | ||
}; | ||
/** | ||
* Creates a request. | ||
@@ -1560,39 +1530,44 @@ * | ||
Request.prototype.create = function(){ | ||
var xhr = this.xhr = util.request(this.xd, { agent: this.agent }); | ||
var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd }); | ||
var self = this; | ||
xhr.open(this.method, this.uri, this.async); | ||
try { | ||
debug('xhr open %s: %s', this.method, this.uri); | ||
xhr.open(this.method, this.uri, this.async); | ||
if ('POST' == this.method) { | ||
try { | ||
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); | ||
} catch (e) {} | ||
} | ||
if ('POST' == this.method) { | ||
try { | ||
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); | ||
} catch (e) {} | ||
} | ||
// ie6 check | ||
if ('withCredentials' in xhr) { | ||
xhr.withCredentials = true; | ||
} | ||
// ie6 check | ||
if ('withCredentials' in xhr) { | ||
xhr.withCredentials = true; | ||
} | ||
xhr.onreadystatechange = function(){ | ||
var data; | ||
xhr.onreadystatechange = function(){ | ||
var data; | ||
try { | ||
if (4 != xhr.readyState) return; | ||
if (200 == xhr.status || 1223 == xhr.status) { | ||
data = xhr.responseText; | ||
} else { | ||
self.onError(xhr.status); | ||
try { | ||
if (4 != xhr.readyState) return; | ||
if (200 == xhr.status || 1223 == xhr.status) { | ||
data = xhr.responseText; | ||
} else { | ||
// make sure the `error` event handler that's user-set | ||
// does not throw in the same tick and gets caught here | ||
setTimeout(function(){ | ||
self.onError(xhr.status); | ||
}, 0); | ||
} | ||
} catch (e) { | ||
self.onError(e); | ||
} | ||
} catch (e) { | ||
self.onError(e); | ||
} | ||
if (null != data) { | ||
self.onData(data); | ||
} | ||
}; | ||
if (null != data) { | ||
self.onData(data); | ||
} | ||
}; | ||
debug('sending xhr with url %s | data %s', this.uri, this.data); | ||
try { | ||
debug('xhr data %s', this.data); | ||
xhr.send(this.data); | ||
@@ -1609,3 +1584,3 @@ } catch (e) { | ||
if (xobject) { | ||
if (hasAttachEvent) { | ||
this.index = Request.requestsCount++; | ||
@@ -1666,3 +1641,3 @@ Request.requests[this.index] = this; | ||
if (xobject) { | ||
if (hasAttachEvent) { | ||
delete Request.requests[this.index]; | ||
@@ -1684,3 +1659,8 @@ } | ||
if (xobject) { | ||
/** | ||
* Cleanup is needed for old versions of IE | ||
* that leak memory unless we abort request before unload. | ||
*/ | ||
if (hasAttachEvent) { | ||
Request.requestsCount = 0; | ||
@@ -1698,3 +1678,3 @@ Request.requests = {}; | ||
},{"../emitter":2,"../util":12,"./polling":10,"debug":14,"global":19}],10:[function(require,module,exports){ | ||
},{"../emitter":2,"../util":12,"./polling":10,"debug":14,"global":19,"xmlhttprequest":13}],10:[function(require,module,exports){ | ||
/** | ||
@@ -1704,6 +1684,6 @@ * Module dependencies. | ||
var Transport = require('../transport') | ||
, util = require('../util') | ||
, parser = require('engine.io-parser') | ||
, debug = require('debug')('engine.io-client:polling'); | ||
var Transport = require('../transport'); | ||
var util = require('../util'); | ||
var parser = require('engine.io-parser'); | ||
var debug = require('debug')('engine.io-client:polling'); | ||
@@ -1908,5 +1888,10 @@ /** | ||
// cache busting is forced for IE / android / iOS6 ಠ_ಠ | ||
if (global.ActiveXObject || util.ua.chromeframe || util.ua.android || util.ua.ios6 || | ||
this.timestampRequests) { | ||
query[this.timestampParam] = +new Date; | ||
if ('ActiveXObject' in global | ||
|| util.ua.chromeframe | ||
|| util.ua.android | ||
|| util.ua.ios6 | ||
|| this.timestampRequests) { | ||
if (false !== this.timestampRequests) { | ||
query[this.timestampParam] = +new Date; | ||
} | ||
} | ||
@@ -1935,9 +1920,16 @@ | ||
var Transport = require('../transport') | ||
, WebSocket = require('ws') | ||
, parser = require('engine.io-parser') | ||
, util = require('../util') | ||
, debug = require('debug')('engine.io-client:websocket'); | ||
var Transport = require('../transport'); | ||
var parser = require('engine.io-parser'); | ||
var util = require('../util'); | ||
var debug = require('debug')('engine.io-client:websocket'); | ||
/** | ||
* `ws` exposes a WebSocket-compatible interface in | ||
* Node, or the `WebSocket` or `MozWebSocket` globals | ||
* in the browser. | ||
*/ | ||
var WebSocket = require('ws'); | ||
/** | ||
* Module exports. | ||
@@ -1963,3 +1955,3 @@ */ | ||
Transport.call(this, opts); | ||
}; | ||
} | ||
@@ -2007,3 +1999,3 @@ /** | ||
WS.prototype.addEventListeners = function() { | ||
WS.prototype.addEventListeners = function(){ | ||
var self = this; | ||
@@ -2211,19 +2203,2 @@ | ||
/** | ||
* Defers a function to ensure a spinner is not displayed by the browser. | ||
* | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
exports.defer = function (fn) { | ||
if (!exports.ua.webkit || 'undefined' != typeof importScripts) { | ||
return fn(); | ||
} | ||
exports.load(function () { | ||
setTimeout(fn, 100); | ||
}); | ||
}; | ||
/** | ||
* JSON parse. | ||
@@ -2270,10 +2245,2 @@ * | ||
/** | ||
* Whether the UA supports CORS for XHR. | ||
* | ||
* @api private | ||
*/ | ||
exports.ua.hasCORS = require('has-cors'); | ||
/** | ||
* Detect webkit. | ||
@@ -2318,33 +2285,2 @@ * | ||
/** | ||
* XHR request helper. | ||
* | ||
* @param {Boolean} whether we need xdomain | ||
* @param {Object} opts Optional "options" object | ||
* @api private | ||
*/ | ||
exports.request = function request (xdomain, opts) { | ||
opts = opts || {}; | ||
opts.xdomain = xdomain; | ||
try { | ||
var _XMLHttpRequest = require('xmlhttprequest'); | ||
return new _XMLHttpRequest(opts); | ||
} catch (e) {} | ||
// XMLHttpRequest can be disabled on IE | ||
try { | ||
if ('undefined' != typeof XMLHttpRequest && (!xdomain || exports.ua.hasCORS)) { | ||
return new XMLHttpRequest(); | ||
} | ||
} catch (e) { } | ||
if (!xdomain) { | ||
try { | ||
return new ActiveXObject('Microsoft.XMLHTTP'); | ||
} catch(e) { } | ||
} | ||
}; | ||
/** | ||
* Parses an URI | ||
@@ -2412,3 +2348,3 @@ * | ||
},{"global":19,"has-cors":20,"xmlhttprequest":13}],13:[function(require,module,exports){ | ||
},{"global":19}],13:[function(require,module,exports){ | ||
// browser shim for xmlhttprequest module | ||
@@ -2454,2 +2390,4 @@ var hasCORS = require('has-cors'); | ||
return function(fmt){ | ||
fmt = coerce(fmt); | ||
var curr = new Date; | ||
@@ -2557,5 +2495,16 @@ var ms = curr - (debug[name] || curr); | ||
/** | ||
* Coerce `val`. | ||
*/ | ||
function coerce(val) { | ||
if (val instanceof Error) return val.stack || val.message; | ||
return val; | ||
} | ||
// persist | ||
if (window.localStorage) debug.enable(localStorage.debug); | ||
try { | ||
if (window.localStorage) debug.enable(localStorage.debug); | ||
} catch(e){} | ||
@@ -2562,0 +2511,0 @@ },{}],15:[function(require,module,exports){ |
@@ -13,4 +13,7 @@ 'use strict'; | ||
, Spark = this.Spark | ||
, primus = this.primus; | ||
, primus = this.primus | ||
, prefix = primus.pathname; | ||
if (prefix.charAt(prefix.length - 1) !== '/') prefix += '(?:[^/]+)?'; | ||
this.service = sockjs.createServer(); | ||
@@ -50,3 +53,3 @@ | ||
var handle = this.service.listener({ | ||
prefix: primus.pathname, | ||
prefix: prefix, | ||
log: this.logger.plain | ||
@@ -53,0 +56,0 @@ }).getHandler(); |
'use strict'; | ||
var parse = require('url').parse; | ||
var http = require('http') | ||
, parse = require('url').parse; | ||
@@ -15,3 +16,2 @@ /** | ||
, logger = this.logger | ||
, primus = this.primus | ||
, Spark = this.Spark; | ||
@@ -58,2 +58,5 @@ | ||
}); | ||
}).on('request', function request(req, res) { | ||
res.writeHead(400, {'content-type': 'text/plain'}); | ||
res.end(http.STATUS_CODES[400]); | ||
}); | ||
@@ -60,0 +63,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
298298
7860
1483
4
36
+ Addedeventemitter3@0.1.x
+ Addedforwarded-for@0.0.x
+ Addedpredefine@0.0.x
+ Addedeventemitter3@0.1.6(transitive)
+ Addedforwarded-for@0.0.2(transitive)
+ Addedpredefine@0.0.6(transitive)
- Removedextendable@0.0.x