Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 121 additions & 32 deletions lib/engine.io.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
var http = require('http')
, debug = require('debug')('engine:core')

/**
* Server listeners.
*
* @api private
*/

var serverListeners = [];

/**
* Protocol revision number.
*
Expand Down Expand Up @@ -96,59 +104,140 @@ exports.listen = function (port, options, fn) {
exports.attach = function (server, options) {
var engine = new exports.Server(options)
, options = options || {}
, path = (options.path || '/engine.io').replace(/\/$/, '')
, resource = options.resource || 'default'

// normalize path
path += '/' + resource + '/';
function serverListener() {
for (var i=0; i<serverListeners.length; i++) {
if (serverListeners[i][0] === server) {
return serverListeners[i][1];
}
}

var instanceListeners = {
close: [],
request: [],
upgrade: []
};

function check (req) {
return path == req.url.substr(0, path.length);
}
// cache and clean up listeners
var listeners = server.listeners('request')
, oldListeners = []

// cache and clean up listeners
var listeners = server.listeners('request')
, oldListeners = []
// copy the references onto a new array for node >=0.7
for (var i = 0, l = listeners.length; i < l; i++) {
oldListeners[i] = listeners[i];
}

server.removeAllListeners('request');

// copy the references onto a new array for node >=0.7
for (var i = 0, l = listeners.length; i < l; i++) {
oldListeners[i] = listeners[i];
server.on('close', function () {
for (var i = 0, l = instanceListeners.close.length; i < l; i++) {
instanceListeners.close[i]();
}
});

// add request handler
server.on('request', function (req, res) {
var fired = false;
for (var i = 0, l = instanceListeners.request.length; i < l; i++) {
if (instanceListeners.request[i](req, res)) {
fired = true;
break;
}
}
if (fired) return;
for (var i = 0, l = oldListeners.length; i < l; i++) {
oldListeners[i].call(server, req, res);
}
});

server.on('upgrade', function (req, socket, head) {
var fired = false;
for (var i = 0, l = instanceListeners.upgrade.length; i < l; i++) {
if (instanceListeners.upgrade[i](req, socket, head)) {
fired = true;
break;
}
}
if (fired) return;
// NOTE: `options.destroyUpgrade` must be equivalent for ALL `engine` instances using the same `server` instance!
if (false !== options.destroyUpgrade) {
socket.end();
}
});

if (~engine.transports.indexOf('flashsocket')
&& false !== options.policyFile) {
server.on('connection', function (socket) {
// NOTE: This only needs to be attached once to any `engine` instance as it does nothing
// specific to the instance.
engine.handleSocket(socket);
});
}

serverListeners.push([
server,
instanceListeners
]);

return instanceListeners;
}

server.removeAllListeners('request');
var listener = serverListener();

server.on('close', function () {
listener.close.push(function() {
engine.close();
});

var path = '/engine.io';
if (typeof options.path === 'string') {
path = options.path;
}
var check = makePathCheck(path, options.resource);

// add request handler
server.on('request', function (req, res) {
listener.request.push(function (req, res) {
if (check(req)) {
debug('intercepting request for path "%s"', path);
engine.handleRequest(req, res);
} else {
for (var i = 0, l = oldListeners.length; i < l; i++) {
oldListeners[i].call(server, req, res);
}
return true;
}
return false;
});

if(~engine.transports.indexOf('websocket')) {
server.on('upgrade', function (req, socket, head) {
listener.upgrade.push(function (req, socket, head) {
if (check(req)) {
engine.handleUpgrade(req, socket, head);
} else if (false !== options.destroyUpgrade) {
socket.end();
return true;
}
return false;
});
}

if (~engine.transports.indexOf('flashsocket')
&& false !== options.policyFile) {
server.on('connection', function (socket) {
engine.handleSocket(socket);
});
}
}

return engine;
};

/**
* Make function to check path/route.
*
* @param {Mixed} path
* @param {String} resource
* @return {Function} check function
* @api private
*/

function makePathCheck(path, resource) {
if (typeof resource === 'function') {
return resource;
} else
if (typeof resource === 'object' && resource.test) {
return function (req) {
return resource.test(req.url.substring(path.length));
};
} else {
// normalize path
var route = path + '/' + (resource || 'default').replace(/^\/|\/$/g, '') + '/';
return function (req) {
return route == req.url.substr(0, route.length);
}
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
, "author": "Guillermo Rauch <[email protected]>"
, "contributors": [
{ "name": "Eugen Dueck", "web": "https://github.com/EugenDueck" },
{ "name": "Afshin Mehrabani", "web": "https://github.com/afshinm" }
{ "name": "Afshin Mehrabani", "web": "https://github.com/afshinm" },
{ "name": "Christoph Dorn", "web": "https://github.com/cadorn" }
]
, "dependencies": {
"debug": "0.6.0"
Expand Down
87 changes: 87 additions & 0 deletions test/engine.io.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,91 @@ describe('engine', function () {
});
});

describe('attach() at resource', function () {
it('should attach at string resource', function (done) {
var server = http.createServer()
, engine = eio.attach(server, {
resource: "/custom/path"
});

server.listen(function () {
var uri = ('http://localhost:%d/engine.io/custom/path/').s(server.address().port);
request.get(uri, function (res) {
expect(res.status).to.be(500);
server.once('close', done);
server.close();
});
});
});

it('should attach at regexp resource', function (done) {
var server = http.createServer()
, engine = eio.attach(server, {
resource: /^\/custom\/[^\/]+/
});

server.listen(function () {
var uri = ('http://localhost:%d/engine.io/custom/path/').s(server.address().port);
request.get(uri, function (res) {
expect(res.status).to.be(500);
server.once('close', done);
server.close();
});
});
});

it('should attach at check function resource', function (done) {
var server = http.createServer()
, engine = eio.attach(server, {
resource: function(req) {
var path = '/engine.io/custom/path';
return path == req.url.substr(0, path.length);
}
});

server.listen(function () {
var uri = ('http://localhost:%d/engine.io/custom/path/').s(server.address().port);
request.get(uri, function (res) {
expect(res.status).to.be(500);
server.once('close', done);
server.close();
});
});
});

it('should work with many instances', function (done) {
// TODO: Capture `warning: possible EventEmitter memory leak detected. 11 listeners added.` and fail test.
var server = http.createServer();
var tests = [];
function attach(i) {
var engine = eio.attach(server, {
resource: "/custom/path-" + i
});
tests.push(function(done) {
var uri = ('http://localhost:%d/engine.io/custom/path-' + i + '/').s(server.address().port);
request.get(uri, function (res) {
expect(res.status).to.be(500);
done();
});
});
}
for (var i=0; i<100; i++) {
attach(i);
}
server.listen(function () {
var counter = 0;
tests.forEach(function(test) {
counter += 1;
test(function() {
counter -= 1;
if (counter === 0) {
server.once('close', done);
server.close();
}
});
});
});
});
});

});