diff --git a/README.md b/README.md index 5811513a4..bedc42934 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ Using `npx` you can run the script without installing it first: `--cors` Enable CORS via the `Access-Control-Allow-Origin` header +`--credentials` Cookie credentials can be transferred as part of a CORS request (enables `--cors` automatically) + `-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 (defaults to `3600`). To disable caching, use `-c-1`. diff --git a/bin/http-server b/bin/http-server index 24b54dd93..435824e9a 100755 --- a/bin/http-server +++ b/bin/http-server @@ -10,6 +10,7 @@ var colors = require('colors/safe'), argv = require('optimist') .boolean('cors') .boolean('log-ip') + .boolean('credentials') .argv; var ifaces = os.networkInterfaces(); @@ -30,6 +31,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', + ' --credentials Cookie credentials can be transferred as part of a CORS request', + ' Enables --cors automatically', ' -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.', @@ -129,6 +132,10 @@ function listen(port) { } } + if (argv.credentials) { + options.credentials = true; + } + if (ssl) { options.https = { cert: argv.C || argv.cert || 'cert.pem', diff --git a/lib/http-server.js b/lib/http-server.js index 4cdd1b1c8..cd8a2c0ec 100644 --- a/lib/http-server.js +++ b/lib/http-server.js @@ -96,16 +96,24 @@ function HttpServer(options) { }); } - if (options.cors) { + if (options.cors || options.credentials) { this.headers['Access-Control-Allow-Origin'] = '*'; this.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Range'; if (options.corsHeaders) { options.corsHeaders.split(/\s*,\s*/) .forEach(function (h) { this.headers['Access-Control-Allow-Headers'] += ', ' + h; }, this); } - before.push(corser.create(options.corsHeaders ? { + + var corserOptions = options.corsHeaders ? { requestHeaders: this.headers['Access-Control-Allow-Headers'].split(/\s*,\s*/) - } : null)); + } : {}; + + if (options.credentials) { + corserOptions.supportsCredentials = true; + this.headers['Access-Control-Allow-Credentials'] = 'true'; + } + + before.push(corser.create(corserOptions)); } if (options.robots) { diff --git a/test/http-server-test.js b/test/http-server-test.js index 0a7f897ff..4a543d8ab 100644 --- a/test/http-server-test.js +++ b/test/http-server-test.js @@ -155,6 +155,43 @@ vows.describe('http-server').addBatch({ }, 'response Access-Control-Allow-Headers should contain X-Test': function (err, res) { assert.ok(res.headers['access-control-allow-headers'].split(/\s*,\s*/g).indexOf('X-Test') >= 0, 204); + }, + 'response Access-Control-Allow-Credentials should not be presented': function (err, res) { + assert.equal(res.headers['access-control-allow-credentials'], undefined); + } + } + }, + 'When credentials is enabled': { + topic: function () { + var server = httpServer.createServer({ + root: root, + credentials: true, + corsHeaders: 'X-Test' + }); + server.listen(8085); + this.callback(null, server); + }, + 'and given OPTIONS request': { + topic: function () { + request({ + method: 'OPTIONS', + uri: 'http://127.0.0.1:8085/', + headers: { + 'Access-Control-Request-Method': 'GET', + Origin: 'http://example.com', + 'Access-Control-Request-Headers': 'Foobar', + Cookie: 'cookie' + } + }, this.callback); + }, + 'status code should be 204': function (err, res) { + assert.equal(res.statusCode, 204); + }, + 'response Access-Control-Allow-Headers should contain X-Test': function (err, res) { + assert.ok(res.headers['access-control-allow-headers'].split(/\s*,\s*/g).indexOf('X-Test') >= 0, 204); + }, + 'response Access-Control-Allow-Credentials should be true': function (err, res) { + assert.equal(res.headers['access-control-allow-credentials'], 'true'); } } },