Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add --coop option to enable cross-origin isolated #806

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ This will install `http-server` globally so that it may be run from the command
|`-b` or `--brotli`|When enabled 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. |`false`|
|`-e` or `--ext` |Default file extension if none supplied |`html` |
|`-s` or `--silent` |Suppress log messages from output | |
|`--coop` |Enable COOP via the `Cross-Origin-Opener-Policy` header | |
|`--cors` |Enable CORS via the `Access-Control-Allow-Origin` header | |
|`-o [path]` |Open browser window after starting the server. Optionally provide a URL path to open. e.g.: -o /other/dir/ | |
|`-c` |Set cache time (in seconds) for cache-control max-age header, e.g. `-c10` for 10 seconds. To disable caching, use `-c-1`.|`3600` |
Expand Down Expand Up @@ -118,6 +119,7 @@ This is what should be output if successful:
Starting up http-server, serving ./ through https

http-server settings:
COOP: disabled
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Expand Down
10 changes: 10 additions & 0 deletions bin/http-server
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ if (argv.h || argv.help) {
' If both brotli and gzip are enabled, brotli takes precedence',
' -e --ext Default file extension if none supplied [none]',
' -s --silent Suppress log messages from output',
' --coop[=mode] Enable COOP via the "Cross-Origin-Opener-Policy" header',
' Optionally provide COOP mode.',
' --cors[=headers] Enable CORS via the "Access-Control-Allow-Origin" header',
' Optionally provide CORS headers list separated by commas',
' -o [path] Open browser window after starting the server.',
Expand Down Expand Up @@ -156,6 +158,13 @@ function listen(port) {
password: argv.password || process.env.NODE_HTTP_SERVER_PASSWORD
};

if (argv.coop) {
options.coop = true;
if (typeof argv.coop === 'string') {
options.coopHeader = argv.coop;
}
}

if (argv.cors) {
options.cors = true;
if (typeof argv.cors === 'string') {
Expand Down Expand Up @@ -209,6 +218,7 @@ function listen(port) {

logger.info([
chalk.yellow('\nhttp-server settings: '),
([chalk.yellow('COOP: '), argv.coop ? chalk.cyan(argv.coop) : chalk.red('disabled')].join('')),
([chalk.yellow('CORS: '), argv.cors ? chalk.cyan(argv.cors) : chalk.red('disabled')].join('')),
([chalk.yellow('Cache: '), argv.c ? (argv.c === '-1' ? chalk.red('disabled') : chalk.cyan(argv.c + ' seconds')) : chalk.cyan('3600 seconds')].join('')),
([chalk.yellow('Connection Timeout: '), argv.t === '0' ? chalk.red('disabled') : (argv.t ? chalk.cyan(argv.t + ' seconds') : chalk.cyan('120 seconds'))].join('')),
Expand Down
6 changes: 6 additions & 0 deletions doc/http-server.1
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ Default file extension is none is provided.
.BI \-s ", " \-\-silent
Suppress log messages from output.

.TP
.BI \-\-coop " " [\fIMODE\fR]
Enable COOP via the "Cross-Origin-Opener-Policy" header and sets
the "Cross-Origin-Embedder-Policy" header to "require-corp".
Optionally provide COOP mode which defaults to "same-origin".

.TP
.BI \-\-cors " " [\fIHEADERS\fR]
Enable CORS via the "Access-Control-Allow-Origin" header.
Expand Down
1 change: 1 addition & 0 deletions lib/core/aliases.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"hidePermissions": ["hidePermissions", "hidepermissions", "hide-permissions"],
"si": [ "si", "index" ],
"handleError": [ "handleError", "handleerror" ],
"coop": [ "coop", "COOP" ],
"cors": [ "cors", "CORS" ],
"headers": [ "H", "header", "headers" ],
"contentType": [ "contentType", "contenttype", "content-type" ],
Expand Down
1 change: 1 addition & 0 deletions lib/core/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"hidePermissions": false,
"si": false,
"cache": "max-age=3600",
"coop": false,
"cors": false,
"gzip": true,
"brotli": false,
Expand Down
8 changes: 8 additions & 0 deletions lib/core/opts.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ module.exports = (opts) => {
return false;
});

aliases.coop.forEach((k) => {
if (isDeclared(k) && opts[k]) {
handleOptionsMethod = true;
headers['Cross-Origin-Opener-Policy'] = 'same-origin';
headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
}
});

aliases.cors.forEach((k) => {
if (isDeclared(k) && opts[k]) {
handleOptionsMethod = true;
Expand Down
5 changes: 5 additions & 0 deletions lib/http-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ function HttpServer(options) {
});
}

if (options.coop) {
this.headers['Cross-Origin-Opener-Policy'] = options.coopHeader || 'same-origin';
this.headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
}

if (options.cors) {
this.headers['Access-Control-Allow-Origin'] = '*';
this.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Range';
Expand Down
118 changes: 118 additions & 0 deletions test/coop.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
'use strict';

const test = require('tap').test;
const server = require('../lib/core');
const http = require('http');
const path = require('path');
const request = require('request');

const root = path.join(__dirname, 'public');

test('coop defaults to false', (t) => {
t.plan(4);

const httpServer = http.createServer(
server({
root,
autoIndex: true,
defaultExt: 'html',
})
);

httpServer.listen(() => {
const port = httpServer.address().port;
const uri = `http://localhost:${port}/subdir/index.html`;

request.get({ uri }, (err, res) => {
t.ifError(err);
t.equal(res.statusCode, 200);
t.type(res.headers['cross-origin-opener-policy'], 'undefined');
t.type(res.headers['cross-origin-embedder-policy'], 'undefined');
});
});
t.once('end', () => {
httpServer.close();
});
});

test('coop set to false', (t) => {
t.plan(4);

const httpServer = http.createServer(
server({
root,
coop: false,
autoIndex: true,
defaultExt: 'html',
})
);

httpServer.listen(() => {
const port = httpServer.address().port;
const uri = `http://localhost:${port}/subdir/index.html`;

request.get({ uri }, (err, res) => {
t.ifError(err);
t.equal(res.statusCode, 200);
t.type(res.headers['cross-origin-opener-policy'], 'undefined');
t.type(res.headers['cross-origin-embedder-policy'], 'undefined');
});
});
t.once('end', () => {
httpServer.close();
});
});

test('coop set to true', (t) => {
t.plan(4);

const httpServer = http.createServer(
server({
root,
coop: true,
autoIndex: true,
defaultExt: 'html',
})
);

httpServer.listen(() => {
const port = httpServer.address().port;
const uri = `http://localhost:${port}/subdir/index.html`;
request.get({ uri }, (err, res) => {
t.ifError(err);
t.equal(res.statusCode, 200);
t.equal(res.headers['cross-origin-opener-policy'], 'same-origin');
t.equal(res.headers['cross-origin-embedder-policy'], 'require-corp');
});
});
t.once('end', () => {
httpServer.close();
});
});

test('COOP set to true', (t) => {
t.plan(4);

const httpServer = http.createServer(
server({
root,
COOP: true,
autoIndex: true,
defaultExt: 'html',
})
);

httpServer.listen(() => {
const port = httpServer.address().port;
const uri = `http://localhost:${port}/subdir/index.html`;
request.get({ uri }, (err, res) => {
t.ifError(err);
t.equal(res.statusCode, 200);
t.equal(res.headers['cross-origin-opener-policy'], 'same-origin');
t.equal(res.headers['cross-origin-embedder-policy'], 'require-corp');
});
});
t.once('end', () => {
httpServer.close();
});
});
3 changes: 3 additions & 0 deletions test/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ test('http-server main', (t) => {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'
},
coop: true,
cors: true,
corsHeaders: 'X-Test',
ext: true,
Expand Down Expand Up @@ -63,6 +64,8 @@ test('http-server main', (t) => {
// Custom headers
t.equal(res.headers['access-control-allow-origin'], '*');
t.equal(res.headers['access-control-allow-credentials'], 'true');
t.equal(res.headers['cross-origin-opener-policy'], 'same-origin');
t.equal(res.headers['cross-origin-embedder-policy'], 'require-corp');
}).catch(err => t.fail(err.toString())),

// Get robots
Expand Down