From e6bfd5d70b0feede2ebf2ce76d5e43c6ccb2c0a3 Mon Sep 17 00:00:00 2001 From: Zachary Carter <zack.carter@gmail.com> Date: Tue, 22 May 2012 11:55:28 -0700 Subject: [PATCH] Initial commit of new router layer --- bin/browserid | 31 ---------- bin/router | 132 +++++++++++++++++++++++++++++++++++++++++ config/local.json | 1 + lib/configuration.js | 4 ++ lib/http_forward.js | 2 +- lib/wsapi.js | 8 ++- scripts/run_locally.js | 6 +- 7 files changed, 149 insertions(+), 35 deletions(-) create mode 100755 bin/router diff --git a/bin/browserid b/bin/browserid index 03fa955e4..b0109170d 100755 --- a/bin/browserid +++ b/bin/browserid @@ -34,18 +34,6 @@ app = express.createServer(); logger.info("browserid server starting up"); -// verify that we have a keysigner configured -if (!config.get('keysigner_url')) { - logger.error('missing required configuration - url for the keysigner (KEYSIGNER_URL in env)'); - process.exit(1); -} - -// verify that we have a dbwriter configured -if (!config.get('dbwriter_url')) { - logger.error('missing required configuration - url for the dbwriter (DBWRITER_URL in env)'); - process.exit(1); -} - // NOTE: ordering of middleware registration is important in this file, it is the // order in which middleware will be invoked as requests are processed. @@ -102,25 +90,6 @@ app.use(function(req, resp, next) { next(); }); -// #5 - redirection! redirect requests to the "verifier" or to the "dbwriter" -// processes -if (config.get('verifier_url')) { - var verifier_url = urlparse(config.get('verifier_url')).validate().normalize(); - app.use(function(req, res, next) { - if (/^\/verify$/.test(req.url)) { - forward( - verifier_url, req, res, - function(err) { - if (err) { - logger.error("error forwarding request:", err); - } - }); - } else { - return next(); - } - }); -} - // #6 - verify all JSON responses are objects - prevents regression on issue #217 app.use(function(req, resp, next) { var realRespJSON = resp.json; diff --git a/bin/router b/bin/router new file mode 100755 index 000000000..fa000fadf --- /dev/null +++ b/bin/router @@ -0,0 +1,132 @@ +#!/usr/bin/env node + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const +fs = require('fs'), +path = require('path'), +url = require('url'), +http = require('http'); +urlparse = require('urlparse'), +express = require('express'); + +const +wsapi = require('../lib/wsapi.js'), +config = require('../lib/configuration.js'), +heartbeat = require('../lib/heartbeat.js'), +logger = require('../lib/logging.js').logger, +forward = require('../lib/http_forward').forward, +shutdown = require('../lib/shutdown'); + + +var app = undefined; + +app = express.createServer(); + +logger.info("router server starting up"); + +// verify that we have a keysigner configured +if (!config.get('keysigner_url')) { + logger.error('missing required configuration - url for the keysigner (KEYSIGNER_URL in env)'); + process.exit(1); +} + +// verify that we have a dbwriter configured +if (!config.get('dbwriter_url')) { + logger.error('missing required configuration - url for the dbwriter (DBWRITER_URL in env)'); + process.exit(1); +} + +// verify that we have a browserid configured +if (!config.get('browserid_url')) { + logger.error('missing required configuration - url for browserid (BROWSERID_URL in env)'); + process.exit(1); +} + +// NOTE: ordering of middleware registration is important in this file, it is the +// order in which middleware will be invoked as requests are processed. + +// #1 - Setup health check / heartbeat middleware. +// This is in front of logging on purpose. see issue #537 +heartbeat.setup(app); + +// #2 - logging! all requests other than __heartbeat__ are logged +app.use(express.logger({ + format: config.get('express_log_format'), + stream: { + write: function(x) { + logger.info(typeof x === 'string' ? x.trim() : x); + } + } +})); + +// limit all content bodies to 10kb, at which point we'll forcefully +// close down the connection. +app.use(express.limit("10kb")); + +var statsd_config = config.get('statsd'); +if (statsd_config && statsd_config.enabled) { + logger_statsd = require("connect-logger-statsd"); + app.use(logger_statsd({ + host: statsd_config.hostname || "localhost", + port: statsd_config.port || 8125, + prefix: statsd_config.prefix || "browserid.router." + })); +} + +// Add Strict-Transport-Security headers if we're serving over SSL +if (config.get('scheme') == 'https') { + app.use(function(req, resp, next) { + // expires in 30 days, include subdomains like www + resp.setHeader("Strict-Transport-Security", "max-age=2592000; includeSubdomains"); + next(); + }); +} + +// redirect requests to the "verifier" processes +if (config.get('verifier_url')) { + var verifier_url = urlparse(config.get('verifier_url')).validate().normalize(); + app.use(function(req, res, next) { + if (/^\/verify$/.test(req.url)) { + forward( + verifier_url, req, res, + function(err) { + if (err) { + logger.error("error forwarding request:", err); + } + }); + } else { + return next(); + } + }); +} + +// handle /wsapi writes +wsapi.setup({ + router_mode: true, + forward_writes: urlparse(config.get('dbwriter_url')).validate().normalize().originOnly() +}, app); + +// Forward all leftover requests to browserid +var browserid_url = urlparse(config.get('browserid_url')).validate().normalize().originOnly(); +app.use(function(req, res, next) { + forward( + browserid_url+req.url, req, res, + function(err) { + if (err) { + logger.error("error forwarding request:", err); + } + }); +}); + +// #11 - calls to /code_update from localhost will restart the daemon, +// this feature is not externally accessible and is only used by +// the update logic +shutdown.installUpdateHandler(app, function () {}); + +var bindTo = config.get('bind_to'); +app.listen(bindTo.port, bindTo.host, function(conn) { + logger.info("running on http://" + app.address().address + ":" + app.address().port); +}); diff --git a/config/local.json b/config/local.json index d437b1691..0c89dc8f1 100644 --- a/config/local.json +++ b/config/local.json @@ -4,6 +4,7 @@ "dbwriter": { "bind_to": { "port": 10004 } }, "proxy": { "bind_to": { "port": 10006 } }, "browserid": { "bind_to": { "port": 10002 } }, + "router": { "bind_to": { "port": 10007 } }, "use_minified_resources": false, "database": { "driver": "json" diff --git a/lib/configuration.js b/lib/configuration.js index 14d7e5e5e..4c4957e57 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -198,6 +198,10 @@ var conf = module.exports = convict({ format: 'string?', env: 'DBWRITER_URL' }, + browserid_url: { + format: 'string?', + env: 'BROWSERID_URL' + }, process_type: 'string', email_to_console: 'boolean = false', declaration_of_support_timeout_ms: { diff --git a/lib/http_forward.js b/lib/http_forward.js index d88cbd85d..d9408f997 100644 --- a/lib/http_forward.js +++ b/lib/http_forward.js @@ -39,7 +39,7 @@ exports.forward = function(dest, req, res, cb) { var preq = m.request({ host: u.hostname, port: u.port, - path: u.pathname, + path: u.path, method: req.method, agent: false }, function(pres) { diff --git a/lib/wsapi.js b/lib/wsapi.js index 9ec102464..7b12c40e6 100644 --- a/lib/wsapi.js +++ b/lib/wsapi.js @@ -200,6 +200,9 @@ exports.setup = function(options, app) { return bodyParser(req, resp, function() { next(); }); + } else if (options.router_mode) { + // skip wsapi request, let browserid middleware handle forwards + return next(); } else { // this is not a forwarded operation, perform full parsing and validation return cookieParser(req, resp, function() { @@ -324,7 +327,10 @@ exports.setup = function(options, app) { app.use(function(req, resp, next) { var purl = url.parse(req.url); - if (purl.pathname.substr(0, WSAPI_PREFIX.length) === WSAPI_PREFIX) { + if (options.router_mode) { + // skip wsapi request, let browserid middleware handle forwards + return next(); + } else if (purl.pathname.substr(0, WSAPI_PREFIX.length) === WSAPI_PREFIX) { const operation = purl.pathname.substr(WSAPI_PREFIX.length); // the fake_verification wsapi is implemented elsewhere. diff --git a/scripts/run_locally.js b/scripts/run_locally.js index fa12dca0b..0d7f2adbb 100755 --- a/scripts/run_locally.js +++ b/scripts/run_locally.js @@ -29,7 +29,8 @@ var daemonsToRun = { HOST: HOST }, proxy: { }, - browserid: { } + browserid: { }, + router: { } }; // route outbound HTTP through our in-tree proxy to always test said codepath @@ -53,8 +54,9 @@ process.env['DBWRITER_URL'] = 'http://' + HOST + ":10004"; process.env['BROWSERID_URL'] = 'http://' + HOST + ":10002"; process.env['VERIFIER_URL'] = 'http://' + HOST + ":10000/verify"; process.env['KEYSIGNER_URL'] = 'http://' + HOST + ":10003"; +process.env['ROUTER_URL'] = 'http://' + HOST + ":10007"; -process.env['URL'] = process.env['BROWSERID_URL']; +process.env['URL'] = process.env['ROUTER_URL']; // if the environment is a 'test_' environment, then we'll use an // ephemeral database -- GitLab