diff --git a/lib/_http_common.js b/lib/_http_common.js index 583c5d610a..150d424261 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -232,52 +232,65 @@ exports.httpSocketSetup = httpSocketSetup; * per the rules defined in RFC 7230 * See https://tools.ietf.org/html/rfc7230#section-3.2.6 * + * Allowed characters in an HTTP token: + * ^_`a-z 94-122 + * A-Z 65-90 + * - 45 + * 0-9 48-57 + * ! 33 + * #$%&' 35-39 + * *+ 42-43 + * . 46 + * | 124 + * ~ 126 + * * 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) + * + * Additionally, checkIsHttpToken() is currently designed to be inlinable by v8, + * so take care when making changes to the implementation so that the source + * code size does not exceed v8's default max_inlined_source_size setting. **/ +function isValidTokenChar(ch) { + if (ch >= 94 && ch <= 122) + return true; + if (ch >= 65 && ch <= 90) + return true; + if (ch === 45) + return true; + if (ch >= 48 && ch <= 57) + return true; + if (ch === 34 || ch === 40 || ch === 41 || ch === 44) + return false; + if (ch >= 33 && ch <= 46) + return true; + if (ch === 124 || ch === 126) + return true; + return false; +} function checkIsHttpToken(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) + if (!isValidTokenChar(val.charCodeAt(0))) + return false; + const len = val.length; + if (len > 1) { + if (!isValidTokenChar(val.charCodeAt(1))) + return false; + if (len > 2) { + if (!isValidTokenChar(val.charCodeAt(2))) return false; - continue; + if (len > 3) { + if (!isValidTokenChar(val.charCodeAt(3))) + return false; + for (var i = 4; i < len; i++) { + if (!isValidTokenChar(val.charCodeAt(i))) + return false; + } + } } - - return false; } return true; }