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: Improve processHeader #2513

Merged
merged 4 commits into from
Dec 8, 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
78 changes: 69 additions & 9 deletions lib/core/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,11 @@ const {
const assert = require('assert')
const { kHTTP2BuildRequest, kHTTP2CopyHeaders, kHTTP1BuildRequest } = require('./symbols')
const util = require('./util')
const { headerNameLowerCasedRecord } = require('./constants')

// tokenRegExp and headerCharRegex have been lifted from
// headerCharRegex have been lifted from
// https://github.com/nodejs/node/blob/main/lib/_http_common.js

/**
* Verifies that the given val is a valid HTTP token
* per the rules defined in RFC 7230
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
*/
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/

/**
* Matches if val contains an invalid field-vchar
* field-value = *( field-content / obs-fold )
Expand Down Expand Up @@ -416,6 +410,72 @@ function processHeader (request, key, val, skipAppend = false) {
return
}

const mayBeLowerCasedKey = headerNameLowerCasedRecord[key]

if (mayBeLowerCasedKey !== undefined) {
switch (mayBeLowerCasedKey) {
case 'host': {
if (request.host !== null) break
if (headerCharRegex.exec(val) !== null) {
throw new InvalidArgumentError(`invalid ${key} header`)
}
// Consumed by Client
request.host = val
return
}
case 'content-length': {
if (request.contentLength !== null) break
request.contentLength = parseInt(val, 10)
if (!Number.isFinite(request.contentLength)) {
throw new InvalidArgumentError('invalid content-length header')
}
return
}
case 'content-type': {
if (request.contentType !== null) break
request.contentType = val
if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend)
else request.headers += processHeaderValue(key, val)
return
}
case 'transfer-encoding': {
throw new InvalidArgumentError('invalid transfer-encoding header')
}
case 'connection': {
const value = typeof val === 'string' ? val.toLowerCase() : null
if (value !== 'close' && value !== 'keep-alive') {
throw new InvalidArgumentError('invalid connection header')
} else if (value === 'close') {
request.reset = true
}
return
}
case 'keep-alive': {
throw new InvalidArgumentError('invalid keep-alive header')
}
case 'upgrade': {
throw new InvalidArgumentError('invalid upgrade header')
}
case 'expect': {
throw new NotSupportedError('expect header not supported')
}
}
if (Array.isArray(val)) {
for (let i = 0; i < val.length; i++) {
if (skipAppend) {
if (request.headers[key]) request.headers[key] += `,${processHeaderValue(key, val[i], skipAppend)}`
else request.headers[key] = processHeaderValue(key, val[i], skipAppend)
} else {
request.headers += processHeaderValue(key, val[i])
}
}
} else {
if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend)
else request.headers += processHeaderValue(key, val)
}
return
}

if (
request.host === null &&
key.length === 4 &&
Expand Down Expand Up @@ -473,7 +533,7 @@ function processHeader (request, key, val, skipAppend = false) {
key.toLowerCase() === 'expect'
) {
throw new NotSupportedError('expect header not supported')
} else if (tokenRegExp.exec(key) === null) {
} else if (!util.isValidHTTPToken(key)) {
throw new InvalidArgumentError('invalid header key')
} else {
if (Array.isArray(val)) {
Expand Down
Loading