Skip to content

Commit

Permalink
fix(proxy): handle proxied socket.io websocket transport upgrade
Browse files Browse the repository at this point in the history
Fixes websocket upgrade handling for proxied routes.
This was motivated by long (10s) default timeouts when socket.io
attempts to use websockets and fails to perform the upgrade.
  • Loading branch information
gigadude committed Nov 2, 2013
1 parent 7ac4527 commit fcc2a98
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 9 deletions.
38 changes: 31 additions & 7 deletions lib/middleware/proxy.js
Expand Up @@ -3,7 +3,6 @@ var httpProxy = require('http-proxy');

var log = require('../logger').create('proxy');


var parseProxyConfig = function(proxies) {
var proxyConfig = {};
var endsWithSlash = function(str) {
Expand Down Expand Up @@ -57,15 +56,19 @@ var parseProxyConfig = function(proxies) {
* @param proxies a map of routes to proxy url
* @return {Function} handler function
*/
var createProxyHandler = function(proxy, proxyConfig, proxyValidateSSL) {
var createProxyHandler = function(proxy, proxyConfig, proxyValidateSSL, urlRoot) {
var proxies = parseProxyConfig(proxyConfig);
var proxiesList = Object.keys(proxies).sort().reverse();

if (!proxiesList.length) {
return function(request, response, next) {
var nullProxy = function createNullProxy(request, response, next) {
return next();
};
nullProxy.upgrade = function upgradeNullProxy() {
};
return nullProxy;
}

proxy.on('proxyError', function(err, req) {
if (err.code === 'ECONNRESET' && req.socket.destroyed) {
log.debug('failed to proxy %s (browser hung up the socket)', req.url);
Expand All @@ -74,7 +77,7 @@ var createProxyHandler = function(proxy, proxyConfig, proxyValidateSSL) {
}
});

return function(request, response, next) {
var middleware = function createProxy(request, response, next) {
for (var i = 0; i < proxiesList.length; i++) {
if (request.url.indexOf(proxiesList[i]) === 0) {
var proxiedUrl = proxies[proxiesList[i]];
Expand All @@ -92,9 +95,30 @@ var createProxyHandler = function(proxy, proxyConfig, proxyValidateSSL) {

return next();
};
};

middleware.upgrade = function upgradeProxy(request, socket, head) {
// special-case karma's route to avoid upgrading it
if (request.url.indexOf(urlRoot) === 0) {
log.debug('NOT upgrading proxyWebSocketRequest %s', request.url);
return;
}
for (var i = 0; i < proxiesList.length; i++) {
if (request.url.indexOf(proxiesList[i]) === 0) {
var proxiedUrl = proxies[proxiesList[i]];
log.debug('upgrade proxyWebSocketRequest %s to %s:%s',
request.url, proxiedUrl.host, proxiedUrl.port);
proxy.proxyWebSocketRequest(request, socket, head,
{ host: proxiedUrl.host, port: proxiedUrl.port });
}
}
};

return middleware;
};

exports.create = function(/* config.proxies */ proxies, /* config.proxyValidateSSL */ validateSSL) {
return createProxyHandler(new httpProxy.RoutingProxy({changeOrigin: true}), proxies, validateSSL);
exports.create = function(/* config */ config, /* config.proxies */ proxies,
/* config.proxyValidateSSL */ validateSSL) {
log.debug('createProxyHandler urlRoot=%s', config.urlRoot);
return createProxyHandler(new httpProxy.RoutingProxy({changeOrigin: true}),
proxies, validateSSL, config.urlRoot);
};
2 changes: 2 additions & 0 deletions lib/server.js
Expand Up @@ -202,6 +202,8 @@ start.$inject = ['injector', 'config', 'launcher', 'emitter', 'preprocess', 'fil

var createSocketIoServer = function(webServer, executor, config) {
var server = io.listen(webServer, {
// avoid destroying http upgrades from socket.io to get proxied websockets working
'destroy upgrade': false,
logger: logger.create('socket.io', constant.LOG_ERROR),
resource: config.urlRoot + 'socket.io',
transports: config.transports
Expand Down
14 changes: 12 additions & 2 deletions lib/web-server.js
Expand Up @@ -9,6 +9,7 @@ var karmaMiddleware = require('./middleware/karma');
var sourceFilesMiddleware = require('./middleware/source-files');
var proxyMiddleware = require('./middleware/proxy');

var log = require('./logger').create('web-server');

var createCustomHandler = function(customFileHandlers, /* config.basePath */ basePath) {
return function(request, response, next) {
Expand Down Expand Up @@ -48,21 +49,30 @@ var createWebServer = function(injector, emitter) {
}
};

var proxyMiddlewareInstance = injector.invoke(proxyMiddleware.create);

var handler = connect()
.use(connect.compress(compressOptions))
.use(injector.invoke(runnerMiddleware.create))
.use(injector.invoke(karmaMiddleware.create))
.use(injector.invoke(sourceFilesMiddleware.create))
// TODO(vojta): extract the proxy into a plugin
.use(injector.invoke(proxyMiddleware.create))
.use(proxyMiddlewareInstance)
// TODO(vojta): remove, this is only here because of karma-dart
// we need a better way of custom handlers
.use(injector.invoke(createCustomHandler))
.use(function(request, response) {
common.serve404(response, request.url);
});

return http.createServer(handler);
var server = http.createServer(handler);

server.on('upgrade', function (req, socket, head) {
log.debug('upgrade %s', req.url);
proxyMiddlewareInstance.upgrade(req, socket, head);
});

return server;
};


Expand Down

0 comments on commit fcc2a98

Please sign in to comment.