engine.io
Advanced tools
Comparing version
0.6.0 / 2013-05-31 | ||
================== | ||
* socket: clear timer after sending one noop packet (fixes #174) | ||
* clear all timers on socket close | ||
* sending error on transport creation upon a bad request | ||
* added test for client-side buffer cleanup | ||
* changed flushComplete to flush | ||
* ended support for node 0.6 | ||
0.5.0 / 2013-03-16 | ||
@@ -3,0 +13,0 @@ ================== |
@@ -55,3 +55,4 @@ | ||
UNKNOWN_SID: 1, | ||
BAD_HANDSHAKE_METHOD: 2 | ||
BAD_HANDSHAKE_METHOD: 2, | ||
BAD_REQUEST: 3 | ||
}; | ||
@@ -62,3 +63,4 @@ | ||
1: 'Session ID unknown', | ||
2: 'Bad handshake method' | ||
2: 'Bad handshake method', | ||
3: 'Bad request' | ||
}; | ||
@@ -163,7 +165,3 @@ | ||
if (code !== true) { | ||
res.writeHead(400, { 'Content-Type': 'application/json' }); | ||
res.end(JSON.stringify({ | ||
code: code, | ||
message: Server.errorMessages[code] | ||
})); | ||
sendErrorMessage(res, code); | ||
return this; | ||
@@ -183,2 +181,18 @@ } | ||
/** | ||
* Sends an Engine.IO Error Message | ||
* | ||
* @param {http.ServerResponse} response | ||
* @param {code} error code | ||
* @api private | ||
*/ | ||
function sendErrorMessage(res, code) { | ||
res.writeHead(400, { 'Content-Type': 'application/json' }); | ||
res.end(JSON.stringify({ | ||
code: code, | ||
message: Server.errorMessages[code] | ||
})); | ||
} | ||
/** | ||
* Handshakes a new client. | ||
@@ -196,3 +210,9 @@ * | ||
var transport = new transports[transport](req); | ||
try { | ||
var transport = new transports[transport](req); | ||
} | ||
catch (e) { | ||
sendErrorMessage(req.res, Server.errors.BAD_REQUEST); | ||
return; | ||
} | ||
var socket = new Socket(id, this, transport); | ||
@@ -199,0 +219,0 @@ var self = this; |
@@ -5,4 +5,4 @@ /** | ||
var EventEmitter = require('events').EventEmitter | ||
, debug = require('debug')('engine:socket') | ||
var EventEmitter = require('events').EventEmitter; | ||
var debug = require('debug')('engine:socket'); | ||
@@ -165,7 +165,8 @@ /** | ||
var self = this; | ||
// set transport upgrade timer | ||
var checkInterval; | ||
var upgradeTimeout = setTimeout(function () { | ||
self.upgradeTimeoutTimer = setTimeout(function () { | ||
debug('client did not complete upgrade - closing transport'); | ||
clearInterval(checkInterval); | ||
clearInterval(self.checkIntervalTimer); | ||
if ('open' == transport.readyState) { | ||
@@ -176,3 +177,2 @@ transport.close(); | ||
var self = this; | ||
function onPacket (packet) { | ||
@@ -183,10 +183,9 @@ if ('ping' == packet.type && 'probe' == packet.data) { | ||
// we force a polling cycle to ensure a fast upgrade | ||
function check () { | ||
self.checkIntervalTimer = setInterval(function(){ | ||
if ('polling' == self.transport.name && self.transport.writable) { | ||
debug('writing a noop packet to polling for fast upgrade'); | ||
self.transport.send([{ type: 'noop' }]); | ||
clearInterval(self.checkIntervalTimer); | ||
} | ||
} | ||
checkInterval = setInterval(check, 100); | ||
}, 100); | ||
} else if ('upgrade' == packet.type && self.readyState == 'open') { | ||
@@ -200,4 +199,4 @@ debug('got upgrade packet - upgrading'); | ||
self.flush(); | ||
clearInterval(checkInterval); | ||
clearTimeout(upgradeTimeout); | ||
clearInterval(self.checkIntervalTimer); | ||
clearTimeout(self.upgradeTimeoutTimer); | ||
transport.removeListener('packet', onPacket); | ||
@@ -234,2 +233,5 @@ } else { | ||
if ('closed' != this.readyState) { | ||
clearTimeout(this.pingTimeoutTimer); | ||
clearInterval(this.checkIntervalTimer); | ||
clearTimeout(this.upgradeTimeoutTimer); | ||
var self = this; | ||
@@ -257,4 +259,4 @@ // clean writeBuffer in next tick, so developers can still | ||
var self = this; | ||
//the message was sent successfully, execute the callback | ||
this.transport.on('drain', function() { | ||
//the message was sent successfully, execute the callback | ||
this.transport.on('drain', function() { | ||
if (self.sentCallbackFn.length > 0) { | ||
@@ -334,3 +336,3 @@ var seqFn = self.sentCallbackFn.splice(0,1)[0]; | ||
if (!this.transport.supportsFraming) { | ||
this.sentCallbackFn.push(this.packetsFn) | ||
this.sentCallbackFn.push(this.packetsFn); | ||
} else { | ||
@@ -373,2 +375,5 @@ this.sentCallbackFn.push.apply(this.sentCallbackFn, this.packetsFn); | ||
if ('open' == this.readyState) { | ||
clearTimeout(this.pingTimeoutTimer); | ||
clearInterval(this.checkIntervalTimer); | ||
clearTimeout(this.upgradeTimeoutTimer); | ||
this.readyState = 'closing'; | ||
@@ -375,0 +380,0 @@ var self = this; |
{ | ||
"name": "engine.io" | ||
, "version": "0.5.0" | ||
, "version": "0.6.0" | ||
, "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server" | ||
@@ -27,3 +27,3 @@ , "main": "./lib/engine.io" | ||
, "superagent": "*" | ||
, "engine.io-client": "0.5.0" | ||
, "engine.io-client": "git://github.com/LearnBoost/engine.io-client#68780141ef" | ||
, "s": "*" | ||
@@ -30,0 +30,0 @@ } |
# Engine.IO: the realtime engine | ||
[](http://travis-ci.org/LearnBoost/engine.io) | ||
[](http://badge.fury.io/js/engine.io) | ||
@@ -131,3 +132,3 @@ `Engine` is the implementation of transport-based cross-browser/cross-device | ||
- Captures `upgrade` requests for a `http.Server`. In other words, makes | ||
a regular http.Server websocket-compatible. | ||
a regular http.Server WebSocket-compatible. | ||
- **Parameters** | ||
@@ -159,3 +160,3 @@ - `http.Server`: server to attach to. | ||
**Important**: if you plan to use engine.io in a scalable way, please | ||
**Important**: if you plan to use Engine.IO in a scalable way, please | ||
keep in mind the properties below will only reflect the clients connected | ||
@@ -180,3 +181,3 @@ to a single process. | ||
to (`['polling', 'websocket', 'flashsocket']`) | ||
- `allowUpgrades` (`Boolean`): whether to allow tranport upgrades | ||
- `allowUpgrades` (`Boolean`): whether to allow transport upgrades | ||
(`true`) | ||
@@ -225,3 +226,3 @@ - `cookie` (`String|Boolean`): name of the HTTP cookie that | ||
- **Arguments** | ||
- `String`: unicode string | ||
- `String`: Unicode string | ||
- `error` | ||
@@ -238,3 +239,3 @@ - Fired when an error occurs. | ||
- `packet` | ||
- Called when a socket reseived a packet (`message`, `ping`) | ||
- Called when a socket received a packet (`message`, `ping`) | ||
- **Arguments** | ||
@@ -282,3 +283,3 @@ - `type`: packet type | ||
Engine.IO is powered by [debug](http://github.com/visionmedia/debug). | ||
In order to see all the debug output, run your app with the env variable | ||
In order to see all the debug output, run your app with the environment variable | ||
`DEBUG` including the desired scope. | ||
@@ -362,7 +363,7 @@ | ||
The main goal of `Engine` is ensuring the most reliable realtime communication. | ||
Unlike the previous socket.io core, it always establishes a long-polling | ||
Unlike the previous Socket.IO core, it always establishes a long-polling | ||
connection first, then tries to upgrade to better transports that are "tested" on | ||
the side. | ||
During the lifetime of the socket.io projects, we've found countless drawbacks | ||
During the lifetime of the Socket.IO projects, we've found countless drawbacks | ||
to relying on `HTML5 WebSocket` or `Flash Socket` as the first connection | ||
@@ -415,3 +416,3 @@ mechanisms. | ||
As a result of our research, we've found that at least 3 personal security | ||
applications block websocket traffic. | ||
applications block WebSocket traffic. | ||
@@ -422,3 +423,3 @@ 3. **Cloud application platforms**<br> | ||
inevitably using long polling, but the seamless installation experience of | ||
socket.io we strive for (_"require() it and it just works"_) disappears. | ||
Socket.IO we strive for (_"require() it and it just works"_) disappears. | ||
@@ -425,0 +426,0 @@ Some of these problems have solutions. In the case of proxies and personal programs, |
@@ -226,10 +226,24 @@ /*global eio,eioc,listen,request,expect*/ | ||
}); | ||
it('should disallow bad requests', function (done) { | ||
var engine = listen(function (port) { | ||
request.get('http://localhost:%d/engine.io/default/'.s(port)) | ||
.query({ transport: 'websocket' }) | ||
.end(function (res) { | ||
expect(res.status).to.be(400); | ||
expect(res.body.code).to.be(3); | ||
expect(res.body.message).to.be('Bad request'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('close', function () { | ||
it('should be able to access non-empty writeBuffer at closing', function(done) { | ||
var opts = {allowUpgrades: false, pingInterval: 10, pingTimeout: 10 }; | ||
it('should be able to access non-empty writeBuffer at closing (server)', function(done) { | ||
var opts = {allowUpgrades: false}; | ||
var engine = listen(opts, function (port) { | ||
var socket = new eioc.Socket('http://localhost:%d'.s(port)); | ||
socket.sendPacket = function (){}; | ||
engine.on('connection', function (conn) { | ||
@@ -249,2 +263,23 @@ conn.on('close', function (reason) { | ||
it('should be able to access non-empty writeBuffer at closing (client)', function(done) { | ||
var opts = {allowUpgrades: false}; | ||
var engine = listen(opts, function (port) { | ||
var socket = new eioc.Socket('http://localhost:%d'.s(port)); | ||
socket.on('open', function() { | ||
socket.on('close', function (reason) { | ||
expect(socket.writeBuffer.length).to.be(1); | ||
expect(socket.callbackBuffer.length).to.be(1); | ||
setTimeout(function() { | ||
expect(socket.writeBuffer.length).to.be(0); | ||
expect(socket.callbackBuffer.length).to.be(0); | ||
}, 10); | ||
done(); | ||
}); | ||
socket.writeBuffer.push({ type: 'message', data: 'foo'}); | ||
socket.callbackBuffer.push(function() {}); | ||
socket.onError(''); | ||
}); | ||
}); | ||
}); | ||
it('should trigger on server if the client does not pong', function (done) { | ||
@@ -402,2 +437,29 @@ var opts = { allowUpgrades: false, pingInterval: 5, pingTimeout: 5 }; | ||
it('should trigger when calling socket.close() in payload', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port)) | ||
engine.on('connection', function (conn) { | ||
conn.send(null, function () {socket.close();}); | ||
conn.send('this should not be handled'); | ||
conn.on('close', function (reason) { | ||
expect(reason).to.be('transport close'); | ||
done(); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.on('message', function (msg) { | ||
expect(msg).to.not.be('this should not be handled'); | ||
}); | ||
socket.on('close', function (reason) { | ||
expect(reason).to.be('forced close'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should abort upgrade if socket is closed (GH-35)', function (done) { | ||
@@ -809,3 +871,181 @@ var engine = listen({ allowUpgrades: true }, function (port) { | ||
describe('send', function() { | ||
describe('writeBuffer', function() { | ||
it('should not empty until `drain` event (polling)', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); | ||
var totalEvents = 2; | ||
socket.on('open', function() { | ||
socket.send('a'); | ||
socket.send('b'); | ||
// writeBuffer should be nonempty, with 'a' still in it | ||
expect(socket.writeBuffer.length).to.eql(2); | ||
}); | ||
socket.transport.on('drain', function() { | ||
expect(socket.writeBuffer.length).to.eql(--totalEvents); | ||
totalEvents || done(); | ||
}); | ||
}); | ||
}); | ||
it('should not empty until `drain` event (websocket)', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); | ||
var totalEvents = 2; | ||
socket.on('open', function() { | ||
socket.send('a'); | ||
socket.send('b'); | ||
// writeBuffer should be nonempty, with 'a' still in it | ||
expect(socket.writeBuffer.length).to.eql(2); | ||
}); | ||
socket.transport.on('drain', function() { | ||
expect(socket.writeBuffer.length).to.eql(--totalEvents); | ||
totalEvents || done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('callback', function() { | ||
it('should execute in order when message sent (client) (polling)', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); | ||
var i = 0; | ||
var j = 0; | ||
engine.on('connection', function(conn) { | ||
conn.on('message', function(msg) { | ||
conn.send(msg); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.on('message', function(msg) { | ||
// send another packet until we've sent 3 total | ||
if (++i < 3) { | ||
expect(i).to.eql(j); | ||
sendFn(); | ||
} else { | ||
done(); | ||
} | ||
}); | ||
function sendFn() { | ||
socket.send(j, (function(value) { | ||
j++; | ||
})(j)); | ||
} | ||
sendFn(); | ||
}); | ||
}); | ||
}); | ||
it('should execute in order when message sent (client) (websocket)', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); | ||
var i = 0; | ||
var j = 0; | ||
engine.on('connection', function(conn) { | ||
conn.on('message', function(msg) { | ||
conn.send(msg); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.on('message', function(msg) { | ||
// send another packet until we've sent 3 total | ||
if (++i < 3) { | ||
expect(i).to.eql(j); | ||
sendFn(); | ||
} else { | ||
done(); | ||
} | ||
}); | ||
function sendFn() { | ||
socket.send(j, (function(value) { | ||
j++; | ||
})(j)); | ||
} | ||
sendFn(); | ||
}); | ||
}); | ||
}); | ||
it('should execute in order with payloads (client) (polling)', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); | ||
var i = 0; | ||
var lastCbFired = 0; | ||
engine.on('connection', function(conn) { | ||
conn.on('message', function(msg) { | ||
conn.send(msg); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.on('message', function(msg) { | ||
expect(msg).to.eql(i + 1); | ||
i++; | ||
}); | ||
function cb(value) { | ||
expect(value).to.eql(lastCbFired + 1); | ||
lastCbFired = value; | ||
if (value == 3) { | ||
done(); | ||
} | ||
} | ||
// 2 and 3 will be in the same payload | ||
socket.once('flush', function() { | ||
socket.send(2, function() { cb(2); }); | ||
socket.send(3, function() { cb(3); }); | ||
}); | ||
socket.send(1, function() { cb(1); }); | ||
}); | ||
}); | ||
}); | ||
it('should execute in order with payloads (client) (websocket)', function (done) { | ||
var engine = listen({ allowUpgrades: false }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); | ||
var i = 0; | ||
var lastCbFired = 0; | ||
engine.on('connection', function(conn) { | ||
conn.on('message', function(msg) { | ||
conn.send(msg); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.on('message', function(msg) { | ||
expect(msg).to.eql(i + 1); | ||
i++; | ||
}); | ||
function cb(value) { | ||
expect(value).to.eql(lastCbFired + 1); | ||
lastCbFired = value; | ||
if (value == 3) { | ||
done(); | ||
} | ||
} | ||
// 2 and 3 will be in the same payload | ||
socket.once('flush', function() { | ||
socket.send(2, function() { cb(2); }); | ||
socket.send(3, function() { cb(3); }); | ||
}); | ||
socket.send(1, function() { cb(1); }); | ||
}); | ||
}); | ||
}); | ||
it('should execute when message sent (polling)', function (done) { | ||
@@ -1159,162 +1399,2 @@ var engine = listen({ allowUpgrades: false }, function (port) { | ||
describe('JSONP', function () { | ||
before(function () { | ||
///we have to override the browser's functionality for JSONP | ||
document = { | ||
body: { | ||
appendChild: function () {} | ||
, removeChild: function () {} | ||
} | ||
} | ||
document.createElement = function (name) { | ||
var self = this; | ||
if('script' == name) { | ||
var script = {}; | ||
script.__defineGetter__('parentNode', function () { | ||
return document.body; | ||
}); | ||
script.__defineSetter__('src', function (uri) { | ||
request.get(uri).end(function(res) { | ||
eval(res.text); | ||
}); | ||
}); | ||
return script; | ||
} | ||
else if ('form' == name) { | ||
var form = { | ||
style: {} | ||
, action: '' | ||
, parentNode: { removeChild: function () {} } | ||
, removeChild: function () {} | ||
, setAttribute: function () {} | ||
, appendChild: function (elem) { area: elem; } | ||
, submit: function () { | ||
request.post(this.action).type('form').send({ d: self.areaValue }).end(function (res) {}); | ||
} | ||
} | ||
return form; | ||
} | ||
else if ('textarea' == name) { | ||
var textarea = {}; | ||
//a hack to be able to access the area data when form is sent | ||
textarea.__defineSetter__('value', function (data) { | ||
self.areaValue = data; | ||
}); | ||
return textarea; | ||
} else { | ||
return {} | ||
} | ||
} | ||
document.getElementsByTagName = function (name) { | ||
return [{ | ||
parentNode: { | ||
insertBefore: function () {} | ||
} | ||
}] | ||
} | ||
}); | ||
after(function () { | ||
delete document.getElementsByTagName | ||
, document.createElement | ||
, document; | ||
}); | ||
describe('handshake', function () { | ||
it('should open with polling JSONP when requested', function (done) { | ||
var engine = listen( { allowUpgrades: false, transports: ['polling'] }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:' + port | ||
, { transports: ['polling'], forceJSONP: true, upgrade: false }); | ||
engine.on('connection', function (socket) { | ||
expect(socket.transport.name).to.be('polling'); | ||
expect(socket.transport.head).to.be('___eio[0]('); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('messages', function () { | ||
it('should arrive from client to server and back (pollingJSONP)', function (done) { | ||
var engine = listen( { allowUpgrades: false, transports: ['polling'] }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:' + port | ||
, { transports: ['polling'], forceJSONP: true, upgrade: false }); | ||
engine.on('connection', function (conn) { | ||
conn.on('message', function (msg) { | ||
conn.send('a'); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.send('a'); | ||
socket.on('message', function (msg) { | ||
expect(socket.transport.query.j).to.not.be(undefined); | ||
expect(msg).to.be('a'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('close', function () { | ||
it('should trigger when server closes a client', function (done) { | ||
var engine = listen( { allowUpgrades: false, transports: ['polling'] }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:' + port | ||
, { transports: ['polling'], forceJSONP: true, upgrade: false }) | ||
, total = 2; | ||
engine.on('connection', function (conn) { | ||
conn.on('close', function (reason) { | ||
expect(reason).to.be('forced close'); | ||
--total || done(); | ||
}); | ||
setTimeout(function () { | ||
conn.close(); | ||
}, 10); | ||
}); | ||
socket.on('open', function () { | ||
socket.on('close', function (reason) { | ||
expect(reason).to.be('transport close'); | ||
--total || done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should trigger when client closes', function (done) { | ||
var engine = listen( { allowUpgrades: false, transports: ['polling'] }, function (port) { | ||
var socket = new eioc.Socket('ws://localhost:' + port | ||
, { transports: ['polling'], forceJSONP: true, upgrade: false }) | ||
, total = 2; | ||
engine.on('connection', function (conn) { | ||
conn.on('close', function (reason) { | ||
expect(reason).to.be('transport close'); | ||
--total || done(); | ||
}); | ||
}); | ||
socket.on('open', function () { | ||
socket.send('a'); | ||
socket.on('close', function (reason) { | ||
expect(reason).to.be('forced close'); | ||
--total || done(); | ||
}); | ||
setTimeout(function () { | ||
socket.close(); | ||
}, 10); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
182766
5.04%30
3.45%5404
4.45%509
0.2%9
12.5%