diff --git a/README.md b/README.md index 20eb7720..8d44880b 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ This will install `http-server` globally so that it may be run from the command `-o` Open browser window after staring the server +`-P` or `--proxy` Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com + `-S` or `--ssl` Enable https. `-C` or `--cert` Path to ssl cert file (default: cert.pem). diff --git a/bin/http-server b/bin/http-server index 9f7eb691..04b3c7db 100755 --- a/bin/http-server +++ b/bin/http-server @@ -24,6 +24,8 @@ if (argv.h || argv.help) { " -c Cache time (max-age) in seconds [3600], e.g. -c10 for 10 seconds.", " To disable caching, use -c-1.", "", + " -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com", + "", " -S --ssl Enable https.", " -C --cert Path to ssl cert file (default: cert.pem).", " -K --key Path to ssl key file (default: key.pem).", @@ -37,6 +39,7 @@ var port = argv.p || parseInt(process.env.PORT, 10), host = argv.a || '0.0.0.0', log = (argv.s || argv.silent) ? (function () {}) : console.log, ssl = !!argv.S || !!argv.ssl, + proxy = argv.P || argv.proxy, requestLogger; if (!argv.s && !argv.silent) { @@ -62,7 +65,8 @@ function listen(port) { showDir: argv.d, autoIndex: argv.i, ext: argv.e || argv.ext, - logFn: requestLogger + logFn: requestLogger, + proxy: proxy }; if (argv.cors) { @@ -88,6 +92,10 @@ function listen(port) { + ' on: '.yellow + uri.cyan); + if (typeof proxy === 'string') { + log('Unhandled requests will be served from: ' + proxy); + } + log('Hit CTRL-C to stop the server'); if (argv.o) { opener(uri); diff --git a/lib/http-server.js b/lib/http-server.js index 051a239b..9ccb023d 100644 --- a/lib/http-server.js +++ b/lib/http-server.js @@ -1,7 +1,8 @@ var fs = require('fs'), util = require('util'), union = require('union'), - ecstatic = require('ecstatic'); + ecstatic = require('ecstatic'), + httpProxy = require('http-proxy'); var HTTPServer = exports.HTTPServer = function (options) { options = options || {}; @@ -47,12 +48,23 @@ var HTTPServer = exports.HTTPServer = function (options) { cache: this.cache, showDir: this.showDir, autoIndex: this.autoIndex, - defaultExt: this.ext + defaultExt: this.ext, + handleError: typeof options.proxy !== 'string' }) ]), headers: this.headers || {} }; + if (typeof options.proxy === 'string') { + var proxy = httpProxy.createProxyServer({}); + serverOptions.before.push(function (req, res) { + proxy.web(req, res, { + target: options.proxy, + changeOrigin: true + }); + }); + } + if (options.https) { serverOptions.https = options.https; } diff --git a/package.json b/package.json index afda5b7d..bf8a535d 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "colors": "1.0.3", "optimist": "0.6.x", "union": "~0.4.3", - "ecstatic": "~0.6.0", + "ecstatic": "~0.6.1", + "http-proxy": "^1.8.1", "portfinder": "0.2.x", "opener": "~1.4.0" }, diff --git a/test/http-server-test.js b/test/http-server-test.js index 21e0164e..6efbfb89 100644 --- a/test/http-server-test.js +++ b/test/http-server-test.js @@ -65,6 +65,54 @@ vows.describe('http-server').addBatch({ assert.equal(res.headers['access-control-allow-origin'], '*'); assert.equal(res.headers['access-control-allow-credentials'], 'true'); } + }, + 'When http-server is proxying from 8081 to 8080': { + topic: function () { + var proxyServer = httpServer.createServer({ + proxy: 'http://127.0.0.1:8080/', + root: path.join(__dirname, 'fixtures') + }); + proxyServer.listen(8081); + this.callback(null, proxyServer); + }, + 'it should serve files from the proxy server root directory': { + topic: function () { + request('http://127.0.0.1:8081/root/file', this.callback); + }, + 'status code should be the enpoint code 200': function (res) { + assert.equal(res.statusCode, 200); + }, + 'and file content': { + topic: function (res, body) { + var self = this; + fs.readFile(path.join(root, 'file'), 'utf8', function (err, data) { + self.callback(err, data, body); + }); + }, + 'should match content of the served file': function (err, file, body) { + assert.equal(body.trim(), file.trim()); + } + } + }, + 'it should fallback to the proxied server': { + topic: function () { + request('http://127.0.0.1:8081/file', this.callback); + }, + 'status code should be the enpoint code 200': function (res) { + assert.equal(res.statusCode, 200); + }, + 'and file content': { + topic: function (res, body) { + var self = this; + fs.readFile(path.join(root, 'file'), 'utf8', function (err, data) { + self.callback(err, data, body); + }); + }, + 'should match content of the proxied served file': function (err, file, body) { + assert.equal(body.trim(), file.trim()); + } + } + } } } }).export(module);