diff --git a/benchmark/_http-benchmarkers.js b/benchmark/_http-benchmarkers.js index f4566284547ac4..a4d623003947eb 100644 --- a/benchmark/_http-benchmarkers.js +++ b/benchmark/_http-benchmarkers.js @@ -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; } diff --git a/benchmark/http/incoming_headers.js b/benchmark/http/incoming_headers.js new file mode 100644 index 00000000000000..a1ab57e23876bf --- /dev/null +++ b/benchmark/http/incoming_headers.js @@ -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(); + }); + }); +} diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index d220ede8a4e9be..9ad3b8eb0d725a 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -135,133 +135,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