k-highway
Version:
Websocket and browserchannel transport for Racer
165 lines (132 loc) • 4.62 kB
JavaScript
var WebSocket = require('ws');
var WebSocketServer = WebSocket.Server;
var BrowserChannelServer = require('browserchannel').server;
var crypto = require('crypto');
var extend = require('extend');
var createBrowserChannelStream = require('./createBrowserChannelStream');
var createWebSocketStream = require('./createWebSocketStream');
var bundle = require('./bundle');
var defaultServerOptions = {
session: null,
base: '/ws',
noPing: false,
pingInterval: 30000,
perMessageDeflate: true
};
module.exports = function(backend, serverOptions, clientOptions) {
serverOptions = serverOptions || {};
serverOptions = extend({}, defaultServerOptions, serverOptions);
// ws-module specific options
serverOptions.path = serverOptions.base;
serverOptions.noServer = true;
// add the client side script to the Browserify bundle
backend.on('bundle', bundle(clientOptions));
var middleware = BrowserChannelServer(serverOptions, function(client, connectRequest) {
if (serverOptions.session) {
// https://github.com/expressjs/session/pull/57
if (!connectRequest.originalUrl) connectRequest.originalUrl = connectRequest.url;
serverOptions.session(connectRequest, {}, startBrowserChannel);
} else {
startBrowserChannel();
}
function startBrowserChannel() {
var rejected = false;
var rejectReason;
function reject(reason) {
rejected = true;
if (reason) rejectReason = reason;
}
if (!connectRequest) {
console.error('no connectRequest');
console.trace();
}
else if (connectRequest.session) client.connectSession = connectRequest.session;
backend.emit('client', client, reject);
if (rejected) {
// Tell the client to stop trying to connect
client.stop(function() {
client.close(rejectReason);
});
return;
}
var stream = createBrowserChannelStream(client);
doneInitialization(backend, stream, connectRequest);
}
});
var wss = new WebSocketServer(serverOptions);
wss.on('connection', function (client, req) {
client.id = crypto.randomBytes(16).toString('hex');
// Some proxy drop out long connections
// so do ping periodically to prevent this
// interval = 30s by default
if (!serverOptions.noPing){
client.timer = setInterval(function(){
if (client.readyState === WebSocket.OPEN) {
client.ping();
} else {
clearInterval(client.timer);
}
}, serverOptions.pingInterval);
}
var rejected = false;
var rejectReason;
function reject(reason) {
rejected = true;
if (reason) rejectReason = reason;
}
if (req.session) client.connectSession = req.session;
backend.emit('client', client, reject);
if (rejected) {
// Tell the client to stop trying to connect
client.close(1001, rejectReason);
return;
}
var stream = createWebSocketStream(client);
doneInitialization(backend, stream, req);
});
function upgrade(req, socket, upgradeHead){
//copy upgradeHead to avoid retention of large slab buffers used in node core
var head = new Buffer(upgradeHead.length);
upgradeHead.copy(head);
if (serverOptions.session) {
// https://github.com/expressjs/session/pull/57
if (!req.originalUrl) req.originalUrl = req.url;
serverOptions.session(req, {}, next);
} else {
next();
}
function next() {
wss.handleUpgrade(req, socket, head, function(client) {
wss.emit('connection'+req.url, client);
wss.emit('connection', client, req);
});
}
}
if (serverOptions.session) {
backend.use('connect', function(shareRequest, next){
var req = shareRequest.req;
var agent = shareRequest.agent;
if (!agent.connectSession && req && req.session) {
agent.connectSession = req.session;
}
// TODO check if we really need the code
// for now it doesn't work anymore
// because sharedb 'connect' hook can work
// only synchronously
// serverOptions.session(req, {}, function(){
// agent.connectSession = req.session;
// next();
// });
next()
});
}
return {upgrade: upgrade, middleware: middleware, wss: wss};
};
function doneInitialization(backend, stream, request){
backend.on('connect', function(data){
var agent = data.agent;
if (request.session) agent.connectSession = request.session;
backend.emit('share agent', agent, stream);
});
backend.listen(stream, request);
}