Skip to content

Commit

Permalink
http: improve performance for incoming headers
Browse files Browse the repository at this point in the history
PR-URL: nodejs#26041
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
  • Loading branch information
starkwang committed Feb 13, 2019
1 parent 8ecf313 commit da0dc51
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 118 deletions.
5 changes: 4 additions & 1 deletion benchmark/_http-benchmarkers.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ class AutocannonBenchmarker {
'-c', options.connections,
'-j',
'-n',
`http://127.0.0.1:${options.port}${options.path}`,
];
for (const field in options.headers) {
args.push('-H', `${field}=${options.headers[field]}`);
}
args.push(`http://127.0.0.1:${options.port}${options.path}`);
const child = child_process.spawn(this.executable, args);
return child;
}
Expand Down
35 changes: 35 additions & 0 deletions benchmark/http/incoming_headers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';
const common = require('../common.js');
const http = require('http');

const bench = common.createBenchmark(main, {
// unicode confuses ab on os x.
c: [50, 500],
headerDuplicates: [0, 5, 20]
});

function main({ c, headerDuplicates }) {
const server = http.createServer((req, res) => {
res.end();
});

server.listen(common.PORT, () => {
const headers = {
'Content-Type': 'text/plain',
'Accept': 'text/plain',
'User-Agent': 'nodejs-benchmark',
'Date': new Date().toString(),
'Cache-Control': 'no-cache'
};
for (let i = 0; i < headerDuplicates; i++) {
headers[`foo${i}`] = `some header value ${i}`;
}
bench.http({
path: '/',
connections: c,
headers
}, () => {
server.close();
});
});
}
202 changes: 85 additions & 117 deletions lib/_http_incoming.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,133 +134,101 @@ function _addHeaderLines(headers, n) {
// 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.

// 'array' header list is taken from:
// https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
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':
function matchKnownFields(field, lowercased) {
switch (field.length) {
case 3:
if (field === 'Age' || field === 'age') return 'age';
break;
case 4:
if (field === 'Host' || field === 'host') return 'host';
if (field === 'From' || field === 'from') return 'from';
if (field === 'ETag' || field === 'etag') return 'etag';
if (field === 'Date' || field === 'date') return '\u0000date';
if (field === 'Vary' || field === 'vary') return '\u0000vary';
break;
case 6:
if (field === 'Server' || field === 'server') return 'server';
if (field === 'Cookie' || field === 'cookie') return '\u0002cookie';
if (field === 'Origin' || field === 'origin') return '\u0000origin';
if (field === 'Expect' || field === 'expect') return '\u0000expect';
if (field === 'Accept' || field === 'accept') return '\u0000accept';
break;
case 7:
if (field === 'Referer' || field === 'referer') return 'referer';
if (field === 'Expires' || field === 'expires') return 'expires';
if (field === 'Upgrade' || field === 'upgrade') return '\u0000upgrade';
break;
case 8:
if (field === 'Location' || field === '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':
if (field === 'If-Match' || field === 'if-match')
return '\u0000if-match';
break;
case 10:
if (field === 'User-Agent' || field === 'user-agent')
return 'user-agent';
if (field === 'Set-Cookie' || field === 'set-cookie')
return '\u0001';
case 'Cookie':
case 'cookie':
return '\u0002cookie';
// 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':
if (field === 'Connection' || field === 'connection')
return '\u0000connection';
case 'Cache-Control':
case 'cache-control':
break;
case 11:
if (field === 'Retry-After' || field === 'retry-after')
return 'retry-after';
break;
case 12:
if (field === 'Content-Type' || field === 'content-type')
return 'content-type';
if (field === 'Max-Forwards' || field === 'max-forwards')
return 'max-forwards';
break;
case 13:
if (field === 'Authorization' || field === 'authorization')
return 'authorization';
if (field === 'Last-Modified' || field === 'last-modified')
return 'last-modified';
if (field === 'Cache-Control' || field === 'cache-control')
return '\u0000cache-control';
case 'Vary':
case 'vary':
return '\u0000vary';
case 'Content-Encoding':
case 'content-encoding':
return '\u0000content-encoding';
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':
if (field === 'If-None-Match' || field === 'if-none-match')
return '\u0000if-none-match';
case 'Accept':
case 'accept':
return '\u0000accept';
case 'Accept-Encoding':
case 'accept-encoding':
break;
case 14:
if (field === 'Content-Length' || field === 'content-length')
return 'content-length';
break;
case 15:
if (field === 'Accept-Encoding' || field === 'accept-encoding')
return '\u0000accept-encoding';
case 'Accept-Language':
case 'accept-language':
if (field === 'Accept-Language' || field === 'accept-language')
return '\u0000accept-language';
case 'X-Forwarded-For':
case 'x-forwarded-for':
if (field === 'X-Forwarded-For' || field === 'x-forwarded-for')
return '\u0000x-forwarded-for';
case 'X-Forwarded-Host':
case 'x-forwarded-host':
break;
case 16:
if (field === 'Content-Encoding' || field === 'content-encoding')
return '\u0000content-encoding';
if (field === 'X-Forwarded-Host' || field === 'x-forwarded-host')
return '\u0000x-forwarded-host';
case 'X-Forwarded-Proto':
case 'x-forwarded-proto':
break;
case 17:
if (field === 'If-Modified-Since' || field === 'if-modified-since')
return 'if-modified-since';
if (field === 'Transfer-Encoding' || field === 'transfer-encoding')
return '\u0000transfer-encoding';
if (field === 'X-Forwarded-Proto' || field === 'x-forwarded-proto')
return '\u0000x-forwarded-proto';
default:
if (low)
return '\u0000' + field;
field = field.toLowerCase();
low = true;
}
break;
case 19:
if (field === 'Proxy-Authorization' || field === 'proxy-authorization')
return 'proxy-authorization';
if (field === 'If-Unmodified-Since' || field === 'if-unmodified-since')
return 'if-unmodified-since';
break;
}
if (lowercased) {
return '\u0000' + field;
} else {
return matchKnownFields(field.toLowerCase(), true);
}
}
// Add the given (field, value) pair to the message
Expand Down

0 comments on commit da0dc51

Please sign in to comment.