Skip to content

Commit

Permalink
perf: reduce String#toLowerCase call (nodejs#2516)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsctx authored and crysmags committed Feb 27, 2024
1 parent ba86780 commit 91794d0
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 68 deletions.
13 changes: 8 additions & 5 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -765,11 +765,14 @@ class Parser {
}

const key = this.headers[len - 2]
if (key.length === 10 && key.toString().toLowerCase() === 'keep-alive') {
this.keepAlive += buf.toString()
} else if (key.length === 10 && key.toString().toLowerCase() === 'connection') {
this.connection += buf.toString()
} else if (key.length === 14 && key.toString().toLowerCase() === 'content-length') {
if (key.length === 10) {
const headerName = util.bufferToLowerCasedHeaderName(key)
if (headerName === 'keep-alive') {
this.keepAlive += buf.toString()
} else if (headerName === 'connection') {
this.connection += buf.toString()
}
} else if (key.length === 14 && util.bufferToLowerCasedHeaderName(key) === 'content-length') {
this.contentLength += buf.toString()
}

Expand Down
10 changes: 9 additions & 1 deletion lib/core/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,14 @@ function parseKeepAliveTimeout (val) {
function headerNameToString (value) {
return typeof value === 'string'
? headerNameLowerCasedRecord[value] ?? value.toLowerCase()
: tree.lookup(value) ?? value.toString().toLowerCase()
: tree.lookup(value) ?? value.toString('latin1').toLowerCase()
}

/**
* @param {Buffer} value
*/
function bufferToLowerCasedHeaderName (value) {
return tree.lookup(value) ?? value.toString('latin1').toLowerCase()
}

/**
Expand Down Expand Up @@ -523,6 +530,7 @@ module.exports = {
isAsyncIterable,
isDestroyed,
headerNameToString,
bufferToLowerCasedHeaderName,
parseRawHeaders,
parseHeaders,
parseKeepAliveTimeout,
Expand Down
2 changes: 1 addition & 1 deletion lib/fetch/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ function bodyMixinMethods (instance) {
// If mimeType’s essence is "multipart/form-data", then:
if (/multipart\/form-data/.test(contentType)) {
const headers = {}
for (const [key, value] of this.headers) headers[key.toLowerCase()] = value
for (const [key, value] of this.headers) headers[key] = value

const responseFormData = new FormData()

Expand Down
99 changes: 89 additions & 10 deletions lib/fetch/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,22 @@ class HeadersList {
}
}

// https://fetch.spec.whatwg.org/#header-list-contains
/**
* @see https://fetch.spec.whatwg.org/#header-list-contains
* @param {string} name
*/
contains (name) {
// A header list list contains a header name name if list
// contains a header whose name is a byte-case-insensitive
// match for name.
name = name.toLowerCase()

return this[kHeadersMap].has(name.toLowerCase())
}

/**
* @param {string} name
*/
lowerCaseContains (name) {
return this[kHeadersMap].has(name)
}

Expand All @@ -151,7 +160,11 @@ class HeadersList {
this.cookies = null
}

// https://fetch.spec.whatwg.org/#concept-header-list-append
/**
* @see https://fetch.spec.whatwg.org/#concept-header-list-append
* @param {string} name
* @param {string} value
*/
append (name, value) {
this[kHeadersSortedMap] = null

Expand All @@ -172,12 +185,42 @@ class HeadersList {
}

if (lowercaseName === 'set-cookie') {
this.cookies ??= []
this.cookies.push(value)
(this.cookies ??= []).push(value)
}
}

/**
* @param {string} name
* @param {string} value
*/
lowerCaseAppend (name, value) {
this[kHeadersSortedMap] = null

// 1. If list contains name, then set name to the first such
// header’s name.
const exists = this[kHeadersMap].get(name)

// 2. Append (name, value) to list.
if (exists) {
const delimiter = name === 'cookie' ? '; ' : ', '
this[kHeadersMap].set(name, {
name: exists.name,
value: `${exists.value}${delimiter}${value}`
})
} else {
this[kHeadersMap].set(name, { name, value })
}

if (name === 'set-cookie') {
(this.cookies ??= []).push(value)
}
}

// https://fetch.spec.whatwg.org/#concept-header-list-set
/**
* @see https://fetch.spec.whatwg.org/#concept-header-list-set
* @param {string} name
* @param {string} value
*/
set (name, value) {
this[kHeadersSortedMap] = null
const lowercaseName = name.toLowerCase()
Expand All @@ -193,11 +236,35 @@ class HeadersList {
this[kHeadersMap].set(lowercaseName, { name, value })
}

// https://fetch.spec.whatwg.org/#concept-header-list-delete
/**
* @param {string} name
* @param {string} value
*/
lowerCaseSet (name, value) {
if (name === 'set-cookie') {
this.cookies = [value]
}

// 1. If list contains name, then set the value of
// the first such header to value and remove the
// others.
// 2. Otherwise, append header (name, value) to list.
this[kHeadersMap].set(name, { name, value })
}

/**
* @see https://fetch.spec.whatwg.org/#concept-header-list-delete
* @param {string} name
*/
delete (name) {
this[kHeadersSortedMap] = null
return this.lowerCaseDelete(name.toLowerCase())
}

name = name.toLowerCase()
/**
* @param {string} name
*/
lowerCaseDelete (name) {
this[kHeadersSortedMap] = null

if (name === 'set-cookie') {
this.cookies = null
Expand All @@ -206,7 +273,11 @@ class HeadersList {
this[kHeadersMap].delete(name)
}

// https://fetch.spec.whatwg.org/#concept-header-list-get
/**
* @see https://fetch.spec.whatwg.org/#concept-header-list-get
* @param {string} name
* @returns {string | null}
*/
get (name) {
const value = this[kHeadersMap].get(name.toLowerCase())

Expand All @@ -217,6 +288,14 @@ class HeadersList {
return value === undefined ? null : value.value
}

/**
* @param {string} name
* @returns {string | null}
*/
lowerCaseGet (name) {
return this[kHeadersMap].get(name)?.value ?? null
}

* [Symbol.iterator] () {
// use the lowercased name
for (const [name, { value }] of this[kHeadersMap]) {
Expand Down
Loading

0 comments on commit 91794d0

Please sign in to comment.