163 lines
4.9 KiB
JavaScript
163 lines
4.9 KiB
JavaScript
'use strict';
|
|
|
|
module.exports.create = function () {
|
|
var id = '0';
|
|
var promiseApp;
|
|
|
|
//
|
|
// Worker Mode
|
|
//
|
|
function createAndBind(conf) {
|
|
// NOTE: this callback must return a promise for an express app
|
|
function getOrCreateHttpApp(err, insecserver, webserver/*, newMessage*/) {
|
|
var PromiseA = require('bluebird');
|
|
|
|
if (promiseApp) {
|
|
return promiseApp;
|
|
}
|
|
|
|
promiseApp = new PromiseA(function (resolve) {
|
|
function initHttpApp(srvmsg) {
|
|
if ('walnut.webserver.onrequest' !== srvmsg.type) {
|
|
console.warn('[Worker] [onrequest] unexpected message:');
|
|
console.warn(srvmsg);
|
|
return;
|
|
}
|
|
|
|
process.removeListener('message', initHttpApp);
|
|
|
|
if (srvmsg.conf) {
|
|
Object.keys(srvmsg.conf).forEach(function (key) {
|
|
conf[key] = srvmsg.conf[key];
|
|
});
|
|
}
|
|
|
|
resolve(require('../lib/worker').create(webserver, conf));
|
|
}
|
|
|
|
process.send({ type: 'walnut.webserver.listening' });
|
|
process.on('message', initHttpApp);
|
|
}).then(function (app) {
|
|
console.info('[Worker Ready]');
|
|
return app;
|
|
});
|
|
|
|
return promiseApp;
|
|
}
|
|
|
|
function serverCallback(err, webserver) {
|
|
if (err) {
|
|
console.error('[ERROR] worker.js');
|
|
console.error(err.stack);
|
|
throw err;
|
|
}
|
|
|
|
console.info("#" + id + " Listening on " + conf.protocol + "://" + webserver.address().address + ":" + webserver.address().port, '\n');
|
|
|
|
// we are returning the promise result to the caller
|
|
return getOrCreateHttpApp(null, null, webserver, conf);
|
|
}
|
|
|
|
// Note the odd use of callbacks (instead of promises) here
|
|
// It's to avoid loading bluebird yet (see sni-server.js for explanation)
|
|
function localServerCreate(port) {
|
|
function initServer(err, server) {
|
|
var app;
|
|
var promiseApp;
|
|
|
|
if (err) {
|
|
serverCallback(err);
|
|
return;
|
|
}
|
|
|
|
server.on('error', serverCallback);
|
|
server.listen(port, function () {
|
|
// is it even theoritically possible for
|
|
// a request to come in before this callback has fired?
|
|
// I'm assuming this event must fire before any request event
|
|
promiseApp = serverCallback(null, server);
|
|
});
|
|
/*
|
|
server.listen(port, '::::', function () {
|
|
// is it even theoritically possible for
|
|
// a request to come in before this callback has fired?
|
|
// I'm assuming this event must fire before any request event
|
|
promiseApp = serverCallback(null, server);
|
|
});
|
|
*/
|
|
|
|
// Get up and listening as absolutely quickly as possible
|
|
function onRequest(req, res) {
|
|
// this is a hot piece of code, so we cache the result
|
|
if (app) {
|
|
app(req, res);
|
|
return;
|
|
}
|
|
|
|
promiseApp.then(function (_app) {
|
|
console.log('[Server]', req.method, req.host || req.headers['x-forwarded-host'] || req.headers.host, req.url);
|
|
app = _app;
|
|
app(req, res);
|
|
});
|
|
}
|
|
|
|
server.on('request', onRequest);
|
|
}
|
|
|
|
initServer(null, require('http').createServer());
|
|
}
|
|
|
|
// NOTE that message.conf[x] will be overwritten when the next message comes in
|
|
localServerCreate(conf.localPort);
|
|
}
|
|
|
|
function waitForConfig(realMessage) {
|
|
console.log('realMessage', realMessage);
|
|
if ('walnut.init' !== realMessage.type) {
|
|
console.warn('[Worker] 0 got unexpected message:');
|
|
console.warn(realMessage);
|
|
return;
|
|
}
|
|
|
|
var conf = realMessage.conf;
|
|
process.removeListener('message', waitForConfig);
|
|
|
|
createAndBind(conf);
|
|
}
|
|
|
|
// we are in cluster mode, as opposed to standalone mode
|
|
id = require('cluster').worker.id.toString();
|
|
// We have to wait to get the configuration from the master process
|
|
// before we can start our webserver
|
|
console.info('[Worker #' + id + '] online!');
|
|
process.on('message', waitForConfig);
|
|
|
|
//
|
|
// Debugging
|
|
//
|
|
process.on('exit', function (code) {
|
|
// only sync code can run here
|
|
console.info('uptime:', process.uptime());
|
|
console.info(process.memoryUsage());
|
|
console.info('[exit] process.exit() has been called (or master has killed us).');
|
|
console.info(code);
|
|
});
|
|
process.on('beforeExit', function () {
|
|
// async can be scheduled here
|
|
console.info('[beforeExit] Event Loop is empty. Process will end.');
|
|
});
|
|
process.on('unhandledRejection', function (err) {
|
|
// this should always throw
|
|
// (it means somewhere we're not using bluebird by accident)
|
|
console.error('[caught unhandledRejection]:', err.message || '');
|
|
Object.keys(err).forEach(function (key) {
|
|
console.log('\t'+key+': '+err[key]);
|
|
});
|
|
console.error(err.stack);
|
|
});
|
|
process.on('rejectionHandled', function (msg) {
|
|
console.error('[rejectionHandled]');
|
|
console.error(msg);
|
|
});
|
|
};
|