Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(headers): Improve Headers #2397

Merged
merged 9 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions lib/fetch/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ const assert = require('assert')
const kHeadersMap = Symbol('headers map')
const kHeadersSortedMap = Symbol('headers map sorted')

/**
* @param {number} code
*/
function isHTTPWhiteSpaceCharCode (code) {
switch (code) {
case 0x00a:
case 0x00d:
case 0x009:
case 0x020:
return true
default:
return false
}
}
tsctx marked this conversation as resolved.
Show resolved Hide resolved

/**
* @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
* @param {string} potentialValue
Expand All @@ -24,12 +39,12 @@ function headerValueNormalize (potentialValue) {
// To normalize a byte sequence potentialValue, remove
// any leading and trailing HTTP whitespace bytes from
// potentialValue.
let i = 0; let j = potentialValue.length

while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(j - 1))) --j
while (i > j && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i

// Trimming the end with `.replace()` and a RegExp is typically subject to
// ReDoS. This is safer and faster.
let i = potentialValue.length
while (/[\r\n\t ]/.test(potentialValue.charAt(--i)));
return potentialValue.slice(0, i + 1).replace(/^[\r\n\t ]+/, '')
return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.slice(i, j)
}

function fill (headers, object) {
Expand All @@ -38,7 +53,8 @@ function fill (headers, object) {
// 1. If object is a sequence, then for each header in object:
// Note: webidl conversion to array has already been done.
if (Array.isArray(object)) {
for (const header of object) {
for (let i = 0; i < object.length; i++) {
const header = object[i]
// 1. If header does not contain exactly two items, then throw a TypeError.
if (header.length !== 2) {
throw webidl.errors.exception({
Expand All @@ -55,8 +71,10 @@ function fill (headers, object) {

// 2. Otherwise, object is a record, then for each key → value in object,
// append (key, value) to headers
for (const [key, value] of Object.entries(object)) {
headers.append(key, value)
const keys = Object.keys(object)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
headers.append(key, object[key])
tsctx marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
throw webidl.errors.conversionFailed({
Expand Down Expand Up @@ -422,16 +440,17 @@ class Headers {
const cookies = this[kHeadersList].cookies

// 3. For each name of names:
for (const [name, value] of names) {
for (let i = 0; i < names.length; i++) {
const [name, value] = names[i]
// 1. If name is `set-cookie`, then:
if (name === 'set-cookie') {
// 1. Let values be a list of all values of headers in list whose name
// is a byte-case-insensitive match for name, in order.

// 2. For each value of values:
// 1. Append (name, value) to headers.
for (const value of cookies) {
headers.push([name, value])
for (let j = 0; j < cookies.length; j++) {
headers.push([name, cookies[j]])
}
} else {
// 2. Otherwise:
Expand Down
11 changes: 7 additions & 4 deletions lib/fetch/webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ webidl.nullableConverter = function (converter) {

// https://webidl.spec.whatwg.org/#es-DOMString
webidl.converters.DOMString = function (V, opts = {}) {
// Note: avoid re-stringify
if (typeof V === 'string') {
return V
}
tsctx marked this conversation as resolved.
Show resolved Hide resolved

// 1. If V is null and the conversion is to an IDL type
// associated with the [LegacyNullToEmptyString]
// extended attribute, then return the DOMString value
Expand Down Expand Up @@ -427,12 +432,10 @@ webidl.converters.ByteString = function (V) {
// 2. If the value of any element of x is greater than
// 255, then throw a TypeError.
for (let index = 0; index < x.length; index++) {
const charCode = x.charCodeAt(index)

if (charCode > 255) {
if (x.charCodeAt(index) > 255) {
throw new TypeError(
'Cannot convert argument to a ByteString because the character at ' +
`index ${index} has a value of ${charCode} which is greater than 255.`
`index ${index} has a value of ${x.charCodeAt(index)} which is greater than 255.`
)
}
}
Expand Down
Loading