From 96b7c9bb659c34dabe295515ae176c6a8d9773df Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 17 Nov 2015 08:18:56 +0000 Subject: [PATCH] clustering works (re-started work for standalone) --- master.js => boot/master.js | 25 +-- boot/worker.js | 113 ++++++++++++ etc/init/caddy.conf | 23 +++ lib/api-server.js | 13 +- lib/schemes-config.js | 3 - lib/vhost-server.js | 343 ------------------------------------ lib/worker.js | 41 +++-- package.json | 2 +- walnut.js | 4 +- worker.js | 76 -------- 10 files changed, 182 insertions(+), 461 deletions(-) rename master.js => boot/master.js (77%) create mode 100644 boot/worker.js create mode 100644 etc/init/caddy.conf delete mode 100644 lib/vhost-server.js delete mode 100644 worker.js diff --git a/master.js b/boot/master.js similarity index 77% rename from master.js rename to boot/master.js index 0664fda..d2d2a05 100644 --- a/master.js +++ b/boot/master.js @@ -3,15 +3,15 @@ // TODO if RAM is very low we should not fork at all, // but use a different process altogether -console.log('pid:', process.pid); -console.log('title:', process.title); -console.log('arch:', process.arch); -console.log('platform:', process.platform); -console.log('\n\n\n[MASTER] Welcome to WALNUT!'); +console.info('pid:', process.pid); +console.info('title:', process.title); +console.info('arch:', process.arch); +console.info('platform:', process.platform); +console.info('\n\n\n[MASTER] Welcome to WALNUT!'); var cluster = require('cluster'); var path = require('path'); -var minWorkers = 2; +//var minWorkers = 2; var numCores = 1; // Math.max(minWorkers, require('os').cpus().length); var workers = []; var caddypath = '/usr/local/bin/caddy'; @@ -23,8 +23,8 @@ var conf = { // TODO externalInsecurePort? , locked: false // TODO XXX , ipcKey: null -, caddyfilepath: path.join(__dirname, 'Caddyfile') -, sitespath: path.join(__dirname, 'sites-enabled') +, caddyfilepath: path.join(__dirname, '..', 'Caddyfile') +, sitespath: path.join(__dirname, '..', 'sites-enabled') }; var state = {}; var caddy; @@ -45,9 +45,10 @@ cluster.on('online', function (worker) { var certPaths = [path.join(__dirname, 'certs', 'live')]; var info; - console.log('[MASTER] Worker ' + worker.process.pid + ' is online'); + console.info('[MASTER] Worker ' + worker.process.pid + ' is online'); fork(); + // TODO communicate config with environment vars? info = { type: 'com.daplie.walnut.init' , conf: { @@ -72,7 +73,7 @@ cluster.on('online', function (worker) { // calls init if init has not been called state.caddy = caddy; state.workers = workers; - require('./lib/master').touch(conf, state).then(function () { + require('../lib/master').touch(conf, state).then(function () { info.type = 'com.daplie.walnut.webserver.onrequest'; info.conf.ipcKey = conf.ipcKey; info.conf.memstoreSock = conf.memstoreSock; @@ -84,7 +85,7 @@ cluster.on('online', function (worker) { }); cluster.on('exit', function (worker, code, signal) { - console.log('[MASTER] Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal); + console.info('[MASTER] Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal); workers = workers.map(function (w) { if (worker !== w) { @@ -102,7 +103,7 @@ cluster.on('exit', function (worker, code, signal) { fork(); if (useCaddy) { - caddy = require('./lib/spawn-caddy').create(conf); + caddy = require('../lib/spawn-caddy').create(conf); // relies on { localPort, locked } caddy.spawn(conf); } diff --git a/boot/worker.js b/boot/worker.js new file mode 100644 index 0000000..146d931 --- /dev/null +++ b/boot/worker.js @@ -0,0 +1,113 @@ +'use strict'; + +module.exports.create = function (opts) { + var id = '0'; + + function createAndBindServers(message, cb) { + var msg = message.conf; + + require('../lib/local-server').create(msg.certPaths, msg.localPort, function (err, webserver) { + if (err) { + console.error('[ERROR] worker.js'); + console.error(err.stack); + throw err; + } + + console.info("#" + id + " Listening on " + msg.protocol + "://" + webserver.address().address + ":" + webserver.address().port, '\n'); + + return cb(webserver); + }); + + // TODO conditional if 80 is being served by caddy + require('../lib/insecure-server').create(msg.externalPort, msg.insecurePort); + } + + // + // Worker Mode + // + function waitForConfig(message) { + if ('com.daplie.walnut.init' !== message.type) { + console.warn('[Worker] 0 got unexpected message:'); + console.warn(message); + return; + } + + process.removeListener('message', waitForConfig); + + // NOTE: this callback must return a promise for an express app + createAndBindServers(message, function (webserver) { + var PromiseA = require('bluebird'); + return new PromiseA(function (resolve) { + function initWebServer(srvmsg) { + if ('com.daplie.walnut.webserver.onrequest' !== srvmsg.type) { + console.warn('[Worker] 1 got unexpected message:'); + console.warn(srvmsg); + return; + } + + process.removeListener('message', initWebServer); + + resolve(require('../lib/worker').create(webserver, srvmsg)); + } + + process.send({ type: 'com.daplie.walnut.webserver.listening' }); + process.on('message', initWebServer); + }).then(function (app) { + console.info('[Worker Ready]'); + return app; + }); + }); + } + + // + // Standalone Mode + // + if (opts) { + // NOTE: this callback must return a promise for an express app + createAndBindServers(opts, function (webserver) { + var PromiseA = require('bluebird'); + return new PromiseA(function (resolve) { + opts.getConfig(function (srvmsg) { + resolve(require('../lib/worker').create(webserver, srvmsg)); + }); + }).then(function (app) { + console.info('[Standalone Ready]'); + return app; + }); + }); + } else { + // 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); + }); +}; diff --git a/etc/init/caddy.conf b/etc/init/caddy.conf new file mode 100644 index 0000000..4535e67 --- /dev/null +++ b/etc/init/caddy.conf @@ -0,0 +1,23 @@ +# sudo rsync -av etc/init/caddy.conf /etc/init/caddy.conf + +description "Caddy Server" +version "1.0" +author "AJ ONeal" + +# Upstart has nothing in $PATH by default +env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# Keep the server running on crash or machine reboot +respawn +respawn limit 10 120 +start on runlevel [2345] + +# Start the server using spark and redirect output to log files +script + DATE=`date '+%F_%H-%M-%S'` + cd /srv/walnut + # exec /usr/local/bin/caddy -conf /srv/walnut/Caddyfile -pidfile /tmp/caddy.pid \ + exec start-stop-daemon --start --pidfile /tmp/caddy.pid --exec /usr/local/bin/caddy -- -conf /srv/walnut/Caddyfile --pidfile /tmp/caddy.pid \ + > "./logs/access.caddy.${DATE}.log" \ + 2> "./logs/error.caddy.${DATE}.log" +end script diff --git a/lib/api-server.js b/lib/api-server.js index 3dfca8a..50ae29c 100644 --- a/lib/api-server.js +++ b/lib/api-server.js @@ -14,17 +14,13 @@ module.exports.create = function (conf, deps, app) { var PromiseA = require('bluebird'); var path = require('path'); - console.log(route); // TODO needs some version stuff (which would also allow hot-loading of updates) // TODO version could be tied to sha256sum var pkgpath = path.join(conf.apipath, (route.api.package || route.api.id), (route.api.version || '')); - console.log('pkgpath'); - console.log(pkgpath); - return new PromiseA(function (resolve, reject) { try { - route.route = require(pkgpath).create(conf, deps.app, app); + route.route = require(pkgpath).create(conf, deps, app); } catch(e) { reject(e); return; @@ -37,9 +33,6 @@ module.exports.create = function (conf, deps, app) { function api(req, res, next) { var apps; - console.log('hostname', req.hostname); - console.log('headers', req.headers); - if (!vhostsMap[req.hostname]) { // TODO keep track of match-only vhosts, such as '*.example.com', // separate from exact matches @@ -71,14 +64,10 @@ module.exports.create = function (conf, deps, app) { }); if (!apps) { - console.log('No apps to try for this hostname'); - console.log(vhostsMap[req.hostname]); next(); return; } - //console.log(apps); - function nextify(err) { var route; diff --git a/lib/schemes-config.js b/lib/schemes-config.js index aa3772b..a261831 100644 --- a/lib/schemes-config.js +++ b/lib/schemes-config.js @@ -109,8 +109,6 @@ function getVhostsMap(config) { vhosts.sort(sortApps); vhosts.forEach(function (domain) { - console.log(domain.hostname, domain.pathname, domain.dirname); - if (!vhostsMap[domain.hostname]) { vhostsMap[domain.hostname] = { pathnamesMap: {}, pathnames: [] }; } @@ -129,7 +127,6 @@ function getVhostsMap(config) { module.exports.deserialize = deserialize; module.exports.getVhostsMap = getVhostsMap; module.exports.create = function (db) { - console.log('[DB -1]'); var wrap = require('dbwrap'); var dir = [ diff --git a/lib/vhost-server.js b/lib/vhost-server.js deleted file mode 100644 index cf26ae6..0000000 --- a/lib/vhost-server.js +++ /dev/null @@ -1,343 +0,0 @@ -'use strict'; - -module.exports.create = function (securePort, vhostsdir) { - var PromiseA = require('bluebird').Promise; - var serveStatic; - var fs = require('fs'); - var path = require('path'); - var dummyCerts; - var loopbackToken = require('crypto').randomBytes(32).toString('hex'); - - function handleAppScopedError(tag, domaininfo, req, res, fn) { - function next(err) { - if (!err) { - fn(req, res); - return; - } - - if (res.headersSent) { - console.error('[ERROR] handleAppScopedError headersSent'); - console.log(err); - console.log(err.stack); - return; - } - - console.error('[ERROR] handleAppScopedError'); - console.log(err); - console.log(err.stack); - - res.writeHead(500); - res.end( - "" - + "" - + '' - + "" - + "" - + "
"
-        + ""
-        + "Method: " + encodeURI(req.method)
-        + '\n'
-        + "Hostname: " + encodeURI(domaininfo.hostname)
-        + '\n'
-        + "App: " + encodeURI(domaininfo.pathname ? (domaininfo.pathname + '/') : '')
-        + '\n'
-        + "Route: " + encodeURI(req.url)//.replace(/^\//, '')
-        + '\n'
-          // TODO better sanatization
-        + 'Error: '  + (err.message || err.toString()).replace(/"
-        + "
" - + "" - + "" - ); - } - - return next; - } - - function createPromiseApps(secureServer) { - return new PromiseA(function (resolve) { - var forEachAsync = require('foreachasync').forEachAsync.create(PromiseA); - var connect = require('connect'); - // TODO make lazy - var app = connect().use(require('compression')()); - var vhost = require('vhost'); - - var domainMergeMap = {}; - var domainMerged = []; - - function getDomainInfo(apppath) { - var parts = apppath.split(/[#%]+/); - var hostname = parts.shift(); - var pathname = parts.join('/').replace(/\/+/g, '/').replace(/^\//, ''); - - return { - hostname: hostname - , pathname: pathname - , dirpathname: parts.join('#') - , dirname: apppath - , isRoot: apppath === hostname - }; - } - - function loadDomainMounts(domaininfo) { - var connectContext = {}; - var appContext; - - // should order and group by longest domain, then longest path - if (!domainMergeMap[domaininfo.hostname]) { - // create an connect / express app exclusive to this domain - // TODO express?? - domainMergeMap[domaininfo.hostname] = { - hostname: domaininfo.hostname - , apps: connect() - , mountsMap: {} - }; - domainMerged.push(domainMergeMap[domaininfo.hostname]); - } - - if (domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]) { - return; - } - - console.log('[log] [once] Preparing mount for', domaininfo.hostname + '/' + domaininfo.dirpathname); - domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] = function (req, res, next) { - res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload'); - function loadThatApp() { - var time = Date.now(); - - console.log('[log] LOADING "' + domaininfo.hostname + '/' + domaininfo.pathname + '"', req.url); - return getAppContext(domaininfo).then(function (localApp) { - console.info((Date.now() - time) + 'ms Loaded ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname); - //if (localApp.arity >= 2) { /* connect uses .apply(null, arguments)*/ } - if ('function' !== typeof localApp) { - localApp = getDummyAppContext(null, "[ERROR] no connect-style export from " + domaininfo.dirname); - } - - function fourohfour(req, res) { - res.writeHead(404); - res.end( - "" - + "" - + '' - + "" - + "" - + "Cannot " - + encodeURI(req.method) - + " 'https://" - + encodeURI(domaininfo.hostname) - + '/' - + encodeURI(domaininfo.pathname ? (domaininfo.pathname + '/') : '') - + encodeURI(req.url.replace(/^\//, '')) - + "'" - + "
" - + "
" - + "Domain: " + encodeURI(domaininfo.hostname) - + "
" - + "App: " + encodeURI(domaininfo.pathname) - + "
" - + "Route : " + encodeURI(req.url) - + "" - + "" - ); - } - - // Note: pathname should NEVER have a leading '/' on its own - // we always add it explicitly - function localAppWrapped(req, res) { - console.log('[debug]', domaininfo.hostname + '/' + domaininfo.pathname, req.url); - localApp(req, res, handleAppScopedError('localApp', domaininfo, req, res, fourohfour)); - } - try { - var localConnect = connect(); - localConnect.use(require('connect-query')()); - localConnect.use(localAppWrapped); - domainMergeMap[domaininfo.hostname].apps.use('/' + domaininfo.pathname, localConnect); - return localConnect; - } catch(e) { - console.error('[ERROR] ' - + domaininfo.hostname + ':' + securePort - + '/' + domaininfo.pathname - ); - console.error(e); - // TODO this may not work in web apps (due to 500), probably okay - res.writeHead(500); - res.end('{ "error": { "message": "[ERROR] could not load ' - + encodeURI(domaininfo.hostname) + ':' + securePort + '/' + encodeURI(domaininfo.pathname) - + 'or default error app." } }'); - } - }); - } - - function suckItDubDubDub(req, res) { - var newLoc = 'https://' + (req.headers.host||'').replace(/^www\./) + req.url; - res.statusCode = 301; - res.setHeader('Location', newLoc); - res.end(""); - } - - function nextify() { - if (!appContext) { - appContext = loadThatApp(); - } - - if (!appContext.then) { - appContext(req, res, next); - } else { - appContext.then(function (localConnect) { - appContext = localConnect; - appContext(req, res, next); - }); - } - } - - if (!serveStatic) { - serveStatic = require('serve-static'); - } - - if (!connectContext.static) { - console.log('[static]', path.join(vhostsdir, domaininfo.dirname, 'public')); - connectContext.static = serveStatic(path.join(vhostsdir, domaininfo.dirname, 'public')); - } - - if (/^www\./.test(req.headers.host)) { - if (/\.appcache\b/.test(req.url)) { - res.setHeader('Content-Type', 'text/cache-manifest'); - res.end('CACHE MANIFEST\n\n# v0__DELETE__CACHE__MANIFEST__\n\nNETWORK:\n*'); - return; - } - suckItDubDubDub(req, res); - return; - } - - if (/^\/api\//.test(req.url)) { - nextify(); - return; - } - - connectContext.static(req, res, nextify); - }; - domainMergeMap[domaininfo.hostname].apps.use( - '/' + domaininfo.pathname - , domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] - ); - - return PromiseA.resolve(); - } - - function readNewVhosts() { - return fs.readdirSync(vhostsdir).filter(function (node) { - // not a hidden or private file - return '.' !== node[0] && '_' !== node[0]; - }).map(getDomainInfo).sort(function (a, b) { - var hlen = b.hostname.length - a.hostname.length; - var plen = b.pathname.length - a.pathname.length; - - // A directory could be named example.com, example.com# example.com## - // to indicate order of preference (for API addons, for example) - var dlen = b.dirname.length - a.dirname.length; - if (!hlen) { - if (!plen) { - return dlen; - } - return plen; - } - return plen; - }); - } - - function getDummyAppContext(err, msg) { - console.error('[ERROR] getDummyAppContext'); - console.error(err); - console.error(msg); - return function (req, res) { - res.writeHead(500); - res.end('{ "error": { "message": "' + msg + '" } }'); - }; - } - - function getLoopbackApp() { - return function (req, res) { - res.setHeader('Content-Type', 'application/json; charset=utf-8'); - res.end(JSON.stringify({ "success": true, "token": loopbackToken })); - }; - } - - function getAppContext(domaininfo) { - var localApp; - - if ('loopback.daplie.invalid' === domaininfo.dirname) { - return getLoopbackApp(); - } - - try { - // TODO live reload required modules - localApp = require(path.join(vhostsdir, domaininfo.dirname, 'app.js')); - if (localApp.create) { - // TODO read local config.yml and pass it in - // TODO pass in websocket - localApp = localApp.create(secureServer, { - dummyCerts: dummyCerts - , hostname: domaininfo.hostname - , port: securePort - , url: domaininfo.pathname - }); - - if (!localApp) { - localApp = getDummyAppContext(null, "[ERROR] no app was returned by app.js for " + domaininfo.dirname); - } - } - - if (!localApp.then) { - localApp = PromiseA.resolve(localApp); - } else { - return localApp.catch(function (e) { - console.error("[ERROR] initialization failed during create() for " + domaininfo.dirname); - console.error(e); - throw e; - //return getDummyAppContext(e, "[ERROR] initialization failed during create() for " + domaininfo.dirname); - }); - } - } catch(e) { - localApp = getDummyAppContext(e, "[ERROR] could not load app.js for " + domaininfo.dirname); - localApp = PromiseA.resolve(localApp); - } - - return localApp; - } - - function loadDomainVhosts() { - domainMerged.forEach(function (domainApp) { - if (domainApp._loaded) { - return; - } - - console.log('[log] [once] Loading all mounts for ' + domainApp.hostname); - domainApp._loaded = true; - app.use(vhost(domainApp.hostname, domainApp.apps)); - app.use(vhost('www.' + domainApp.hostname, function (req, res) { - res.send({ - error: { - message: "this is an api. ain't no www belong here" - , code: "E_WWW" - } - }); - })); - }); - } - - - // TODO pre-cache these once the server has started? - // return forEachAsync(rootDomains, loadCerts); - // TODO load these even more lazily - return forEachAsync(readNewVhosts(), loadDomainMounts).then(loadDomainVhosts).then(function () { - console.log('[log] TODO fix and use hotload'); - //app.use(hotloadApp); - resolve(app); - return; - }); - }); - } - - return { create: createPromiseApps }; -}; diff --git a/lib/worker.js b/lib/worker.js index 17968a5..738c8eb 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -37,6 +37,14 @@ module.exports.create = function (webserver, info, state) { }); var cstore = require('cluster-store'); + if (info.conf.trustProxy) { + console.info('[Trust Proxy]'); + app.set('trust proxy', ['loopback']); + //app.set('trust proxy', function (ip) { console.log('[ip]', ip); return true; }); + } else { + console.info('[DO NOT trust proxy]'); + } + /* function unlockDevice(conf, state) { return require('./lib/unlock-device').create().then(function (result) { @@ -65,7 +73,6 @@ module.exports.create = function (webserver, info, state) { // I guess I just needs letsencrypt function scrubTheDub(req, res, next) { - console.log('[no-www]', req.method, req.url); var host = req.hostname; if (!host || 'string' !== typeof host) { @@ -84,11 +91,23 @@ module.exports.create = function (webserver, info, state) { require('./no-www').scrubTheDub(req, res); } - if (info.trustProxy) { - app.set('trust proxy', ['loopback']); - //app.set('trust proxy', function (ip) { ... }); + function caddyBugfix(req, res, next) { + // workaround for Caddy + // https://github.com/mholt/caddy/issues/341 + if (app.get('trust proxy')) { + if (req.headers['x-forwarded-proto']) { + req.headers['x-forwarded-proto'] = (req.headers['x-forwarded-proto'] || '').split(/,\s+/g)[0] || undefined; + } + if (req.headers['x-forwarded-host']) { + req.headers['x-forwarded-host'] = (req.headers['x-forwarded-host'] || '').split(/,\s+/g)[0] || undefined; + } + } + + next(); } + app.use('/', scrubTheDub); + app.use('/', caddyBugfix); return PromiseA.all([ cstore.create({ @@ -137,7 +156,6 @@ module.exports.create = function (webserver, info, state) { */ function handleApi(req, res, next) { - console.log('[API]', req.method, req.url); var myApp; if (!/^\/api/.test(req.url)) { @@ -155,8 +173,8 @@ module.exports.create = function (webserver, info, state) { if (apiHandler) { if (apiHandler.then) { - apiHandler.then(function (app) { - app(req, res, next); + apiHandler.then(function (myApp) { + myApp(req, res, next); }); return; } @@ -167,12 +185,16 @@ module.exports.create = function (webserver, info, state) { // apiHandler = require('./vhost-server').create(info.localPort, vhostsdir).create(webserver, app) myApp = express(); + if (app.get('trust proxy')) { + myApp.set('trust proxy', app.get('trust proxy')); + } apiHandler = require('./api-server').create( { apppath: '../packages/apps/' , apipath: '../packages/apis/' , vhostsMap: vhostsMap , server: webserver , externalPort: info.externalPort + , apiPrefix: '/api' } , { app: myApp , memstore: memstore @@ -185,11 +207,6 @@ module.exports.create = function (webserver, info, state) { } ).api; - // TODO - // X-Forwarded-For - // X-Forwarded-Proto - console.log('api server', req.hostname, req.secure, req.ip); - apiHandler(req, res, next); } diff --git a/package.json b/package.json index 67673a2..c8f8227 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "escape-string-regexp": "1.x", "etag": "^1.5.1", "express": "4.x", - "express-lazy": "1.x", + "express-lazy": "^1.1.1", "express-session": "^1.11.3", "finalhandler": "^0.3.4", "foreachasync": "5.x", diff --git a/walnut.js b/walnut.js index fafacff..043c856 100644 --- a/walnut.js +++ b/walnut.js @@ -20,7 +20,7 @@ Math.random = function () { }; if (cluster.isMaster) { - require('./master'); + require('./boot/master'); } else { - require('./worker'); + require('./boot/worker').create(null); } diff --git a/worker.js b/worker.js deleted file mode 100644 index bdc9ec8..0000000 --- a/worker.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -var cluster = require('cluster'); -var id = cluster.worker.id.toString(); - -function waitForInit(message) { - if ('com.daplie.walnut.init' !== message.type) { - console.warn('[Worker] 0 got unexpected message:'); - console.warn(message); - return; - } - - var msg = message.conf; - process.removeListener('message', waitForInit); - require('./lib/local-server').create(msg.certPaths, msg.localPort, function (err, webserver) { - if (err) { - console.error('[ERROR] worker.js'); - console.error(err.stack); - throw err; - } - - console.log("#" + id + " Listening on " + msg.protocol + "://" + webserver.address().address + ":" + webserver.address().port, '\n'); - - var PromiseA = require('bluebird'); - return new PromiseA(function (resolve) { - function initWebServer(srvmsg) { - if ('com.daplie.walnut.webserver.onrequest' !== srvmsg.type) { - console.warn('[Worker] 1 got unexpected message:'); - console.warn(srvmsg); - return; - } - - process.removeListener('message', initWebServer); - resolve(require('./lib/worker').create(webserver, srvmsg)); - } - process.send({ type: 'com.daplie.walnut.webserver.listening' }); - process.on('message', initWebServer); - }); - }); - - // TODO conditional if 80 is being served by caddy - require('./lib/insecure-server').create(msg.externalPort, msg.insecurePort); -} - -// We have to wait to get the configuration from the master process -// before we can start our webserver -console.log('[Worker #' + id + '] online!'); -process.on('message', waitForInit); - -// -// Debugging -// -process.on('exit', function (code) { - // only sync code can run here - console.log('uptime:', process.uptime()); - console.log(process.memoryUsage()); - console.log('[exit] process.exit() has been called (or master has killed us).'); - console.log(code); -}); -process.on('beforeExit', function (msg) { - // async can be scheduled here - console.log('[beforeExit] Event Loop is empty. Process will end.'); - console.log(msg); -}); -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); -});