diff --git a/lib/internal/mime.js b/lib/internal/mime.js index de691a745031e0..b1df776e771961 100644 --- a/lib/internal/mime.js +++ b/lib/internal/mime.js @@ -7,6 +7,7 @@ const { SafeMap, SafeStringPrototypeSearch, StringPrototypeCharAt, + StringPrototypeCharCodeAt, StringPrototypeIndexOf, StringPrototypeSlice, StringPrototypeToLowerCase, @@ -20,7 +21,23 @@ const NOT_HTTP_TOKEN_CODE_POINT = /[^!#$%&'*+\-.^_`|~A-Za-z0-9]/g; const NOT_HTTP_QUOTED_STRING_CODE_POINT = /[^\t\u0020-~\u0080-\u00FF]/g; const END_BEGINNING_WHITESPACE = /[^\r\n\t ]|$/; -const START_ENDING_WHITESPACE = /[\r\n\t ]*$/; + +// Index at which the run of trailing HTTP whitespace begins, or str.length +// when there is none. Equivalent to the result of searching for +// /[\r\n\t ]*$/, but a backward scan avoids the quadratic backtracking that an +// unanchored search performs on a long internal whitespace run. +function startOfTrailingWhitespace(str) { + let i = str.length; + while (i > 0) { + const c = StringPrototypeCharCodeAt(str, i - 1); + if (c === 0x09 || c === 0x0A || c === 0x0D || c === 0x20) { + i--; + } else { + break; + } + } + return i; +} function toASCIILower(str) { // eslint-disable-next-line no-control-regex @@ -68,7 +85,7 @@ function parseTypeAndSubtype(str) { const trimmedSubtype = StringPrototypeSlice( rawSubtype, 0, - SafeStringPrototypeSearch(rawSubtype, START_ENDING_WHITESPACE)); + startOfTrailingWhitespace(rawSubtype)); const invalidSubtypeIndex = SafeStringPrototypeSearch(trimmedSubtype, NOT_HTTP_TOKEN_CODE_POINT); if (trimmedSubtype === '' || invalidSubtypeIndex !== -1) { @@ -225,9 +242,8 @@ class MIMEParams { const paramsMap = this.#data; let position = 0; const str = this.#string; - const endOfSource = SafeStringPrototypeSearch( + const endOfSource = startOfTrailingWhitespace( StringPrototypeSlice(str, position), - START_ENDING_WHITESPACE, ) + position; while (position < endOfSource) { // Skip any whitespace before parameter @@ -291,7 +307,7 @@ class MIMEParams { const trimmedValue = StringPrototypeSlice( rawValue, 0, - SafeStringPrototypeSearch(rawValue, START_ENDING_WHITESPACE), + startOfTrailingWhitespace(rawValue), ); // Ignore parameters without values if (trimmedValue === '') continue;