mirror of
https://github.com/socketio/socket.io.git
synced 2026-04-30 03:00:39 -04:00
This fixes an issue where multiple engine.io instances (on different paths) would result in the closing of websocket connections for all of the instances. This happened because each engine.io instance would register an `upgrade` handler on the server. This handler would check for a matching path and otherwise call `socket.end()` Since multiple upgrade events would be triggered with different paths, the peer handlers would close each other. This patch resolves this behavior in the following way: - When an instance upgrade handler encounters a path which it does not recognize it creates a timeout for `destroyUpgradeTimeout`. - At the end of the timeout, the socket is checked for writable state and bytes written. If there has been not activity and the socket is writable, then it will be ended. This allows for peer socket handlers to keep the socket alive by sending some data over it. This also mimics the core node behavior of closing sockets on upgrade when no handler is specified. We consider not handling an upgrade request similar to no handler. However, we cannot immediately end the socket for the reasons noted above. fixes #143
171 lines
3.6 KiB
JavaScript
171 lines
3.6 KiB
JavaScript
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var http = require('http')
|
|
, debug = require('debug')('engine:core');
|
|
|
|
/**
|
|
* Protocol revision number.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
exports.protocol = 1;
|
|
|
|
/**
|
|
* Expose Server constructor.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
exports.Server = require('./server');
|
|
|
|
/**
|
|
* Expose Server constructor.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
exports.Socket = require('./socket');
|
|
|
|
/**
|
|
* Expose Transport constructor.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
exports.Transport = require('./transport');
|
|
|
|
/**
|
|
* Expose mutable list of available trnasports.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
exports.transports = require('./transports');
|
|
|
|
/**
|
|
* Exports parser.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
exports.parser = require('engine.io-parser');
|
|
|
|
/**
|
|
* Crates an http.Server exclusively used for WS upgrades.
|
|
*
|
|
* @param {Number} port
|
|
* @param {Function} callback
|
|
* @param {Object} options
|
|
* @return {Server} websocket.io server
|
|
* @api public
|
|
*/
|
|
|
|
exports.listen = function (port, options, fn) {
|
|
if ('function' == typeof options) {
|
|
fn = options;
|
|
options = {};
|
|
}
|
|
|
|
var server = http.createServer(function (req, res) {
|
|
res.writeHead(501);
|
|
res.end('Not Implemented');
|
|
});
|
|
|
|
server.listen(port, fn);
|
|
|
|
// create engine server
|
|
var engine = exports.attach(server, options);
|
|
engine.httpServer = server;
|
|
|
|
return engine;
|
|
};
|
|
|
|
/**
|
|
* Captures upgrade requests for a http.Server.
|
|
*
|
|
* @param {http.Server} server
|
|
* @param {Object} options
|
|
* @return {Server} engine server
|
|
* @api public
|
|
*/
|
|
|
|
exports.attach = function (server, options) {
|
|
var engine = new exports.Server(options);
|
|
var options = options || {};
|
|
var path = (options.path || '/engine.io').replace(/\/$/, '');
|
|
|
|
var destroyUpgrade = (options.destroyUpgrade !== undefined) ? options.destroyUpgrade : true;
|
|
var destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000;
|
|
|
|
// normalize path
|
|
path += '/';
|
|
|
|
function check (req) {
|
|
return path == req.url.substr(0, path.length);
|
|
}
|
|
|
|
// cache and clean up listeners
|
|
var listeners = server.listeners('request').slice(0);
|
|
server.removeAllListeners('request');
|
|
server.on('close', engine.close.bind(engine));
|
|
|
|
// add request handler
|
|
server.on('request', function(req, res){
|
|
if (check(req)) {
|
|
debug('intercepting request for path "%s"', path);
|
|
engine.handleRequest(req, res);
|
|
} else {
|
|
for (var i = 0, l = listeners.length; i < l; i++) {
|
|
listeners[i].call(server, req, res);
|
|
}
|
|
}
|
|
});
|
|
|
|
if(~engine.transports.indexOf('websocket')) {
|
|
server.on('upgrade', function (req, socket, head) {
|
|
if (check(req)) {
|
|
engine.handleUpgrade(req, socket, head);
|
|
} else if (false !== options.destroyUpgrade) {
|
|
// default node behavior is to disconnect when no handlers
|
|
// but by adding a handler, we prevent that
|
|
// and if no eio thing handles the upgrade
|
|
// then the socket needs to die!
|
|
setTimeout(function() {
|
|
if (socket.writable && socket.bytesWritten <= 0) {
|
|
return socket.end();
|
|
}
|
|
}, options.destroyUpgradeTimeout);
|
|
}
|
|
});
|
|
}
|
|
|
|
// flash policy file
|
|
var trns = engine.transports;
|
|
var policy = options.policyFile;
|
|
if (~trns.indexOf('flashsocket') && false !== policy) {
|
|
server.on('connection', function (socket) {
|
|
engine.handleSocket(socket);
|
|
});
|
|
}
|
|
|
|
return engine;
|
|
};
|
|
|
|
/**
|
|
* Invoking the library as a function delegates to attach
|
|
*
|
|
* @param {http.Server} server
|
|
* @param {Object} options
|
|
* @return {Server} engine server
|
|
* @api public
|
|
*/
|
|
|
|
module.exports = function() {
|
|
return exports.attach.apply(this, arguments);
|
|
};
|
|
|
|
module.exports.__proto__ = exports;
|