Skip to content
Merged
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
41 changes: 29 additions & 12 deletions lib/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@
*/
const utils = require('./utils');

const REFERER_HEADER_NAMES = ['referer', 'referrer'];

/**
* Get a header value from the headers object.
* Because headers can be set in multiple ways, their names can be uppercased and lowercased.
*
* @param {Object} headers
* @param {string} name
* @returns
*/
function getHeaderValue(headers, name) {
const lowerName = name.toLowerCase();

return headers[
Object.keys(headers).find((key) => {
const lowerKey = key.toLowerCase();
return (
lowerKey === lowerName ||
(REFERER_HEADER_NAMES.includes(lowerKey) && REFERER_HEADER_NAMES.includes(lowerName))
);
})
];
}

/**
* Creates a Headers object that implements both Express.js style access
* and the Web API Headers interface
Expand All @@ -18,24 +42,17 @@ function createHeaders(headers = {}) {
// Handle Headers interface methods
switch (prop) {
case 'get':
return (name) => {
const lowerName = name.toLowerCase();
// Special case for referer/referrer
if (lowerName === 'referer' || lowerName === 'referrer') {
return target.referrer || target.referer;
}
return target[lowerName];
};
return (name) => getHeaderValue(target, name);
case 'getAll':
return (name) => {
const value = target[name.toLowerCase()];
const value = getHeaderValue(target, name);
if (!value) {
return [];
}
return Array.isArray(value) ? value : [value];
};
case 'has':
return (name) => name.toLowerCase() in target;
return (name) => getHeaderValue(target, name) !== undefined;
case 'set':
return (name, value) => {
// eslint-disable-next-line no-param-reassign
Expand Down Expand Up @@ -82,7 +99,7 @@ function createHeaders(headers = {}) {
}
);
default:
return target[typeof prop === 'string' ? prop.toLowerCase() : prop];
return typeof prop === 'string' ? getHeaderValue(target, prop) : target[prop];
}
},
set(target, prop, value) {
Expand Down Expand Up @@ -117,4 +134,4 @@ function createHeaders(headers = {}) {
});
}

module.exports = { createHeaders };
module.exports = { createHeaders, getHeaderValue };
4 changes: 2 additions & 2 deletions lib/mockRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const accepts = require('accepts');
const parseRange = require('range-parser');
let { EventEmitter } = require('events');
const querystring = require('querystring');
const { createHeaders } = require('./headers');
const { createHeaders, getHeaderValue } = require('./headers');

const standardRequestOptions = [
'method',
Expand Down Expand Up @@ -130,7 +130,7 @@ function createRequest(options = {}) {
* @api public
*/
mockRequest.getHeader = function getHeader(name) {
return mockRequest.headers.get(name);
return getHeaderValue(mockRequest.headers, name);
};
mockRequest.header = mockRequest.getHeader;
mockRequest.get = mockRequest.getHeader;
Expand Down
42 changes: 35 additions & 7 deletions test/lib/mockRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,18 +304,46 @@ describe('mockRequest', () => {
});

describe('.get()/.header()', () => {
it('should return header, when set', () => {
it('should return header, when set in constructor', () => {
const options = {
headers: {
key: 'value'
'Content-type': 'value'
}
};

const request = mockRequest.createRequest(options);
expect(request.get('key')).to.equal('value');
expect(request.header('key')).to.equal('value');
expect(request.headers.get('key')).to.equal('value');
expect(request.getHeader('key')).to.equal('value');
expect(request.headers.key).to.equal('value');
expect(request.get('Content-type')).to.equal('value');
expect(request.header('Content-type')).to.equal('value');
expect(request.headers.get('Content-type')).to.equal('value');
expect(request.getHeader('Content-type')).to.equal('value');
expect(request.headers['Content-type']).to.equal('value');
});

it('should return header, when set explicitly', () => {
const request = mockRequest.createRequest();

request.headers['Content-type'] = 'value';

expect(request.get('Content-type')).to.equal('value');
expect(request.header('Content-type')).to.equal('value');
expect(request.headers.get('Content-type')).to.equal('value');
expect(request.getHeader('Content-type')).to.equal('value');
expect(request.headers['Content-type']).to.equal('value');
});

it('should return header, when set as an object (deprecated)', () => {
const request = mockRequest.createRequest();

// setting headers as an object is officially supported by Express
// @ts-expect-error
request.headers = { 'Content-type': 'value' };

expect(request.get('Content-type')).to.equal('value');
expect(request.header('Content-type')).to.equal('value');
expect(request.getHeader('Content-type')).to.equal('value');
expect(request.headers['Content-type']).to.equal('value');

// request.headers.get() is not working in this case
});

it('should return referer, when request as referrer', () => {
Expand Down