'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); }); };