Skip to content

Commit 5c25529

Browse files
Add support for Private Network Access via Access-Control-Allow-Private-Network header (#801)
This PR has stale tests for an older version of tap so a subsequent fix is required for the tests. Co-authored-by: Eric Dubé <[email protected]>
1 parent 8dbde7d commit 5c25529

File tree

8 files changed

+113
-0
lines changed

8 files changed

+113
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ with the provided Dockerfile.
7272
|`-s` or `--silent` |Suppress log messages from output | |
7373
|`--coop` |Enable COOP via the `Cross-Origin-Opener-Policy` header | |
7474
|`--cors` |Enable CORS via the `Access-Control-Allow-Origin` header | |
75+
|`--private-network-access` |Enable Private Network Access via the `Access-Control-Allow-Private-Network` header | |
7576
|`--cors` | Enable CORS via the `Access-Control-Allow-Origin: *` header. Optionally provide comma-separated values to add to `Access-Control-Allow-Headers` | |
7677
|`-H` or `--header` |Add an extra response header (can be used several times) | |
7778
|`-o [path]` |Open browser window after starting the server. Optionally provide a URL path to open. e.g.: -o /other/dir/ | |

bin/http-server

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ if (argv.h || argv.help) {
5050
' Optionally provide COOP mode.',
5151
' --content-type Default content type for unknown file types [application/octet-stream]',
5252
' --cors[=headers] Enable CORS via the "Access-Control-Allow-Origin" header',
53+
' Optionally provide CORS headers list separated by commas',
54+
' --private-network-access Enable Private Network Access via the',
55+
' "Access-Control-Allow-Private-Network" header',
5356
' When enabled, sets Access-Control-Allow-Origin to "*"',
5457
' Optional value adds to Access-Control-Allow-Headers',
5558
' -H',
@@ -236,6 +239,10 @@ function listen(port) {
236239
}
237240
}
238241

242+
if (argv['private-network-access']) {
243+
options.privateNetworkAccess = true;
244+
}
245+
239246
if (proxy) {
240247
try {
241248
new url.URL(proxy);
@@ -319,6 +326,7 @@ function listen(port) {
319326
chalk.yellow('\nhttp-server settings: '),
320327
([chalk.yellow('COOP: '), argv.coop ? chalk.cyan(argv.coop) : chalk.red('disabled')].join('')),
321328
([chalk.yellow('CORS: '), argv.cors ? chalk.cyan(argv.cors) : chalk.red('disabled')].join('')),
329+
([chalk.yellow('Private Network Access: '), argv['private-network-access'] ? chalk.cyan(argv['private-network-access']) : chalk.red('disabled')].join('')),
322330
([chalk.yellow('Cache: '), argv.c ? (argv.c === '-1' ? chalk.red('disabled') : chalk.cyan(argv.c + ' seconds')) : chalk.cyan('3600 seconds')].join('')),
323331
([chalk.yellow('Connection Timeout: '), Math.max(0, argv.t) === 0 ? chalk.red('disabled') :
324332
((!isNaN(argv.t) && !isNaN(parseFloat(argv.t))) ?

doc/http-server.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ Default Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Ac
8585
.BI \-H ", " \-\-header " " \fIHEADER\fR
8686
Add an extra response header (can be used several times)
8787

88+
.TP
89+
.BI \-\-private-network-access
90+
Enable Private Network Access via the "Access-Control-Allow-Private-Network" header.
91+
8892
.TP
8993
.BI \-o " " [\fIPATH\fR]
9094
Open default browser window after starting the server.

lib/core/aliases.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"handleError": [ "handleError", "handleerror" ],
1616
"coop": [ "coop", "COOP" ],
1717
"cors": [ "cors", "CORS" ],
18+
"privateNetworkAccess": [ "privateNetworkAccess", "privatenetworkaccess", "private-network-access" ],
1819
"headers": [ "H", "header", "headers" ],
1920
"contentType": [ "contentType", "contenttype", "content-type" ],
2021
"mimeType": [

lib/core/defaults.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"cache": "max-age=3600",
1010
"coop": false,
1111
"cors": false,
12+
"privateNetworkAccess": false,
1213
"gzip": true,
1314
"brotli": false,
1415
"forceContentEncoding": false,

lib/core/opts.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ module.exports = (opts) => {
160160
}
161161
});
162162

163+
aliases.privateNetworkAccess.forEach((k) => {
164+
if (isDeclared(k) && opts[k]) {
165+
headers['Access-Control-Allow-Private-Network'] = 'true';
166+
}
167+
});
168+
163169
aliases.headers.forEach((k) => {
164170
if (isDeclared(k)) {
165171
if (Array.isArray(opts[k])) {

lib/http-server.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ function HttpServer(options) {
140140
} : null));
141141
}
142142

143+
if (options.privateNetworkAccess) {
144+
this.headers['Access-Control-Allow-Private-Network'] = true;
145+
}
146+
143147
if (options.robots) {
144148
before.push(function (req, res) {
145149
if (req.url === '/robots.txt') {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict';
2+
3+
const test = require('tap').test;
4+
const server = require('../lib/core');
5+
const http = require('http');
6+
const path = require('path');
7+
const request = require('request');
8+
9+
const root = path.join(__dirname, 'public');
10+
11+
test('private-network-access defaults to false', (t) => {
12+
t.plan(3);
13+
14+
const httpServer = http.createServer(
15+
server({
16+
root,
17+
autoIndex: true,
18+
defaultExt: 'html',
19+
})
20+
);
21+
22+
httpServer.listen(() => {
23+
const port = httpServer.address().port;
24+
const uri = `http://localhost:${port}/subdir/index.html`;
25+
26+
request.get({ uri }, (err, res) => {
27+
t.ifError(err);
28+
t.equal(res.statusCode, 200);
29+
t.type(res.headers['access-control-allow-private-network'], 'undefined');
30+
});
31+
});
32+
t.once('end', () => {
33+
httpServer.close();
34+
});
35+
});
36+
37+
test('privateNetworkAccess set to false', (t) => {
38+
t.plan(3);
39+
40+
const httpServer = http.createServer(
41+
server({
42+
root,
43+
privateNetworkAccess: false,
44+
autoIndex: true,
45+
defaultExt: 'html',
46+
})
47+
);
48+
49+
httpServer.listen(() => {
50+
const port = httpServer.address().port;
51+
const uri = `http://localhost:${port}/subdir/index.html`;
52+
53+
request.get({ uri }, (err, res) => {
54+
t.ifError(err);
55+
t.equal(res.statusCode, 200);
56+
t.type(res.headers['access-control-allow-private-network'], 'undefined');
57+
});
58+
});
59+
t.once('end', () => {
60+
httpServer.close();
61+
});
62+
});
63+
64+
test('privateNetworkAccess set to true', (t) => {
65+
t.plan(3);
66+
67+
const httpServer = http.createServer(
68+
server({
69+
root,
70+
privateNetworkAccess: true,
71+
autoIndex: true,
72+
defaultExt: 'html',
73+
})
74+
);
75+
76+
httpServer.listen(() => {
77+
const port = httpServer.address().port;
78+
const uri = `http://localhost:${port}/subdir/index.html`;
79+
request.get({ uri }, (err, res) => {
80+
t.ifError(err);
81+
t.equal(res.statusCode, 200);
82+
t.equal(res.headers['access-control-allow-private-network'], 'true');
83+
});
84+
});
85+
t.once('end', () => {
86+
httpServer.close();
87+
});
88+
});

0 commit comments

Comments
 (0)