Skip to content

Commit ca52b0c

Browse files
committed
merge http-party/http-server master
2 parents f495842 + f42d662 commit ca52b0c

File tree

17 files changed

+2992
-38
lines changed

17 files changed

+2992
-38
lines changed

.github/ISSUE_TEMPLATE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
**Do you want to request a *feature* or report a *bug*?**
2+
3+
**If the issue is a bug report, please provide the steps to reproduce it. Please include the actual command causing the issue if applicable.**
4+
5+
<!-- Paste the command here: -->
6+
7+
**What did you expect to happen?**
8+
9+
**What actually happened? Please include the actual error trace and / or stack trace if applicable.**
10+
11+
<!-- Paste the error and / or stack trace here: -->
12+
13+
**If the issue is a feature request, what is the motivation / use case for it?**
14+
15+
**Tell us about your environment**
16+
- **http-server version:**
17+
- **Platform:**
18+
19+
**Other information (related issues, suggestions for a fix, etc):**
20+

.github/PULL_REQUEST_TEMPLATE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
**Please ensure that your pull request fulfills these requirements:**
2+
- [ ] The pull request is being made against the `master` branch
3+
- [ ] Tests for the changes have been added (for bug fixes / features)
4+
5+
**What is the purpose of this pull request? (bug fix, enhancement, new feature,...)**
6+
7+
<!--
8+
Link to the issue this pull request fixes here, if applicable: "Fixes #xxx" or "Resolves #xxx"
9+
-->
10+
11+
**What changes did you make?**
12+
13+
**Provide some example code that this change will affect, if applicable:**
14+
15+
<!-- Paste the example code here: -->
16+
17+
**Is there anything you'd like reviewers to focus on?**
18+
19+
**Please provide testing instructions, if applicable:**

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules/*
22
npm-debug.log
3+
.dir-locals.el

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2011 Charlie Robbins, Marak Squires, and the Contributors.
1+
Copyright (c) 2011-2019 Charlie Robbins, Marak Squires, and the Contributors.
22

33
Permission is hereby granted, free of charge, to any person obtaining
44
a copy of this software and associated documentation files (the

README.md

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ Installation via `npm`:
1717

1818
This will install `http-server` globally so that it may be run from the command line.
1919

20+
## Running on-demand:
21+
22+
Using `npx` you can run the script without installing it first:
23+
24+
npx http-server [path] [options]
25+
2026
## Usage:
2127

2228
http-server [path] [options]
@@ -25,39 +31,49 @@ This will install `http-server` globally so that it may be run from the command
2531

2632
*Now you can visit http://localhost:8080 to view your server*
2733

34+
**Note:** Caching is on by default. Add `-c-1` as an option to disable caching.
35+
2836
## Available Options:
2937

30-
`-p` Port to use (defaults to 8080)
38+
`-p` or `--port` Port to use (defaults to 8080)
3139

3240
`-a` Address to use (defaults to 0.0.0.0)
3341

34-
`-d` Show directory listings (defaults to 'True')
42+
`-d` Show directory listings (defaults to `true`)
3543

36-
`-i` Display autoIndex (defaults to 'True')
44+
`-i` Display autoIndex (defaults to `true`)
3745

38-
`-g` or `--gzip` When enabled (defaults to 'False') it will serve `./public/some-file.js.gz` in place of `./public/some-file.js` when a gzipped version of the file exists and the request accepts gzip encoding.
46+
`-g` or `--gzip` When enabled (defaults to `false`) it will serve `./public/some-file.js.gz` in place of `./public/some-file.js` when a gzipped version of the file exists and the request accepts gzip encoding. If brotli is also enabled, it will try to serve brotli first.
3947

40-
`-e` or `--ext` Default file extension if none supplied (defaults to 'html')
48+
`-b` or `--brotli` When enabled (defaults to `false`) it will serve `./public/some-file.js.br` in place of `./public/some-file.js` when a brotli compressed version of the file exists and the request accepts `br` encoding. If gzip is also enabled, it will try to serve brotli first.
49+
50+
`-e` or `--ext` Default file extension if none supplied (defaults to `html`)
4151

4252
`-s` or `--silent` Suppress log messages from output
4353

4454
`--cors` Enable CORS via the `Access-Control-Allow-Origin` header
4555

46-
`-o` Open browser window after starting the server
56+
`-o [path]` Open browser window after starting the server. Optionally provide a URL path to open. e.g.: -o /other/dir/
4757

48-
`-c` Set cache time (in seconds) for cache-control max-age header, e.g. -c10 for 10 seconds (defaults to '3600'). To disable caching, use -c-1.
58+
`-c` Set cache time (in seconds) for cache-control max-age header, e.g. `-c10` for 10 seconds (defaults to `3600`). To disable caching, use `-c-1`.
4959

5060
`-U` or `--utc` Use UTC time format in log messages.
5161

62+
`--log-ip` Enable logging of the client's IP address (default: `false`).
63+
5264
`-P` or `--proxy` Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com
5365

66+
`--username` Username for basic authentication [none]
67+
68+
`--password` Password for basic authentication [none]
69+
5470
`-S` or `--ssl` Enable https.
5571

56-
`-C` or `--cert` Path to ssl cert file (default: cert.pem).
72+
`-C` or `--cert` Path to ssl cert file (default: `cert.pem`).
5773

58-
`-K` or `--key` Path to ssl key file (default: key.pem).
74+
`-K` or `--key` Path to ssl key file (default: `key.pem`).
5975

60-
`-r` or `--robots` Provide a /robots.txt (whose content defaults to 'User-agent: *\nDisallow: /').
76+
`-r` or `--robots` Provide a /robots.txt (whose content defaults to `User-agent: *\nDisallow: /`)
6177

6278
`-m` or `--middleware` Provide a filepath (single or multiple file paths through multiple '-m or --middleware' parameters) to a JS file that exports a function to be used as middleware for each request before anything else happens (or any other middleware is called). e.g. `./my_middldeware.js`
6379

@@ -73,6 +89,21 @@ module.exports = function(req, res, next) {
7389

7490
`-h` or `--help` Print this list and exit.
7591

92+
## Magic Files
93+
94+
- `index.html` will be served as the default file to any directory requests.
95+
- `404.html` will be served if a file is not found. This can be used for Single-Page App (SPA) hosting to serve the entry page.
96+
97+
## Catch-all redirect
98+
99+
To implement a catch-all redirect, use the index page itself as the proxy with:
100+
101+
```
102+
http-server --proxy http://localhost:8080?
103+
```
104+
105+
Note the `?` at the end of the proxy URL. Thanks to [@houston3](https://github.com/houston3) for this clever hack!
106+
76107
# Development
77108

78109
Checkout this repository locally, then:

bin/http-server

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ var colors = require('colors/safe'),
99
opener = require('opener'),
1010
argv = require('optimist')
1111
.boolean('cors')
12+
.boolean('log-ip')
1213
.argv;
1314

1415
var ifaces = os.networkInterfaces();
@@ -18,22 +19,31 @@ if (argv.h || argv.help) {
1819
'usage: http-server [path] [options]',
1920
'',
2021
'options:',
21-
' -p Port to use [8080]',
22+
' -p --port Port to use [8080]',
2223
' -a Address to use [0.0.0.0]',
2324
' -d Show directory listings [true]',
2425
' -i Display autoIndex [true]',
2526
' -g --gzip Serve gzip files when possible [false]',
27+
' -b --brotli Serve brotli files when possible [false]',
28+
' If both brotli and gzip are enabled, brotli takes precedence',
2629
' -e --ext Default file extension if none supplied [none]',
2730
' -s --silent Suppress log messages from output',
2831
' --cors[=headers] Enable CORS via the "Access-Control-Allow-Origin" header',
2932
' Optionally provide CORS headers list separated by commas',
30-
' -o [path] Open browser window after starting the server',
33+
' -o [path] Open browser window after starting the server.',
34+
' Optionally provide a URL path to open the browser window to.',
3135
' -c Cache time (max-age) in seconds [3600], e.g. -c10 for 10 seconds.',
3236
' To disable caching, use -c-1.',
3337
' -U --utc Use UTC time format in log messages.',
38+
' --log-ip Enable logging of the client\'s IP address',
3439
'',
3540
' -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com',
3641
'',
42+
' --username Username for basic authentication [none]',
43+
' Can also be specified with the env variable NODE_HTTP_SERVER_USERNAME',
44+
' --password Password for basic authentication [none]',
45+
' Can also be specified with the env variable NODE_HTTP_SERVER_PASSWORD',
46+
'',
3747
' -S --ssl Enable https.',
3848
' -C --cert Path to ssl cert file (default: cert.pem).',
3949
' -K --key Path to ssl key file (default: key.pem).',
@@ -46,7 +56,7 @@ if (argv.h || argv.help) {
4656
process.exit();
4757
}
4858

49-
var port = argv.p || parseInt(process.env.PORT, 10),
59+
var port = argv.p || argv.port || parseInt(process.env.PORT, 10),
5060
host = argv.a || '0.0.0.0',
5161
ssl = !!argv.S || !!argv.ssl,
5262
proxy = argv.P || argv.proxy,
@@ -58,17 +68,20 @@ if (!argv.s && !argv.silent) {
5868
info: console.log,
5969
request: function (req, res, error) {
6070
var date = utc ? new Date().toUTCString() : new Date();
71+
var ip = argv['log-ip']
72+
? req.headers['x-forwarded-for'] || '' + req.connection.remoteAddress
73+
: '';
6174
if (error) {
6275
logger.info(
63-
'[%s] "%s %s" Error (%s): "%s"',
64-
date, colors.red(req.method), colors.red(req.url),
76+
'[%s] %s "%s %s" Error (%s): "%s"',
77+
date, ip, colors.red(req.method), colors.red(req.url),
6578
colors.red(error.status.toString()), colors.red(error.message)
6679
);
6780
}
6881
else {
6982
logger.info(
70-
'[%s] "%s %s" "%s"',
71-
date, colors.cyan(req.method), colors.cyan(req.url),
83+
'[%s] %s "%s %s" "%s"',
84+
date, ip, colors.cyan(req.method), colors.cyan(req.url),
7285
req.headers['user-agent']
7386
);
7487
}
@@ -100,12 +113,15 @@ function listen(port) {
100113
showDir: argv.d,
101114
autoIndex: argv.i,
102115
gzip: argv.g || argv.gzip,
116+
brotli: argv.b || argv.brotli,
103117
robots: argv.r || argv.robots,
104118
extraMiddleware: argv.m || argv.middleware,
105119
ext: argv.e || argv.ext,
106120
logFn: logger.request,
107121
proxy: proxy,
108-
showDotfiles: argv.dotfiles
122+
showDotfiles: argv.dotfiles,
123+
username: argv.username || process.env.NODE_HTTP_SERVER_USERNAME,
124+
password: argv.password || process.env.NODE_HTTP_SERVER_PASSWORD
109125
};
110126

111127
if (argv.cors) {
@@ -152,10 +168,12 @@ function listen(port) {
152168

153169
logger.info('Hit CTRL-C to stop the server');
154170
if (argv.o) {
155-
opener(
156-
protocol + canonicalHost + ':' + port,
157-
{ command: argv.o !== true ? argv.o : null }
158-
);
171+
var openUrl = protocol + canonicalHost + ':' + port;
172+
if (typeof argv.o === 'string') {
173+
openUrl += argv.o[0] === '/' ? argv.o : '/' + argv.o;
174+
}
175+
logger.info('open: ' + openUrl);
176+
opener(openUrl);
159177
}
160178
});
161179
}

lib/http-server.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
var fs = require('fs'),
44
union = require('union'),
55
ecstatic = require('ecstatic'),
6+
auth = require('basic-auth'),
67
httpProxy = require('http-proxy'),
7-
corser = require('corser');
8+
corser = require('corser'),
9+
secureCompare = require('secure-compare');
810

911
//
1012
// Remark: backwards compatibility for previous
@@ -43,11 +45,18 @@ function HttpServer(options) {
4345

4446
this.headers = options.headers || {};
4547

46-
this.cache = options.cache === undefined ? 3600 : options.cache; // in seconds.
48+
this.cache = (
49+
options.cache === undefined ? 3600 :
50+
// -1 is a special case to turn off caching.
51+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Preventing_caching
52+
options.cache === -1 ? 'no-cache, no-store, must-revalidate' :
53+
options.cache // in seconds.
54+
);
4755
this.showDir = options.showDir !== 'false';
4856
this.autoIndex = options.autoIndex !== 'false';
4957
this.showDotfiles = options.showDotfiles;
5058
this.gzip = options.gzip === true;
59+
this.brotli = options.brotli === true;
5160
this.contentType = options.contentType || 'application/octet-stream';
5261

5362
if (options.ext) {
@@ -82,6 +91,27 @@ function HttpServer(options) {
8291
res.emit('next');
8392
});
8493

94+
if (options.username || options.password) {
95+
before.push(function (req, res) {
96+
var credentials = auth(req);
97+
98+
// We perform these outside the if to avoid short-circuiting and giving
99+
// an attacker knowledge of whether the username is correct via a timing
100+
// attack.
101+
if (credentials) {
102+
var usernameEqual = secureCompare(options.username, credentials.name);
103+
var passwordEqual = secureCompare(options.password, credentials.pass);
104+
if (usernameEqual && passwordEqual) {
105+
return res.emit('next');
106+
}
107+
}
108+
109+
res.statusCode = 401;
110+
res.setHeader('WWW-Authenticate', 'Basic realm=""');
111+
res.end('Access denied');
112+
});
113+
}
114+
85115
if (options.cors) {
86116
this.headers['Access-Control-Allow-Origin'] = '*';
87117
this.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Range';
@@ -117,6 +147,7 @@ function HttpServer(options) {
117147
autoIndex: this.autoIndex,
118148
defaultExt: this.ext,
119149
gzip: this.gzip,
150+
brotli: this.brotli,
120151
contentType: this.contentType,
121152
handleError: typeof options.proxy !== 'string'
122153
}));
@@ -127,6 +158,13 @@ function HttpServer(options) {
127158
proxy.web(req, res, {
128159
target: options.proxy,
129160
changeOrigin: true
161+
}, function (err, req, res, target) {
162+
if (options.logFn) {
163+
options.logFn(req, res, {
164+
message: err.message,
165+
status: res.statusCode });
166+
}
167+
res.emit('next');
130168
});
131169
});
132170
}

0 commit comments

Comments
 (0)