tjh-express
Version:
TogetherJS Hub express module
151 lines (130 loc) • 5.08 kB
JavaScript
/*
* A hacked websocket module which retains compatibility with the old
* Hixie-76 version of the standard, needed for phantom JS (and,
* presumably, very old browsers).
*
* This file released into the public domain
* by C. Scott Ananian <cscott@cscott.net> 2014-08-26
*
* Based on https://gist.github.com/toshirot/1428579
*/
var events = require("events");
var util = require("util");
var WebSocketRequest = require('websocket').request;
var WebSocketServer = require('websocket').server;
// Copy helpers from WebSocketServer to WebSocketRequest
WebSocketRequest.prototype.connections = [];
WebSocketRequest.prototype.handleRequestAccepted =
WebSocketServer.prototype.handleRequestAccepted;
WebSocketRequest.prototype.handleConnectionClose =
WebSocketServer.prototype.handleConnectionClose;
WebSocketRequest.prototype.broadcastUTF =
WebSocketServer.prototype.broadcastUTF;
var miksagoServerFactory = require('websocket-server');
var miksagoConnection = require('websocket-server/lib/ws/connection');
var CompatWebSocketServer = function(options) {
events.EventEmitter.call(this); // superclass constructor
var self = this;
var handleConnection;
// node-websocket-server (hixie-75 and hixie-76 support)
var miksagoServer = miksagoServerFactory.createServer();
miksagoServer.server = options.httpServer;
miksagoServer.addListener('connection', function(connection) {
// Add remoteAddress property
connection.remoteAddress = connection._socket.remoteAddress;
// We want to use "sendUTF" regardless of the server implementation
connection.sendUTF = connection.send;
handleConnection(connection);
});
// WebSocket-Node config (modern websocket support)
var wsServerConfig = {
// All options *except* 'httpServer' are required when bypassing
// WebSocketServer.
maxReceivedFrameSize: options.maxReceivedFrameSize || 0x10000,
maxReceivedMessageSize: options.maxReceivedMessageSize || 0x100000,
fragmentOutgoingMessages: true,
fragmentationThreshold: 0x4000,
keepalive: true,
keepaliveInterval: 20000,
assembleFragments: true,
// autoAcceptConnections is not applicable when bypassing WebSocketServer
// autoAcceptConnections: false,
disableNagleAlgorithm: true,
closeTimeout: 5000
};
// Handle the upgrade event ourselves instead of using WebSocketServer
var wsRequest={};
options.httpServer.on('upgrade', function(req, socket, head) {
if (typeof req.headers['sec-websocket-version'] !== 'undefined') {
// WebSocket hybi-08/-09/-10 connection (WebSocket-Node)
wsRequest = new WebSocketRequest(socket, req, wsServerConfig);
try {
wsRequest.readHandshake();
} catch (e) {
wsRequest.reject(
e.httpCode ? e.httpCode : 400,
e.message,
e.headers
);
return;
}
wsRequest.once('requestAccepted', function(connection) {
wsRequest.handleRequestAccepted(connection);
});
self.emit('request', wsRequest);
} else {
// WebSocket hixie-75/-76/hybi-00 connection (node-websocket-server)
if (req.method === 'GET' &&
(req.headers.upgrade && req.headers.connection) &&
req.headers.upgrade.toLowerCase() === 'websocket' &&
req.headers.connection.toLowerCase() === 'upgrade') {
new miksagoConnection(
miksagoServer.manager, miksagoServer.options, req, socket, head
);
}
}
});
// A connection handler for old-style websockets
handleConnection = function(connection) {
// fake a request
self.emit('request', new CompatRequest(self, connection));
};
};
util.inherits(CompatWebSocketServer, events.EventEmitter);
var CompatRequest = function(server, connection) {
this._server = server;
this._connection = connection;
this.origin = connection._options.origin || '*';
this.httpRequest = connection._req;
// create wrapper right away in order to install event handlers promptly
this._connectionWrapper = new CompatConnection(server, connection);
};
CompatRequest.prototype.reject = function(code, message) {
this._connection.reject(message || "no reason");
};
CompatRequest.prototype.accept = function(proto, origin) {
// this is faked: we've already accepted the connection
return this._connectionWrapper;
};
var CompatConnection = function(server, connection) {
var self = this;
events.EventEmitter.call(this); // superclass constructor
this._server = server;
this._connection = connection;
this.remoteAddress = connection.remoteAddress;
connection.addListener('message', function(wsMessage) {
// make the argument compatible with WebSocket-Node
self.emit('message', {
type: 'utf8',
utf8Data: wsMessage
});
});
connection.addListener('close', function() {
self.emit('close');
});
};
util.inherits(CompatConnection, events.EventEmitter);
CompatConnection.prototype.sendUTF = function(message) {
return this._connection.sendUTF(message);
};
module.exports.server = CompatWebSocketServer;