From aa11620b8e0ab27a5a554c0cdeb7ce5f41db07e0 Mon Sep 17 00:00:00 2001 From: Lloyd Brookes Date: Mon, 30 May 2016 00:32:51 +0100 Subject: [PATCH] proxy connection errors no longer crash the server, fixes #37 --- bin/cli.js | 11 ++---- example/rewrite/.local-web-server.json | 1 + example/rewrite/index.html | 2 + lib/local-web-server.js | 68 +++++++++++----------------------- lib/middleware.js | 19 +++++----- 5 files changed, 37 insertions(+), 64 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 23e8554..3c358a7 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -42,15 +42,10 @@ if (options.misc.help) { const _use = app.use app.use = x => _use.call(app, convert(x)) - // app.use((ctx, next) => { - // return next() - // .catch(err => { - // console.error('FUKKK', err) - // }) - // }) - app.on('error', err => { - console.error('ERROROO', err) + if (options.server['log-format']) { + console.error(ansi.format(err.message, 'red')) + } }) const ws = localWebServer({ diff --git a/example/rewrite/.local-web-server.json b/example/rewrite/.local-web-server.json index ae46592..1eb9003 100644 --- a/example/rewrite/.local-web-server.json +++ b/example/rewrite/.local-web-server.json @@ -2,6 +2,7 @@ "rewrite": [ { "from": "/css/*", "to": "/build/styles/$1" }, { "from": "/npm/*", "to": "http://registry.npmjs.org/$1" }, + { "from": "/broken/*", "to": "http://localhost:9999" }, { "from": "/:user/repos/:name", "to": "https://api.github.com/repos/:user/:name" } ] } diff --git a/example/rewrite/index.html b/example/rewrite/index.html index 2711101..02dcfd3 100644 --- a/example/rewrite/index.html +++ b/example/rewrite/index.html @@ -9,6 +9,7 @@ "rewrite": [ { "from": "/css/*", "to": "/build/styles/$1" }, { "from": "/npm/*", "to": "http://registry.npmjs.org/$1" }, + { "from": "/broken/*", "to": "http://localhost:9999" }, { "from": "/:user/repos/:name", "to": "https://api.github.com/repos/:user/:name" } ] } @@ -18,5 +19,6 @@ diff --git a/lib/local-web-server.js b/lib/local-web-server.js index b766dfb..8d16a4f 100644 --- a/lib/local-web-server.js +++ b/lib/local-web-server.js @@ -66,7 +66,6 @@ function localWebServer (options) { options.mocks = arrayify(options.mocks) const debug = require('debug')('local-web-server') - const Koa = require('koa') const convert = require('koa-convert') const cors = require('kcors') const _ = require('koa-route') @@ -74,19 +73,13 @@ function localWebServer (options) { const bodyParser = require('koa-bodyparser') const mw = require('./middleware') - let middlewares = [] - - // const app = new Koa() - // const _use = app.use - // app.use = x => _use.call(app, convert(x)) + let middlewareStack = [] /* CORS: allow from any origin */ - // app.use(cors()) - middlewares.push(cors()) + middlewareStack.push(cors()) /* pretty print JSON */ - // app.use(json()) - middlewares.push(json()) + middlewareStack.push(json()) /* rewrite rules */ if (options.rewrite && options.rewrite.length) { @@ -95,53 +88,45 @@ function localWebServer (options) { /* `to` address is remote if the url specifies a host */ if (url.parse(route.to).host) { debug('proxy rewrite', `${route.from} -> ${route.to}`) - // app.use(_.all(route.from, mw.proxyRequest(route))) - middlewares.push(_.all(route.from, mw.proxyRequest(route))) + middlewareStack.push(_.all(route.from, mw.proxyRequest(route))) } else { const rewrite = require('koa-rewrite') const rmw = rewrite(route.from, route.to) rmw._name = 'rewrite' - // app.use(rmw) - middlewares.push(rmw) + middlewareStack.push(rmw) } } }) } /* must come after rewrite. See https://github.com/nodejitsu/node-http-proxy/issues/180. */ - // app.use(bodyParser()) - middlewares.push(bodyParser()) + middlewareStack.push(bodyParser()) /* path blacklist */ if (options.forbid.length) { debug('forbid', options.forbid.join(', ')) - // app.use(mw.blacklist(options.forbid)) - middlewares.push(mw.blacklist(options.forbid)) + middlewareStack.push(mw.blacklist(options.forbid)) } /* cache */ if (!options['no-cache']) { const conditional = require('koa-conditional-get') const etag = require('koa-etag') - // app.use(conditional()) - // app.use(etag()) - middlewares.push(conditional()) - middlewares.push(etag()) + middlewareStack.push(conditional()) + middlewareStack.push(etag()) } /* mime-type overrides */ if (options.mime) { debug('mime override', JSON.stringify(options.mime)) - // app.use(mw.mime(options.mime)) - middlewares.push(mw.mime(options.mime)) + middlewareStack.push(mw.mime(options.mime)) } /* compress response */ if (options.compress) { const compress = require('koa-compress') debug('compression', 'enabled') - // app.use(compress()) - middlewares.push(compress()) + middlewareStack.push(compress()) } /* Logging */ @@ -151,15 +136,12 @@ function localWebServer (options) { if (!log.format) { const streamLogStats = require('stream-log-stats') log.options.stream = streamLogStats({ refreshRate: 500 }) - // app.use(morgan('common', log.options)) - middlewares.push(morgan('common', log.options)) + middlewareStack.push(morgan('common', log.options)) } else if (log.format === 'logstalgia') { morgan.token('date', logstalgiaDate) - // app.use(morgan('combined', log.options)) - middlewares.push(morgan('combined', log.options)) + middlewareStack.push(morgan('combined', log.options)) } else { - // app.use(morgan(log.format, log.options)) - middlewares.push(morgan(log.format, log.options)) + middlewareStack.push(morgan(log.format, log.options)) } } @@ -170,15 +152,13 @@ function localWebServer (options) { } if (mock.responses) { - // app.use(mw.mockResponses(mock.route, mock.responses)) - middlewares.push(mw.mockResponses(mock.route, mock.responses)) + middlewareStack.push(mw.mockResponses(mock.route, mock.responses)) } else if (mock.response) { mock.target = { request: mock.request, response: mock.response } - // app.use(mw.mockResponses(mock.route, mock.target)) - middlewares.push(mw.mockResponses(mock.route, mock.target)) + middlewareStack.push(mw.mockResponses(mock.route, mock.target)) } }) @@ -186,11 +166,7 @@ function localWebServer (options) { if (options.spa) { const historyApiFallback = require('koa-connect-history-api-fallback') debug('SPA', options.spa) - // app.use(historyApiFallback({ - // index: options.spa, - // verbose: options.verbose - // })) - middlewares.push(historyApiFallback({ + middlewareStack.push(historyApiFallback({ index: options.spa, verbose: options.verbose })) @@ -199,20 +175,18 @@ function localWebServer (options) { /* serve static files */ if (options.static.root) { const serve = require('koa-static') - // app.use(serve(options.static.root, options.static.options)) - middlewares.push(serve(options.static.root, options.static.options)) + middlewareStack.push(serve(options.static.root, options.static.options)) } /* serve directory index */ if (options.serveIndex.path) { const serveIndex = require('koa-serve-index') - // app.use(serveIndex(options.serveIndex.path, options.serveIndex.options)) - middlewares.push(serveIndex(options.serveIndex.path, options.serveIndex.options)) + middlewareStack.push(serveIndex(options.serveIndex.path, options.serveIndex.options)) } const compose = require('koa-compose') - middlewares = middlewares.map(convert) - return compose(middlewares) + middlewareStack = middlewareStack.map(convert) + return compose(middlewareStack) } function logstalgiaDate () { diff --git a/lib/middleware.js b/lib/middleware.js index 3c53acb..984b768 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -22,7 +22,6 @@ function proxyRequest (route) { }) return function proxyMiddleware () { - const next = arguments[arguments.length - 1] const keys = [] route.re = pathToRegexp(route.from, keys) route.new = this.url.replace(route.re, route.to) @@ -33,17 +32,19 @@ function proxyRequest (route) { .replace(re, arguments[index + 1] || '') }) - this.response = false debug('proxy request', `from: ${this.path}, to: ${url.parse(route.new).href}`) - proxy.once('error', err => { - this.throw(500, `[PROXY] ${err.message}: ${route.new}`) - }) - proxy.once('proxyReq', function (proxyReq) { - proxyReq.path = url.parse(route.new).path + return new Promise((resolve, reject) => { + proxy.once('error', err => { + err.message = `[PROXY] Error: ${err.message} Target: ${route.new}` + reject(err) + }) + proxy.once('proxyReq', function (proxyReq) { + proxyReq.path = url.parse(route.new).path + }) + proxy.once('close', resolve) + proxy.web(this.req, this.res, { target: route.new }) }) - - proxy.web(this.req, this.res, { target: route.new }) } }