From 55122f3ab3d67c39f2984673e7734535b04d911e Mon Sep 17 00:00:00 2001 From: Jeremy Keys Date: Fri, 22 Mar 2024 18:48:32 -0600 Subject: [PATCH] feat: Add callback for configuring Vary header --- lib/index.js | 33 +++++---- package.json | 2 +- test/test.js | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 14 deletions(-) diff --git a/lib/index.js b/lib/index.js index ad899ca..d433980 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,7 +9,8 @@ origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', preflightContinue: false, - optionsSuccessStatus: 204 + optionsSuccessStatus: 204, + shouldSetVaryHeader: function(req, header) { return true } }; function isString(s) { @@ -50,10 +51,12 @@ key: 'Access-Control-Allow-Origin', value: options.origin }]); - headers.push([{ - key: 'Vary', - value: 'Origin' - }]); + if (options.shouldSetVaryHeader(req, 'Origin')) { + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } } else { isAllowed = isOriginAllowed(requestOrigin, options.origin); // reflect origin @@ -61,10 +64,12 @@ key: 'Access-Control-Allow-Origin', value: isAllowed ? requestOrigin : false }]); - headers.push([{ - key: 'Vary', - value: 'Origin' - }]); + if (options.shouldSetVaryHeader(req, 'Origin')) { + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } } return headers; @@ -97,10 +102,12 @@ if (!allowedHeaders) { allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers - headers.push([{ - key: 'Vary', - value: 'Access-Control-Request-Headers' - }]); + if (options.shouldSetVaryHeader(req, 'Access-Control-Request-Headers')) { + headers.push([{ + key: 'Vary', + value: 'Access-Control-Request-Headers' + }]); + } } else if (allowedHeaders.join) { allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string } diff --git a/package.json b/package.json index 3368389..9b755d1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cors", "description": "Node.js CORS middleware", - "version": "2.8.5", + "version": "2.9.0", "author": "Troy Goode (https://github.com/troygoode/)", "license": "MIT", "keywords": [ diff --git a/test/test.js b/test/test.js index f2a2e94..21a8cd3 100644 --- a/test/test.js +++ b/test/test.js @@ -150,6 +150,198 @@ var util = require('util') }) }); + describe('shouldSetVaryHeader', function() { + it('Vary: "Access-Control-Request-Headers" header is set by default for pre-flight requests', function (done) { + var cb = after(1, done) + + var req = new FakeRequest('OPTIONS') + var res = new FakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + + + res.on('finish', function () { + assert.equal(res.statusCode, 204) + assert.equal(res.getHeader('Vary'), 'Access-Control-Request-Headers') + cb() + }) + + cors()(req, res, function (err) { + cb(err || new Error('should not be called')) + }) + }) + + it('Vary: "Origin, Access-Control-Request-Headers" header is set by default for pre-flight requests with origins', function (done) { + var cb = after(1, done) + + var req = new FakeRequest('OPTIONS') + var res = new FakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + var options = { + origin: 'https://example.com' + } + + res.on('finish', function () { + assert.equal(res.statusCode, 204) + assert.equal(res.getHeader('Vary'), 'Origin, Access-Control-Request-Headers') + cb() + }) + + cors(options)(req, res, function (err) { + cb(err || new Error('should not be called')) + }) + }) + + it('Vary: "Access-Control-Request-Headers" header is set for pre-flight requests and shouldSetVaryHeader(req, \'Origin\') configured to return false with origins', function (done) { + var cb = after(1, done) + + var req = new FakeRequest('OPTIONS') + var res = new FakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + var options = { + origin: 'https://example.com', + shouldSetVaryHeader: function(req, header) { + if (header === 'Access-Control-Request-Headers') { + return true + } + + if (req.originalUrl.startsWith('/images') && header === 'Origin') { + return false + } else { + return true + } + } + } + + res.on('finish', function () { + assert.equal(res.statusCode, 204) + assert.equal(res.getHeader('Vary'), 'Access-Control-Request-Headers') + cb() + }) + + cors(options)(req, res, function (err) { + cb(err || new Error('should not be called')) + }) + }) + + it('Vary: "Access-Control-Request-Headers" header is not set for pre-flight requests when shouldSetVaryHeader(req, \'Access-Control-Request-Headers\') configured to return false', function (done) { + var cb = after(1, done) + + var req = new FakeRequest('OPTIONS') + var res = new FakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + var options = { + origin: 'https://example.com', + shouldSetVaryHeader: function(req, header) { + if (header === 'Access-Control-Request-Headers') { + return false + } + + if (req.originalUrl.startsWith('/images') && header === 'Origin') { + return false + } else { + return true + } + } + } + + res.on('finish', function () { + assert.equal(res.statusCode, 204) + assert.equal(res.getHeader('Vary'), undefined) + cb() + }) + + cors(options)(req, res, function (err) { + cb(err || new Error('should not be called')) + }) + }) + + it('Vary: "Origin" header is set by default', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + var options = { + origin: 'http://example.com' + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), 'Origin') + done(); + }; + + cors(options)(req, res, next) + }) + + it('Vary: "Origin" header is not set when shouldSetVaryHeader(req, \'Origin\') configured to return false', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + + var options = { + origin: ['http://example.com', 'http://foo.com'], + shouldSetVaryHeader: function(req, header) { + if (header === 'Access-Control-Request-Headers') { + return true + } + + if (req.originalUrl.startsWith('/images') && header === 'Origin') { + return false + } else { + return true + } + } + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), undefined) + done(); + }; + + cors(options)(req, res, next) + }) + + it('Vary: "Origin" header is set when shouldSetVaryHeader(req, \'Origin\') configured to return true', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + + req.originalUrl = '/some/other/path' + + var options = { + origin: ['http://example.com', 'http://foo.com'], + shouldSetVaryHeader: function(req, header) { + if (header === 'Access-Control-Request-Headers') { + return true + } + + if (req.originalUrl.startsWith('/images') && header === 'Origin') { + return false + } else { + return true + } + } + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), 'Origin') + done(); + }; + + cors(options)(req, res, next) + }) + }) + describe('passing static options', function () { it('overrides defaults', function (done) { var cb = after(1, done)