@pkg-nec/http-server 14.1.1-pkgnec.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +147 -0
- package/bin/http-server +278 -0
- package/doc/http-server.1 +165 -0
- package/lib/core/aliases.json +34 -0
- package/lib/core/defaults.json +18 -0
- package/lib/core/etag.js +9 -0
- package/lib/core/index.js +487 -0
- package/lib/core/opts.js +203 -0
- package/lib/core/show-dir/icons.json +65 -0
- package/lib/core/show-dir/index.js +173 -0
- package/lib/core/show-dir/last-modified-to-string.js +10 -0
- package/lib/core/show-dir/perms-to-string.js +21 -0
- package/lib/core/show-dir/size-to-string.js +30 -0
- package/lib/core/show-dir/sort-files.js +36 -0
- package/lib/core/show-dir/styles.js +20 -0
- package/lib/core/status-handlers.js +96 -0
- package/lib/http-server.js +192 -0
- package/lib/shims/https-server-shim.js +67 -0
- package/package.json +122 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs'),
|
|
4
|
+
union = require('union'),
|
|
5
|
+
httpServerCore = require('./core'),
|
|
6
|
+
auth = require('basic-auth'),
|
|
7
|
+
httpProxy = require('http-proxy'),
|
|
8
|
+
corser = require('corser'),
|
|
9
|
+
secureCompare = require('secure-compare');
|
|
10
|
+
|
|
11
|
+
//
|
|
12
|
+
// Remark: backwards compatibility for previous
|
|
13
|
+
// case convention of HTTP
|
|
14
|
+
//
|
|
15
|
+
exports.HttpServer = exports.HTTPServer = HttpServer;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns a new instance of HttpServer with the
|
|
19
|
+
* specified `options`.
|
|
20
|
+
*/
|
|
21
|
+
exports.createServer = function (options) {
|
|
22
|
+
return new HttpServer(options);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Constructor function for the HttpServer object
|
|
27
|
+
* which is responsible for serving static files along
|
|
28
|
+
* with other HTTP-related features.
|
|
29
|
+
*/
|
|
30
|
+
function HttpServer(options) {
|
|
31
|
+
options = options || {};
|
|
32
|
+
|
|
33
|
+
if (options.root) {
|
|
34
|
+
this.root = options.root;
|
|
35
|
+
} else {
|
|
36
|
+
try {
|
|
37
|
+
// eslint-disable-next-line no-sync
|
|
38
|
+
fs.lstatSync('./public');
|
|
39
|
+
this.root = './public';
|
|
40
|
+
} catch (err) {
|
|
41
|
+
this.root = './';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.headers = options.headers || {};
|
|
46
|
+
this.headers['Accept-Ranges'] = 'bytes';
|
|
47
|
+
|
|
48
|
+
this.cache = (
|
|
49
|
+
// eslint-disable-next-line no-nested-ternary
|
|
50
|
+
options.cache === undefined ? 3600 :
|
|
51
|
+
// -1 is a special case to turn off caching.
|
|
52
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Preventing_caching
|
|
53
|
+
options.cache === -1 ? 'no-cache, no-store, must-revalidate' :
|
|
54
|
+
options.cache // in seconds.
|
|
55
|
+
);
|
|
56
|
+
this.showDir = options.showDir !== 'false';
|
|
57
|
+
this.autoIndex = options.autoIndex !== 'false';
|
|
58
|
+
this.showDotfiles = options.showDotfiles;
|
|
59
|
+
this.gzip = options.gzip === true;
|
|
60
|
+
this.brotli = options.brotli === true;
|
|
61
|
+
if (options.ext) {
|
|
62
|
+
this.ext = options.ext === true
|
|
63
|
+
? 'html'
|
|
64
|
+
: options.ext;
|
|
65
|
+
}
|
|
66
|
+
this.contentType = options.contentType ||
|
|
67
|
+
this.ext === 'html' ? 'text/html' : 'application/octet-stream';
|
|
68
|
+
|
|
69
|
+
var before = options.before ? options.before.slice() : [];
|
|
70
|
+
|
|
71
|
+
if (options.logFn) {
|
|
72
|
+
before.push(function (req, res) {
|
|
73
|
+
options.logFn(req, res);
|
|
74
|
+
res.emit('next');
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (options.username || options.password) {
|
|
79
|
+
before.push(function (req, res) {
|
|
80
|
+
var credentials = auth(req);
|
|
81
|
+
|
|
82
|
+
// We perform these outside the if to avoid short-circuiting and giving
|
|
83
|
+
// an attacker knowledge of whether the username is correct via a timing
|
|
84
|
+
// attack.
|
|
85
|
+
if (credentials) {
|
|
86
|
+
// if credentials is defined, name and pass are guaranteed to be string
|
|
87
|
+
// type
|
|
88
|
+
var usernameEqual = secureCompare(options.username.toString(), credentials.name);
|
|
89
|
+
var passwordEqual = secureCompare(options.password.toString(), credentials.pass);
|
|
90
|
+
if (usernameEqual && passwordEqual) {
|
|
91
|
+
return res.emit('next');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
res.statusCode = 401;
|
|
96
|
+
res.setHeader('WWW-Authenticate', 'Basic realm=""');
|
|
97
|
+
res.end('Access denied');
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (options.cors) {
|
|
102
|
+
this.headers['Access-Control-Allow-Origin'] = '*';
|
|
103
|
+
this.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Range';
|
|
104
|
+
if (options.corsHeaders) {
|
|
105
|
+
options.corsHeaders.split(/\s*,\s*/)
|
|
106
|
+
.forEach(function (h) { this.headers['Access-Control-Allow-Headers'] += ', ' + h; }, this);
|
|
107
|
+
}
|
|
108
|
+
before.push(corser.create(options.corsHeaders ? {
|
|
109
|
+
requestHeaders: this.headers['Access-Control-Allow-Headers'].split(/\s*,\s*/)
|
|
110
|
+
} : null));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (options.robots) {
|
|
114
|
+
before.push(function (req, res) {
|
|
115
|
+
if (req.url === '/robots.txt') {
|
|
116
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
117
|
+
var robots = options.robots === true
|
|
118
|
+
? 'User-agent: *\nDisallow: /'
|
|
119
|
+
: options.robots.replace(/\\n/, '\n');
|
|
120
|
+
|
|
121
|
+
return res.end(robots);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
res.emit('next');
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
before.push(httpServerCore({
|
|
129
|
+
root: this.root,
|
|
130
|
+
cache: this.cache,
|
|
131
|
+
showDir: this.showDir,
|
|
132
|
+
showDotfiles: this.showDotfiles,
|
|
133
|
+
autoIndex: this.autoIndex,
|
|
134
|
+
defaultExt: this.ext,
|
|
135
|
+
gzip: this.gzip,
|
|
136
|
+
brotli: this.brotli,
|
|
137
|
+
contentType: this.contentType,
|
|
138
|
+
mimetypes: options.mimetypes,
|
|
139
|
+
handleError: typeof options.proxy !== 'string'
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
if (typeof options.proxy === 'string') {
|
|
143
|
+
var proxyOptions = options.proxyOptions || {};
|
|
144
|
+
var proxy = httpProxy.createProxyServer(proxyOptions);
|
|
145
|
+
before.push(function (req, res) {
|
|
146
|
+
proxy.web(req, res, {
|
|
147
|
+
target: options.proxy,
|
|
148
|
+
changeOrigin: true
|
|
149
|
+
}, function (err, req, res) {
|
|
150
|
+
if (options.logFn) {
|
|
151
|
+
options.logFn(req, res, {
|
|
152
|
+
message: err.message,
|
|
153
|
+
status: res.statusCode });
|
|
154
|
+
}
|
|
155
|
+
res.emit('next');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var serverOptions = {
|
|
161
|
+
before: before,
|
|
162
|
+
headers: this.headers,
|
|
163
|
+
onError: function (err, req, res) {
|
|
164
|
+
if (options.logFn) {
|
|
165
|
+
options.logFn(req, res, err);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
res.end();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
if (options.https) {
|
|
173
|
+
serverOptions.https = options.https;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.server = serverOptions.https && serverOptions.https.passphrase
|
|
177
|
+
// if passphrase is set, shim must be used as union does not support
|
|
178
|
+
? require('./shims/https-server-shim')(serverOptions)
|
|
179
|
+
: union.createServer(serverOptions);
|
|
180
|
+
|
|
181
|
+
if (options.timeout !== undefined) {
|
|
182
|
+
this.server.setTimeout(options.timeout);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
HttpServer.prototype.listen = function () {
|
|
187
|
+
this.server.listen.apply(this.server, arguments);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
HttpServer.prototype.close = function () {
|
|
191
|
+
return this.server.close();
|
|
192
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* eslint-disable no-process-env */
|
|
2
|
+
/* eslint-disable no-sync */
|
|
3
|
+
var https = require('https');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var core = require('union/lib/core');
|
|
6
|
+
var RoutingStream = require('union/lib/routing-stream');
|
|
7
|
+
|
|
8
|
+
module.exports = function (options) {
|
|
9
|
+
var isArray = Array.isArray(options.after);
|
|
10
|
+
var credentials;
|
|
11
|
+
|
|
12
|
+
if (!options) {
|
|
13
|
+
throw new Error('options is required to create a server');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function requestHandler(req, res) {
|
|
17
|
+
var routingStream = new RoutingStream({
|
|
18
|
+
before: options.before,
|
|
19
|
+
buffer: options.buffer,
|
|
20
|
+
after:
|
|
21
|
+
isArray &&
|
|
22
|
+
options.after.map(function (After) {
|
|
23
|
+
return new After();
|
|
24
|
+
}),
|
|
25
|
+
request: req,
|
|
26
|
+
response: res,
|
|
27
|
+
limit: options.limit,
|
|
28
|
+
headers: options.headers
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
routingStream.on('error', function (err) {
|
|
32
|
+
var fn = options.onError || core.errorHandler;
|
|
33
|
+
fn(err, routingStream, routingStream.target, function () {
|
|
34
|
+
routingStream.target.emit('next');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
req.pipe(routingStream);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var serverOptions;
|
|
42
|
+
|
|
43
|
+
serverOptions = options.https;
|
|
44
|
+
if (!serverOptions.key || !serverOptions.cert) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
'Both options key and cert are required.'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
credentials = {
|
|
51
|
+
key: fs.readFileSync(serverOptions.key),
|
|
52
|
+
cert: fs.readFileSync(serverOptions.cert),
|
|
53
|
+
passphrase: process.env.NODE_HTTP_SERVER_SSL_PASSPHRASE
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (serverOptions.ca) {
|
|
57
|
+
serverOptions.ca = !Array.isArray(serverOptions.ca)
|
|
58
|
+
? [serverOptions.ca]
|
|
59
|
+
: serverOptions.ca;
|
|
60
|
+
|
|
61
|
+
credentials.ca = serverOptions.ca.map(function (ca) {
|
|
62
|
+
return fs.readFileSync(ca);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return https.createServer(credentials, requestHandler);
|
|
67
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pkg-nec/http-server",
|
|
3
|
+
"version": "14.1.1-pkgnec.0",
|
|
4
|
+
"description": "A simple zero-configuration command-line http server",
|
|
5
|
+
"main": "./lib/http-server",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/pkg-nec/http-server.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"cli",
|
|
12
|
+
"command",
|
|
13
|
+
"static",
|
|
14
|
+
"http",
|
|
15
|
+
"https",
|
|
16
|
+
"http-server",
|
|
17
|
+
"https-server",
|
|
18
|
+
"server"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"start": "node ./bin/http-server",
|
|
22
|
+
"test": "tap --reporter=spec test/*.test.js",
|
|
23
|
+
"test-watch": "tap --reporter=spec --watch test/*.test.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"lib",
|
|
27
|
+
"bin",
|
|
28
|
+
"doc"
|
|
29
|
+
],
|
|
30
|
+
"man": "./doc/http-server.1",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=12"
|
|
33
|
+
},
|
|
34
|
+
"contributors": [
|
|
35
|
+
{
|
|
36
|
+
"name": "Charlie Robbins",
|
|
37
|
+
"email": "charlie.robbins@gmail.com"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "Marak Squires",
|
|
41
|
+
"email": "marak.squires@gmail.com"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "Charlie McConnell",
|
|
45
|
+
"email": "charlie@charlieistheman.com"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "Joshua Holbrook",
|
|
49
|
+
"email": "josh.holbrook@gmail.com"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "Maciej Małecki",
|
|
53
|
+
"email": "maciej.malecki@notimplemented.org"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "Matthew Bergman",
|
|
57
|
+
"email": "mzbphoto@gmail.com"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "brad dunbar",
|
|
61
|
+
"email": "dunbarb2@gmail.com"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "Dominic Tarr"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "Travis Person",
|
|
68
|
+
"email": "travis.person@gmail.com"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "Jinkwon Lee",
|
|
72
|
+
"email": "master@bdyne.net"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "BigBlueHat",
|
|
76
|
+
"email": "byoung@bigbluehat.com"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "Daniel Dalton",
|
|
80
|
+
"email": "daltond2@hawkmail.newpaltz.edu"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "Jade Michael Thornton",
|
|
84
|
+
"email": "jademichael@jmthornton.net"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"dependencies": {
|
|
88
|
+
"basic-auth": "^2.0.1",
|
|
89
|
+
"chalk": "^4.1.2",
|
|
90
|
+
"corser": "^2.0.1",
|
|
91
|
+
"he": "^1.2.0",
|
|
92
|
+
"html-encoding-sniffer": "^3.0.0",
|
|
93
|
+
"http-proxy": "^1.18.1",
|
|
94
|
+
"mime": "^1.6.0",
|
|
95
|
+
"minimist": "^1.2.6",
|
|
96
|
+
"opener": "^1.5.1",
|
|
97
|
+
"portfinder": "^1.0.28",
|
|
98
|
+
"secure-compare": "3.0.1",
|
|
99
|
+
"union": "~0.5.0",
|
|
100
|
+
"url-join": "^4.0.1"
|
|
101
|
+
},
|
|
102
|
+
"devDependencies": {
|
|
103
|
+
"eol": "^0.9.1",
|
|
104
|
+
"eslint": "^4.19.1",
|
|
105
|
+
"eslint-config-populist": "^4.2.0",
|
|
106
|
+
"express": "^4.17.1",
|
|
107
|
+
"request": "^2.88.2",
|
|
108
|
+
"tap": "^14.11.0"
|
|
109
|
+
},
|
|
110
|
+
"bugs": {
|
|
111
|
+
"url": "https://github.com/pkg-nec/http-server/issues"
|
|
112
|
+
},
|
|
113
|
+
"homepage": "https://github.com/pkg-nec/http-server#readme",
|
|
114
|
+
"license": "MIT",
|
|
115
|
+
"publishConfig": {
|
|
116
|
+
"access": "public"
|
|
117
|
+
},
|
|
118
|
+
"preferGlobal": true,
|
|
119
|
+
"bin": {
|
|
120
|
+
"http-server": "./bin/http-server"
|
|
121
|
+
}
|
|
122
|
+
}
|