Files
meteor/packages/ddp/stream_server.js
David Glasser 7c40c3c5a6 Remove legacy proxy-binding
Also remove SIGHUP handler. This was designed for a legacy system, and
SIGHUP would be a better fit for what autoupdate currently uses SIGUSR2
for.  This includes removing some code in the ddp server to close
sockets on SIGHUP.
2015-01-29 14:03:11 -08:00

140 lines
5.6 KiB
JavaScript

var url = Npm.require('url');
var pathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || "";
StreamServer = function () {
var self = this;
self.registration_callbacks = [];
self.open_sockets = [];
// Because we are installing directly onto WebApp.httpServer instead of using
// WebApp.app, we have to process the path prefix ourselves.
self.prefix = pathPrefix + '/sockjs';
// routepolicy is only a weak dependency, because we don't need it if we're
// just doing server-to-server DDP as a client.
if (Package.routepolicy) {
Package.routepolicy.RoutePolicy.declare(self.prefix + '/', 'network');
}
// set up sockjs
var sockjs = Npm.require('sockjs');
var serverOptions = {
prefix: self.prefix,
log: function() {},
// this is the default, but we code it explicitly because we depend
// on it in stream_client:HEARTBEAT_TIMEOUT
heartbeat_delay: 45000,
// The default disconnect_delay is 5 seconds, but if the server ends up CPU
// bound for that much time, SockJS might not notice that the user has
// reconnected because the timer (of disconnect_delay ms) can fire before
// SockJS processes the new connection. Eventually we'll fix this by not
// combining CPU-heavy processing with SockJS termination (eg a proxy which
// converts to Unix sockets) but for now, raise the delay.
disconnect_delay: 60 * 1000,
// Set the USE_JSESSIONID environment variable to enable setting the
// JSESSIONID cookie. This is useful for setting up proxies with
// session affinity.
jsessionid: !!process.env.USE_JSESSIONID
};
// If you know your server environment (eg, proxies) will prevent websockets
// from ever working, set $DISABLE_WEBSOCKETS and SockJS clients (ie,
// browsers) will not waste time attempting to use them.
// (Your server will still have a /websocket endpoint.)
if (process.env.DISABLE_WEBSOCKETS)
serverOptions.websocket = false;
self.server = sockjs.createServer(serverOptions);
if (!Package.webapp) {
throw new Error("Cannot create a DDP server without the webapp package");
}
// Install the sockjs handlers, but we want to keep around our own particular
// request handler that adjusts idle timeouts while we have an outstanding
// request. This compensates for the fact that sockjs removes all listeners
// for "request" to add its own.
Package.webapp.WebApp.httpServer.removeListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);
self.server.installHandlers(Package.webapp.WebApp.httpServer);
Package.webapp.WebApp.httpServer.addListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);
// Support the /websocket endpoint
self._redirectWebsocketEndpoint();
self.server.on('connection', function (socket) {
socket.send = function (data) {
socket.write(data);
};
socket.on('close', function () {
self.open_sockets = _.without(self.open_sockets, socket);
});
self.open_sockets.push(socket);
// XXX COMPAT WITH 0.6.6. Send the old style welcome message, which
// will force old clients to reload. Remove this once we're not
// concerned about people upgrading from a pre-0.7.0 release. Also,
// remove the clause in the client that ignores the welcome message
// (livedata_connection.js)
socket.send(JSON.stringify({server_id: "0"}));
// call all our callbacks when we get a new socket. they will do the
// work of setting up handlers and such for specific messages.
_.each(self.registration_callbacks, function (callback) {
callback(socket);
});
});
};
_.extend(StreamServer.prototype, {
// call my callback when a new socket connects.
// also call it for all current connections.
register: function (callback) {
var self = this;
self.registration_callbacks.push(callback);
_.each(self.all_sockets(), function (socket) {
callback(socket);
});
},
// get a list of all sockets
all_sockets: function () {
var self = this;
return _.values(self.open_sockets);
},
// Redirect /websocket to /sockjs/websocket in order to not expose
// sockjs to clients that want to use raw websockets
_redirectWebsocketEndpoint: function() {
var self = this;
// Unfortunately we can't use a connect middleware here since
// sockjs installs itself prior to all existing listeners
// (meaning prior to any connect middlewares) so we need to take
// an approach similar to overshadowListeners in
// https://github.com/sockjs/sockjs-node/blob/cf820c55af6a9953e16558555a31decea554f70e/src/utils.coffee
_.each(['request', 'upgrade'], function(event) {
var httpServer = Package.webapp.WebApp.httpServer;
var oldHttpServerListeners = httpServer.listeners(event).slice(0);
httpServer.removeAllListeners(event);
// request and upgrade have different arguments passed but
// we only care about the first one which is always request
var newListener = function(request /*, moreArguments */) {
// Store arguments for use within the closure below
var args = arguments;
// Rewrite /websocket and /websocket/ urls to /sockjs/websocket while
// preserving query string.
var parsedUrl = url.parse(request.url);
if (parsedUrl.pathname === pathPrefix + '/websocket' ||
parsedUrl.pathname === pathPrefix + '/websocket/') {
parsedUrl.pathname = self.prefix + '/websocket';
request.url = url.format(parsedUrl);
}
_.each(oldHttpServerListeners, function(oldListener) {
oldListener.apply(httpServer, args);
});
};
httpServer.addListener(event, newListener);
});
}
});