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
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
Next Next commit
perf(headers): Improve Headers
tsctx committed Nov 2, 2023
commit 752d3fb7648e4e580af32ae91c721f8ce308a149
41 changes: 30 additions & 11 deletions lib/fetch/headers.js
Original file line number Diff line number Diff line change
@@ -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
}
}

/**
* @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
* @param {string} potentialValue
@@ -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) {
@@ -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({
@@ -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])
}
} else {
throw webidl.errors.conversionFailed({
@@ -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:
11 changes: 7 additions & 4 deletions lib/fetch/webidl.js
Original file line number Diff line number Diff line change
@@ -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
}

// 1. If V is null and the conversion is to an IDL type
// associated with the [LegacyNullToEmptyString]
// extended attribute, then return the DOMString value
@@ -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.`
)
}
}