diff --git a/README.md b/README.md index eeb3f9868..d288e5f82 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,9 @@ Using `npx` you can run the script without installing it first: `-s` or `--silent` Suppress log messages from output -`--cors` Enable CORS via the `Access-Control-Allow-Origin` header +`--cors` Enable CORS via the `Access-Control-Allow-Origin` header. Cannot be specified alongside `--coi`. + +`--coi` Enable cross origin isolation via the `Cross-Origin-Embedder-Policy:required-corp` and `Cross-Origin-Opener-Policy:same-origin` headers. Cannot be specified alongside `--cors`. `-o [path]` Open browser window after starting the server. Optionally provide a URL path to open. e.g.: -o /other/dir/ diff --git a/bin/http-server b/bin/http-server index 42ebf651f..7628135fd 100755 --- a/bin/http-server +++ b/bin/http-server @@ -29,6 +29,8 @@ if (argv.h || argv.help) { ' -s --silent Suppress log messages from output', ' --cors[=headers] Enable CORS via the "Access-Control-Allow-Origin" header', ' Optionally provide CORS headers list separated by commas', + ' --coi Enable cross origin isolation via the "Cross-Origin-Embedder-Policy:required-corp"', + ' and "Cross-Origin-Opener-Policy:same-origin" headers.', ' -o [path] Open browser window after starting the server.', ' Optionally provide a URL path to open the browser window to.', ' -c Cache time (max-age) in seconds [3600], e.g. -c10 for 10 seconds.', @@ -131,12 +133,20 @@ function listen(port) { password: argv.password || process.env.NODE_HTTP_SERVER_PASSWORD }; + if (argv.cors && argv.coi) { + logger.info(colors.red('Error: conflicting arguments: --cors and --coi')); + process.exit(1); + } + if (argv.cors) { options.cors = true; if (typeof argv.cors === 'string') { options.corsHeaders = argv.cors; } } + else if (argv.coi) { + options.coi = true; + } if (ssl) { options.https = { diff --git a/lib/http-server.js b/lib/http-server.js index 8bafdf8e9..dee0f7730 100644 --- a/lib/http-server.js +++ b/lib/http-server.js @@ -134,6 +134,11 @@ function HttpServer(options) { } : null)); } + if (options.coi) { + this.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'; + this.headers['Cross-Origin-Opener-Policy'] = 'same-origin'; + } + if (options.robots) { before.push(function (req, res) { if (req.url === '/robots.txt') { diff --git a/test/http-server-test.js b/test/http-server-test.js index 6d5fce462..f7516eedc 100644 --- a/test/http-server-test.js +++ b/test/http-server-test.js @@ -167,6 +167,26 @@ vows.describe('http-server').addBatch({ server.close(); } }, + 'When cross origin isolation is enabled,\n': { + topic: function () { + var server = httpServer.createServer({ + root: root, + coi: true + }); + + server.listen(8080); + this.callback(null, server); + }, + 'and a page is requested': { + topic: function () { + request('http://127.0.0.1:8080/', this.callback); + }, + 'response should have cross origin isolation headers set': function (err, res) { + assert.equal(res.headers['Cross-Origin-Embedder-Policy'], 'require-corp'); + assert.equal(res.headers['Cross-Origin-Opener-Policy'], 'same-origin'); + } + } + }, 'When gzip and brotli compression is enabled and a compressed file is available': { topic: function () { var server = httpServer.createServer({