diff --git a/benchmark/http/check_is_http_token.js b/benchmark/http/check_is_http_token.js new file mode 100644 index 00000000000000..9e0b8279b58ed0 --- /dev/null +++ b/benchmark/http/check_is_http_token.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common.js'); +const _checkIsHttpToken = require('_http_common')._checkIsHttpToken; + +const bench = common.createBenchmark(main, { + key: [ + 'TCN', + 'ETag', + 'date', + 'Vary', + 'server', + 'Server', + 'status', + 'version', + 'Expires', + 'alt-svc', + 'location', + 'Connection', + 'Keep-Alive', + 'content-type', + 'Content-Type', + 'Cache-Control', + 'Last-Modified', + 'Accept-Ranges', + 'content-length', + 'x-frame-options', + 'x-xss-protection', + 'Content-Encoding', + 'Content-Location', + 'Transfer-Encoding', + 'alternate-protocol', + ':', // invalid input + '@@', + '中文呢', // unicode + '((((())))', // invalid + ':alternate-protocol', // fast bailout + 'alternate-protocol:' // slow bailout + ], + n: [1e6], +}); + +function main(conf) { + var n = +conf.n; + var key = conf.key; + + bench.start(); + for (var i = 0; i < n; i++) { + _checkIsHttpToken(key); + } + bench.end(n); +} diff --git a/lib/_http_common.js b/lib/_http_common.js index 328b6eea8affba..08f93d8c4d04a2 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -225,10 +225,56 @@ exports.httpSocketSetup = httpSocketSetup; /** * Verifies that the given val is a valid HTTP token * per the rules defined in RFC 7230 + * See https://tools.ietf.org/html/rfc7230#section-3.2.6 + * + * This implementation of checkIsHttpToken() loops over the string instead of + * using a regular expression since the former is up to 180% faster with v8 4.9 + * depending on the string length (the shorter the string, the larger the + * performance difference) **/ -const token = /^[a-zA-Z0-9_!#$%&'*+.^`|~-]+$/; function checkIsHttpToken(val) { - return typeof val === 'string' && token.test(val); + if (typeof val !== 'string' || val.length === 0) + return false; + + for (var i = 0, len = val.length; i < len; i++) { + var ch = val.charCodeAt(i); + + if (ch >= 65 && ch <= 90) // A-Z + continue; + + if (ch >= 97 && ch <= 122) // a-z + continue; + + // ^ => 94 + // _ => 95 + // ` => 96 + // | => 124 + // ~ => 126 + if (ch === 94 || ch === 95 || ch === 96 || ch === 124 || ch === 126) + continue; + + if (ch >= 48 && ch <= 57) // 0-9 + continue; + + // ! => 33 + // # => 35 + // $ => 36 + // % => 37 + // & => 38 + // ' => 39 + // * => 42 + // + => 43 + // - => 45 + // . => 46 + if (ch >= 33 && ch <= 46) { + if (ch === 34 || ch === 40 || ch === 41 || ch === 44) + return false; + continue; + } + + return false; + } + return true; } exports._checkIsHttpToken = checkIsHttpToken;