walnut.js/boot/worker.js

162 lines
4.8 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]');
console.error(Object.keys(err));
console.error(err);
console.error(err.stack);
});
process.on('rejectionHandled', function (msg) {
console.error('[rejectionHandled]');
console.error(msg);
});
};