http: try to avoid lowercasing incoming headers
PR-URL: https://github.com/nodejs/node/pull/10558 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Evan Lucas <evanlucas@me.com>
This commit is contained in:
parent
c77ed327d9
commit
73d9445782
@ -119,6 +119,146 @@ function _addHeaderLines(headers, n) {
|
||||
}
|
||||
|
||||
|
||||
// This function is used to help avoid the lowercasing of a field name if it
|
||||
// matches a 'traditional cased' version of a field name. It then returns the
|
||||
// lowercased name to both avoid calling toLowerCase() a second time and to
|
||||
// indicate whether the field was a 'no duplicates' field. If a field is not a
|
||||
// 'no duplicates' field, a `0` byte is prepended as a flag. The one exception
|
||||
// to this is the Set-Cookie header which is indicated by a `1` byte flag, since
|
||||
// it is an 'array' field and thus is treated differently in _addHeaderLines().
|
||||
// TODO: perhaps http_parser could be returning both raw and lowercased versions
|
||||
// of known header names to avoid us having to call toLowerCase() for those
|
||||
// headers.
|
||||
/* eslint-disable max-len */
|
||||
// 'array' header list is taken from:
|
||||
// https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
|
||||
/* eslint-enable max-len */
|
||||
function matchKnownFields(field) {
|
||||
var low = false;
|
||||
while (true) {
|
||||
switch (field) {
|
||||
case 'Content-Type':
|
||||
case 'content-type':
|
||||
return 'content-type';
|
||||
case 'Content-Length':
|
||||
case 'content-length':
|
||||
return 'content-length';
|
||||
case 'User-Agent':
|
||||
case 'user-agent':
|
||||
return 'user-agent';
|
||||
case 'Referer':
|
||||
case 'referer':
|
||||
return 'referer';
|
||||
case 'Host':
|
||||
case 'host':
|
||||
return 'host';
|
||||
case 'Authorization':
|
||||
case 'authorization':
|
||||
return 'authorization';
|
||||
case 'Proxy-Authorization':
|
||||
case 'proxy-authorization':
|
||||
return 'proxy-authorization';
|
||||
case 'If-Modified-Since':
|
||||
case 'if-modified-since':
|
||||
return 'if-modified-since';
|
||||
case 'If-Unmodified-Since':
|
||||
case 'if-unmodified-since':
|
||||
return 'if-unmodified-since';
|
||||
case 'From':
|
||||
case 'from':
|
||||
return 'from';
|
||||
case 'Location':
|
||||
case 'location':
|
||||
return 'location';
|
||||
case 'Max-Forwards':
|
||||
case 'max-forwards':
|
||||
return 'max-forwards';
|
||||
case 'Retry-After':
|
||||
case 'retry-after':
|
||||
return 'retry-after';
|
||||
case 'ETag':
|
||||
case 'etag':
|
||||
return 'etag';
|
||||
case 'Last-Modified':
|
||||
case 'last-modified':
|
||||
return 'last-modified';
|
||||
case 'Server':
|
||||
case 'server':
|
||||
return 'server';
|
||||
case 'Age':
|
||||
case 'age':
|
||||
return 'age';
|
||||
case 'Expires':
|
||||
case 'expires':
|
||||
return 'expires';
|
||||
case 'Set-Cookie':
|
||||
case 'set-cookie':
|
||||
return '\u0001';
|
||||
// The fields below are not used in _addHeaderLine(), but they are common
|
||||
// headers where we can avoid toLowerCase() if the mixed or lower case
|
||||
// versions match the first time through.
|
||||
case 'Transfer-Encoding':
|
||||
case 'transfer-encoding':
|
||||
return '\u0000transfer-encoding';
|
||||
case 'Date':
|
||||
case 'date':
|
||||
return '\u0000date';
|
||||
case 'Connection':
|
||||
case 'connection':
|
||||
return '\u0000connection';
|
||||
case 'Cache-Control':
|
||||
case 'cache-control':
|
||||
return '\u0000cache-control';
|
||||
case 'Vary':
|
||||
case 'vary':
|
||||
return '\u0000vary';
|
||||
case 'Content-Encoding':
|
||||
case 'content-encoding':
|
||||
return '\u0000content-encoding';
|
||||
case 'Cookie':
|
||||
case 'cookie':
|
||||
return '\u0000cookie';
|
||||
case 'Origin':
|
||||
case 'origin':
|
||||
return '\u0000origin';
|
||||
case 'Upgrade':
|
||||
case 'upgrade':
|
||||
return '\u0000upgrade';
|
||||
case 'Expect':
|
||||
case 'expect':
|
||||
return '\u0000expect';
|
||||
case 'If-Match':
|
||||
case 'if-match':
|
||||
return '\u0000if-match';
|
||||
case 'If-None-Match':
|
||||
case 'if-none-match':
|
||||
return '\u0000if-none-match';
|
||||
case 'Accept':
|
||||
case 'accept':
|
||||
return '\u0000accept';
|
||||
case 'Accept-Encoding':
|
||||
case 'accept-encoding':
|
||||
return '\u0000accept-encoding';
|
||||
case 'Accept-Language':
|
||||
case 'accept-language':
|
||||
return '\u0000accept-language';
|
||||
case 'X-Forwarded-For':
|
||||
case 'x-forwarded-for':
|
||||
return '\u0000x-forwarded-for';
|
||||
case 'X-Forwarded-Host':
|
||||
case 'x-forwarded-host':
|
||||
return '\u0000x-forwarded-host';
|
||||
case 'X-Forwarded-Proto':
|
||||
case 'x-forwarded-proto':
|
||||
return '\u0000x-forwarded-proto';
|
||||
default:
|
||||
if (low)
|
||||
return '\u0000' + field;
|
||||
field = field.toLowerCase();
|
||||
low = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add the given (field, value) pair to the message
|
||||
//
|
||||
// Per RFC2616, section 4.2 it is acceptable to join multiple instances of the
|
||||
@ -128,51 +268,27 @@ function _addHeaderLines(headers, n) {
|
||||
// always joined.
|
||||
IncomingMessage.prototype._addHeaderLine = _addHeaderLine;
|
||||
function _addHeaderLine(field, value, dest) {
|
||||
field = field.toLowerCase();
|
||||
switch (field) {
|
||||
// Array headers:
|
||||
case 'set-cookie':
|
||||
if (dest[field] !== undefined) {
|
||||
dest[field].push(value);
|
||||
} else {
|
||||
dest[field] = [value];
|
||||
}
|
||||
break;
|
||||
|
||||
/* eslint-disable max-len */
|
||||
// list is taken from:
|
||||
// https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
|
||||
/* eslint-enable max-len */
|
||||
case 'content-type':
|
||||
case 'content-length':
|
||||
case 'user-agent':
|
||||
case 'referer':
|
||||
case 'host':
|
||||
case 'authorization':
|
||||
case 'proxy-authorization':
|
||||
case 'if-modified-since':
|
||||
case 'if-unmodified-since':
|
||||
case 'from':
|
||||
case 'location':
|
||||
case 'max-forwards':
|
||||
case 'retry-after':
|
||||
case 'etag':
|
||||
case 'last-modified':
|
||||
case 'server':
|
||||
case 'age':
|
||||
case 'expires':
|
||||
// drop duplicates
|
||||
if (dest[field] === undefined)
|
||||
dest[field] = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
// make comma-separated list
|
||||
if (typeof dest[field] === 'string') {
|
||||
dest[field] += ', ' + value;
|
||||
} else {
|
||||
dest[field] = value;
|
||||
}
|
||||
field = matchKnownFields(field);
|
||||
var flag = field.charCodeAt(0);
|
||||
if (flag === 0) {
|
||||
field = field.slice(1);
|
||||
// Make comma-separated list
|
||||
if (typeof dest[field] === 'string') {
|
||||
dest[field] += ', ' + value;
|
||||
} else {
|
||||
dest[field] = value;
|
||||
}
|
||||
} else if (flag === 1) {
|
||||
// Array header -- only Set-Cookie at the moment
|
||||
if (dest['set-cookie'] !== undefined) {
|
||||
dest['set-cookie'].push(value);
|
||||
} else {
|
||||
dest['set-cookie'] = [value];
|
||||
}
|
||||
} else {
|
||||
// Drop duplicates
|
||||
if (dest[field] === undefined)
|
||||
dest[field] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user