diff --git a/README.md b/README.md index 5811513a..b3a03235 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Using `npx` you can run the script without installing it first: `-a` Address to use (defaults to 0.0.0.0) +`-b` or `--base-dir` Base path to serve files from (defaults to /) + `-d` Show directory listings (defaults to `true`) `-i` Display autoIndex (defaults to `true`) diff --git a/bin/http-server b/bin/http-server index 24b54dd9..50bfa1f2 100755 --- a/bin/http-server +++ b/bin/http-server @@ -2,15 +2,15 @@ 'use strict'; -var colors = require('colors/safe'), - os = require('os'), - httpServer = require('../lib/http-server'), - portfinder = require('portfinder'), - opener = require('opener'), - argv = require('optimist') - .boolean('cors') - .boolean('log-ip') - .argv; +var colors = require('colors/safe'), + os = require('os'), + httpServer = require('../lib/http-server'), + portfinder = require('portfinder'), + opener = require('opener'), + argv = require('optimist') + .boolean('cors') + .boolean('log-ip') + .argv; var ifaces = os.networkInterfaces(); @@ -22,6 +22,7 @@ if (argv.h || argv.help) { ' -p --port Port to use [8080]', ' -a Address to use [0.0.0.0]', ' -d Show directory listings [true]', + ' -b --base-dir Base directory to serve files from [/]', ' -i Display autoIndex [true]', ' -g --gzip Serve gzip files when possible [false]', ' -b --brotli Serve brotli files when possible [false]', @@ -56,11 +57,12 @@ if (argv.h || argv.help) { } var port = argv.p || argv.port || parseInt(process.env.PORT, 10), - host = argv.a || '0.0.0.0', - ssl = !!argv.S || !!argv.ssl, - proxy = argv.P || argv.proxy, - utc = argv.U || argv.utc, - logger; + host = argv.a || '0.0.0.0', + ssl = !!argv.S || !!argv.ssl, + proxy = argv.P || argv.proxy, + utc = argv.U || argv.utc, + baseDir = argv.b || argv['base-dir'], + logger; if (!argv.s && !argv.silent) { logger = { @@ -68,8 +70,8 @@ if (!argv.s && !argv.silent) { request: function (req, res, error) { var date = utc ? new Date().toUTCString() : new Date(); var ip = argv['log-ip'] - ? req.headers['x-forwarded-for'] || '' + req.connection.remoteAddress - : ''; + ? req.headers['x-forwarded-for'] || '' + req.connection.remoteAddress + : ''; if (error) { logger.info( '[%s] %s "%s %s" Error (%s): "%s"', @@ -89,8 +91,8 @@ if (!argv.s && !argv.silent) { } else if (colors) { logger = { - info: function () {}, - request: function () {} + info: function () { }, + request: function () { } }; } @@ -110,6 +112,7 @@ function listen(port) { root: argv._[0], cache: argv.c, showDir: argv.d, + baseDir: baseDir, autoIndex: argv.i, gzip: argv.g || argv.gzip, brotli: argv.b || argv.brotli, @@ -139,22 +142,23 @@ function listen(port) { var server = httpServer.createServer(options); server.listen(port, host, function () { var canonicalHost = host === '0.0.0.0' ? '127.0.0.1' : host, - protocol = ssl ? 'https://' : 'http://'; + protocol = ssl ? 'https://' : 'http://', + path = baseDir ? '/' + baseDir.replace(/^\//, '') : ''; logger.info([colors.yellow('Starting up http-server, serving '), - colors.cyan(server.root), - ssl ? (colors.yellow(' through') + colors.cyan(' https')) : '', - colors.yellow('\nAvailable on:') + colors.cyan(server.root), + ssl ? (colors.yellow(' through') + colors.cyan(' https')) : '', + colors.yellow('\nAvailable on:') ].join('')); if (argv.a && host !== '0.0.0.0') { - logger.info((' ' + protocol + canonicalHost + ':' + colors.green(port.toString()))); + logger.info((' ' + protocol + canonicalHost + ':' + colors.green(port.toString()) + path)); } else { Object.keys(ifaces).forEach(function (dev) { ifaces[dev].forEach(function (details) { if (details.family === 'IPv4') { - logger.info((' ' + protocol + details.address + ':' + colors.green(port.toString()))); + logger.info((' ' + protocol + details.address + ':' + colors.green(port.toString()) + path)); } }); }); diff --git a/lib/http-server.js b/lib/http-server.js index 4cdd1b1c..5c514d54 100644 --- a/lib/http-server.js +++ b/lib/http-server.js @@ -125,6 +125,7 @@ function HttpServer(options) { before.push(ecstatic({ root: this.root, + baseDir: options.baseDir, cache: this.cache, showDir: this.showDir, showDotfiles: this.showDotfiles, diff --git a/test/http-server-test.js b/test/http-server-test.js index 0a7f897f..1a4e8a9a 100644 --- a/test/http-server-test.js +++ b/test/http-server-test.js @@ -1,9 +1,9 @@ var assert = require('assert'), - path = require('path'), - fs = require('fs'), - vows = require('vows'), - request = require('request'), - httpServer = require('../lib/http-server'); + path = require('path'), + fs = require('fs'), + vows = require('vows'), + request = require('request'), + httpServer = require('../lib/http-server'); // Prevent vows from swallowing errors process.on('uncaughtException', console.error); @@ -342,5 +342,45 @@ vows.describe('http-server').addBatch({ } } } + }, + 'When baseDir is specified': { + topic: function () { + var server = httpServer.createServer({ + root: root, + baseDir: '/test' + }); + server.listen(8086); + this.callback(null, server); + }, + 'it should serve files at the specified baseDir': { + topic: function () { + request('http://127.0.0.1:8086/test/file', this.callback); + }, + 'status code should be 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 served file': function (err, file, body) { + assert.equal(body.trim(), file.trim()); + } + } + }, + 'it should not serve files at the root': { + topic: function () { + request('http://127.0.0.1:8086/file', this.callback); + }, + 'status code should be 403': function (res) { + assert.equal(res.statusCode, 403); + }, + 'and file content should be empty': function (res) { + assert.equal(res.body, ''); + } + } } }).export(module);