Skip to content

Commit

Permalink
Rewritten to address concerns raised in abernix's review
Browse files Browse the repository at this point in the history
  • Loading branch information
vlasky committed May 19, 2017
1 parent 693ac8a commit d9e7f86
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 50 deletions.
82 changes: 52 additions & 30 deletions packages/webapp/webapp_server.js
Expand Up @@ -440,15 +440,6 @@ var getUrlPrefixForArch = function (arch) {
'' : '/' + '__' + arch.replace(/^web\./, '');
};

// parse port to see if it's a Windows Server-style named pipe or a UNIX path/filename for a UNIX domain socket file. If so, return as-is (String), otherwise return as Int
WebAppInternals.parsePort = function (port) {
if( /\\\\?.+\\pipe\\?.+/.test(port) || /^(.+)\/([^/]+)$/.test(port) ) {
return port;
}

return parseInt(port);
};

var runWebAppServer = function () {
var shuttingDown = false;
var syncQueue = new Meteor._SynchronousQueue();
Expand Down Expand Up @@ -813,7 +804,7 @@ var runWebAppServer = function () {
WebAppInternals.generateBoilerplate();

// only start listening after all the startup code has run.
var localPort = WebAppInternals.parsePort(process.env.PORT) || 0;
var localPort = process.env.PORT || 0;
var host = process.env.BIND_IP;
var localIp = host || '0.0.0.0';

Expand All @@ -832,32 +823,63 @@ var runWebAppServer = function () {
}));
};

if (typeof localPort == "number") {
var listenOptions = { port: localPort, host: host };
} else {
localPort = isNaN(Number(localPort)) ? localPort : Number(localPort);

if (typeof localPort == "string") {
var socketPath = localPort;
var listenOptions = { path: socketPath };

httpServer.on('error', Meteor.bindEnvironment(function(e) {
if (e.code == 'EADDRINUSE') {
var clientSocket = new net.Socket();
clientSocket.on('error', Meteor.bindEnvironment(function(e) {
if (e.code == 'ECONNREFUSED') {
console.log("Deleting stale socket file");
fs.unlinkSync(socketPath);
startHttpServer(listenOptions);
}
}));
clientSocket.connect({ path: socketPath }, function() {
console.log("Another server is already listening on socket: " + socketPath + ", exiting.");
// Test to see if the socket path is Windows Server-style named pipe with the
// format: \\.\pipe\{randomstring} or \\{servername}\pipe\{randomstring}
// Otherwise, treat it as a UNIX domain socket path pointing to a socket file
if ( /\\\\?.+\\pipe\\?.+/.test(socketPath)) {
startHttpServer(listenOptions);
} else {
if (fs.existsSync(socketPath)) {
if (fs.statSync(socketPath).isSocket()) {
var clientSocket = new net.Socket();
clientSocket.on('error', Meteor.bindEnvironment(function(e) {
//If there is an existing socket file that we cannot connect to,
//it must be stale as it is not associated with a running server
if (e.code == 'ECONNREFUSED') {
console.log("Deleting stale socket file");
fs.unlinkSync(socketPath);
startHttpServer(listenOptions);
}
}));
clientSocket.connect({ path: socketPath }, function() {
//If there is a running server listening on that socket file,
//we have to leave the file alone to avoid preventing other clients
//from connecting to it.
console.error("Another server is already listening on socket: " + socketPath + ", exiting.");
process.exit();
});
} else {
//There is an existing file at socketPath that is not a socket,
//so we will leave it alone and exit
console.error("Cannot listen on socket: " + socketPath + ", exiting.");
process.exit();
});
}
} else {
startHttpServer(listenOptions);
}
}));
//Clean up the socket file when we've finished to avoid leaving a stale one
//That situation should only be able to happen if the running node process is
//killed via signal 9 (SIGKILL).
process.on('exit', Meteor.bindEnvironment(function(e) {
fs.unlinkSync(socketPath);
}));
process.on('SIGINT', Meteor.bindEnvironment(function(e) {
process.exit();
}));
}
} else if (typeof localPort == "number") {
var listenOptions = { port: localPort, host: host };
startHttpServer(localPort);
} else {
console.error("Invalid PORT specified");
process.exit();
}

startHttpServer(listenOptions);

return 'DAEMON';
};
};
Expand Down
20 changes: 0 additions & 20 deletions packages/webapp/webapp_tests.js
Expand Up @@ -156,26 +156,6 @@ Tinytest.add("webapp - generating boilerplate should not change runtime config",
test.isFalse(__meteor_runtime_config__.WEBAPP_TEST_KEY);
});

// Support 'named pipes' (strings) as ports for support of Windows Server / Azure deployments
Tinytest.add("webapp - port should be parsed as int unless it is a named pipe or path/filename of a Unix domain socket", function(test){
// Named pipes on Windows Server follow the format: \\.\pipe\{randomstring} or \\{servername}\pipe\{randomstring}
var namedPipe = "\\\\.\\pipe\\b27429e9-61e3-4c12-8bfe-950fa3295f74";
var namedPipeServer = "\\\\SERVERNAME-1234\\pipe\\6e157e98-faef-49e4-a0cf-241037223308";
var socketPath = "/var/run/meteor.sock";

test.equal(WebAppInternals.parsePort(namedPipe),
"\\\\.\\pipe\\b27429e9-61e3-4c12-8bfe-950fa3295f74");
test.equal(WebAppInternals.parsePort(namedPipeServer),
"\\\\SERVERNAME-1234\\pipe\\6e157e98-faef-49e4-a0cf-241037223308");
test.equal(WebAppInternals.parsePort(socketPath),
"/var/run/meteor.sock");
test.equal(WebAppInternals.parsePort(8080),
8080);
test.equal(WebAppInternals.parsePort("8080"),
8080);
test.equal(WebAppInternals.parsePort("8080abc"), // ensure strangely formatted ports still work for backwards compatibility
8080);
});

__meteor_runtime_config__.WEBAPP_TEST_A = '<p>foo</p>';
__meteor_runtime_config__.WEBAPP_TEST_B = '</script>';
Expand Down

0 comments on commit d9e7f86

Please sign in to comment.