From 6f55ec7228d87e67963097e2e1fcffaa35f777b5 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 13 Apr 2024 18:19:51 +0900 Subject: [PATCH 01/14] perf: optimization of request creation --- lib/core/request.js | 7 +++--- lib/core/util.js | 27 +++++++++++++++++++++++ lib/web/fetch/request.js | 34 +++++++---------------------- lib/web/fetch/util.js | 46 +++++++++++++--------------------------- 4 files changed, 54 insertions(+), 60 deletions(-) diff --git a/lib/core/request.js b/lib/core/request.js index 37839d3c949..b9cb39fbc98 100644 --- a/lib/core/request.js +++ b/lib/core/request.js @@ -16,7 +16,8 @@ const { isBlobLike, buildURL, validateHandler, - getServerName + getServerName, + normalizeMethodRecord } = require('./util') const { channels } = require('./diagnostics.js') const { headerNameLowerCasedRecord } = require('./constants') @@ -51,13 +52,13 @@ class Request { method !== 'CONNECT' ) { throw new InvalidArgumentError('path must be an absolute URL or start with a slash') - } else if (invalidPathRegex.exec(path) !== null) { + } else if (invalidPathRegex.test(path)) { throw new InvalidArgumentError('invalid request path') } if (typeof method !== 'string') { throw new InvalidArgumentError('method must be a string') - } else if (!isValidHTTPToken(method)) { + } else if (normalizeMethodRecord[method] === undefined && !isValidHTTPToken(method)) { throw new InvalidArgumentError('invalid request method') } diff --git a/lib/core/util.js b/lib/core/util.js index e530732de25..463ca4aec97 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -562,6 +562,31 @@ function errorRequest (client, request, err) { const kEnumerableProperty = Object.create(null) kEnumerableProperty.enumerable = true +const normalizeMethodRecordBase = { + delete: 'DELETE', + DELETE: 'DELETE', + get: 'GET', + GET: 'GET', + head: 'HEAD', + HEAD: 'HEAD', + options: 'OPTIONS', + OPTIONS: 'OPTIONS', + post: 'POST', + POST: 'POST', + put: 'PUT', + PUT: 'PUT' +} + +const normalizeMethodRecord = { + ...normalizeMethodRecordBase, + patch: 'patch', + PATCH: 'PATCH' +} + +// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`. +Object.setPrototypeOf(normalizeMethodRecordBase, null) +Object.setPrototypeOf(normalizeMethodRecord, null) + module.exports = { kEnumerableProperty, nop, @@ -600,6 +625,8 @@ module.exports = { isValidHeaderChar, isTokenCharCode, parseRangeHeader, + normalizeMethodRecordBase, + normalizeMethodRecord, nodeMajor, nodeMinor, nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13), diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index a35b90bf9f3..1c0cf9cc4f7 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -8,14 +8,11 @@ const { FinalizationRegistry } = require('./dispatcher-weakref')() const util = require('../../core/util') const nodeUtil = require('node:util') const { - isValidHTTPToken, sameOrigin, - normalizeMethod, makePolicyContainer, - normalizeMethodRecord + validateAndNormalizeMethod } = require('./util') const { - forbiddenMethodsSet, corsSafeListedMethodsSet, referrerPolicy, requestRedirect, @@ -24,7 +21,7 @@ const { requestCache, requestDuplex } = require('./constants') -const { kEnumerableProperty } = util +const { kEnumerableProperty, normalizeMethodRecord } = util const { kHeaders, kSignal, kState, kGuard, kRealm, kDispatcher } = require('./symbols') const { webidl } = require('./webidl') const { getGlobalOrigin } = require('./global') @@ -318,30 +315,15 @@ class Request { // 25. If init["method"] exists, then: if (init.method !== undefined) { // 1. Let method be init["method"]. - let method = init.method + const method = init.method - const mayBeNormalized = normalizeMethodRecord[method] + // 2. If method is not a method or method is a forbidden method, then + // throw a TypeError. - if (mayBeNormalized !== undefined) { - // Note: Bypass validation DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH and these lowercase ones - request.method = mayBeNormalized - } else { - // 2. If method is not a method or method is a forbidden method, then - // throw a TypeError. - if (!isValidHTTPToken(method)) { - throw new TypeError(`'${method}' is not a valid HTTP method.`) - } - - if (forbiddenMethodsSet.has(method.toUpperCase())) { - throw new TypeError(`'${method}' HTTP method is unsupported.`) - } - - // 3. Normalize method. - method = normalizeMethod(method) + // 3. Normalize method. - // 4. Set request’s method to method. - request.method = method - } + // 4. Set request’s method to method. + request.method = normalizeMethodRecord[method] ?? validateAndNormalizeMethod(method) if (!patchMethodWarning && request.method === 'patch') { process.emitWarning('Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.', { diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js index 8c73f909604..ccb5d2b72d3 100644 --- a/lib/web/fetch/util.js +++ b/lib/web/fetch/util.js @@ -6,11 +6,11 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet const { getGlobalOrigin } = require('./global') const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url') const { performance } = require('node:perf_hooks') -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken } = require('../../core/util') +const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizeMethodRecordBase } = require('../../core/util') const assert = require('node:assert') const { isUint8Array } = require('node:util/types') const { webidl } = require('./webidl') - +const { forbiddenMethodsSet } = require('./constants') let supportedHashes = [] // https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable @@ -783,37 +783,22 @@ function isCancelled (fetchParams) { fetchParams.controller.state === 'terminated' } -const normalizeMethodRecordBase = { - delete: 'DELETE', - DELETE: 'DELETE', - get: 'GET', - GET: 'GET', - head: 'HEAD', - HEAD: 'HEAD', - options: 'OPTIONS', - OPTIONS: 'OPTIONS', - post: 'POST', - POST: 'POST', - put: 'PUT', - PUT: 'PUT' -} - -const normalizeMethodRecord = { - ...normalizeMethodRecordBase, - patch: 'patch', - PATCH: 'PATCH' -} - -// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`. -Object.setPrototypeOf(normalizeMethodRecordBase, null) -Object.setPrototypeOf(normalizeMethodRecord, null) - /** * @see https://fetch.spec.whatwg.org/#concept-method-normalize * @param {string} method */ -function normalizeMethod (method) { - return normalizeMethodRecordBase[method.toLowerCase()] ?? method +function validateAndNormalizeMethod (method) { + if (!isValidHTTPToken(method)) { + throw new TypeError(`'${method}' is not a valid HTTP method.`) + } + + const upperCase = method.toUpperCase() + + if (forbiddenMethodsSet.has(upperCase)) { + throw new TypeError(`'${method}' HTTP method is unsupported.`) + } + + return normalizeMethodRecordBase[upperCase] ?? method } // https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string @@ -1590,7 +1575,7 @@ module.exports = { isURLPotentiallyTrustworthy, isValidReasonPhrase, sameOrigin, - normalizeMethod, + validateAndNormalizeMethod, serializeJavascriptValueToJSONString, iteratorMixin, createIterator, @@ -1606,7 +1591,6 @@ module.exports = { urlHasHttpsScheme, urlIsHttpHttpsScheme, readAllBytes, - normalizeMethodRecord, simpleRangeHeaderValue, buildContentRange, parseMetadata, From d850ade5caa35c1faa8c7977c9649ec783f9a175 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 13 Apr 2024 18:27:49 +0900 Subject: [PATCH 02/14] refactor: reduce variable --- lib/core/util.js | 10 ++-------- lib/web/fetch/util.js | 5 +++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/core/util.js b/lib/core/util.js index 463ca4aec97..ced79d5b620 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -562,7 +562,7 @@ function errorRequest (client, request, err) { const kEnumerableProperty = Object.create(null) kEnumerableProperty.enumerable = true -const normalizeMethodRecordBase = { +const normalizeMethodRecord = { delete: 'DELETE', DELETE: 'DELETE', get: 'GET', @@ -574,17 +574,12 @@ const normalizeMethodRecordBase = { post: 'POST', POST: 'POST', put: 'PUT', - PUT: 'PUT' -} - -const normalizeMethodRecord = { - ...normalizeMethodRecordBase, + PUT: 'PUT', patch: 'patch', PATCH: 'PATCH' } // Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`. -Object.setPrototypeOf(normalizeMethodRecordBase, null) Object.setPrototypeOf(normalizeMethodRecord, null) module.exports = { @@ -625,7 +620,6 @@ module.exports = { isValidHeaderChar, isTokenCharCode, parseRangeHeader, - normalizeMethodRecordBase, normalizeMethodRecord, nodeMajor, nodeMinor, diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js index ccb5d2b72d3..eebd7931bae 100644 --- a/lib/web/fetch/util.js +++ b/lib/web/fetch/util.js @@ -6,7 +6,7 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet const { getGlobalOrigin } = require('./global') const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url') const { performance } = require('node:perf_hooks') -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizeMethodRecordBase } = require('../../core/util') +const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizeMethodRecord } = require('../../core/util') const assert = require('node:assert') const { isUint8Array } = require('node:util/types') const { webidl } = require('./webidl') @@ -798,7 +798,8 @@ function validateAndNormalizeMethod (method) { throw new TypeError(`'${method}' HTTP method is unsupported.`) } - return normalizeMethodRecordBase[upperCase] ?? method + // Note: must be in uppercase + return normalizeMethodRecord[upperCase] ?? method } // https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string From 9222b367f642068a234ea60dcc0d7a9fd84d85ab Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 13 Apr 2024 19:55:27 +0900 Subject: [PATCH 03/14] rename to normalizedMethodRecords --- lib/core/request.js | 4 ++-- lib/core/util.js | 6 +++--- lib/web/fetch/util.js | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/core/request.js b/lib/core/request.js index b9cb39fbc98..4d0dec1890b 100644 --- a/lib/core/request.js +++ b/lib/core/request.js @@ -17,7 +17,7 @@ const { buildURL, validateHandler, getServerName, - normalizeMethodRecord + normalizedMethodRecords } = require('./util') const { channels } = require('./diagnostics.js') const { headerNameLowerCasedRecord } = require('./constants') @@ -58,7 +58,7 @@ class Request { if (typeof method !== 'string') { throw new InvalidArgumentError('method must be a string') - } else if (normalizeMethodRecord[method] === undefined && !isValidHTTPToken(method)) { + } else if (normalizedMethodRecords[method] === undefined && !isValidHTTPToken(method)) { throw new InvalidArgumentError('invalid request method') } diff --git a/lib/core/util.js b/lib/core/util.js index ced79d5b620..5fe8164bed6 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -562,7 +562,7 @@ function errorRequest (client, request, err) { const kEnumerableProperty = Object.create(null) kEnumerableProperty.enumerable = true -const normalizeMethodRecord = { +const normalizedMethodRecords = { delete: 'DELETE', DELETE: 'DELETE', get: 'GET', @@ -580,7 +580,7 @@ const normalizeMethodRecord = { } // Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`. -Object.setPrototypeOf(normalizeMethodRecord, null) +Object.setPrototypeOf(normalizedMethodRecords, null) module.exports = { kEnumerableProperty, @@ -620,7 +620,7 @@ module.exports = { isValidHeaderChar, isTokenCharCode, parseRangeHeader, - normalizeMethodRecord, + normalizedMethodRecords, nodeMajor, nodeMinor, nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13), diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js index eebd7931bae..5a6b7653f22 100644 --- a/lib/web/fetch/util.js +++ b/lib/web/fetch/util.js @@ -6,7 +6,7 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet const { getGlobalOrigin } = require('./global') const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url') const { performance } = require('node:perf_hooks') -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizeMethodRecord } = require('../../core/util') +const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecords } = require('../../core/util') const assert = require('node:assert') const { isUint8Array } = require('node:util/types') const { webidl } = require('./webidl') @@ -799,7 +799,7 @@ function validateAndNormalizeMethod (method) { } // Note: must be in uppercase - return normalizeMethodRecord[upperCase] ?? method + return normalizedMethodRecords[upperCase] ?? method } // https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string From 466ac4b337744c58e83528b61c91a3b557b622bf Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 13 Apr 2024 20:01:52 +0900 Subject: [PATCH 04/14] fixup --- lib/web/fetch/request.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 1c0cf9cc4f7..12883d7e4be 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -21,7 +21,7 @@ const { requestCache, requestDuplex } = require('./constants') -const { kEnumerableProperty, normalizeMethodRecord } = util +const { kEnumerableProperty, normalizedMethodRecords } = util const { kHeaders, kSignal, kState, kGuard, kRealm, kDispatcher } = require('./symbols') const { webidl } = require('./webidl') const { getGlobalOrigin } = require('./global') @@ -323,7 +323,7 @@ class Request { // 3. Normalize method. // 4. Set request’s method to method. - request.method = normalizeMethodRecord[method] ?? validateAndNormalizeMethod(method) + request.method = normalizedMethodRecords[method] ?? validateAndNormalizeMethod(method) if (!patchMethodWarning && request.method === 'patch') { process.emitWarning('Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.', { From cb87adc79f59004db4df2d0046e476534005d8c0 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 14 Apr 2024 06:25:35 +0900 Subject: [PATCH 05/14] suggested change --- lib/core/util.js | 10 ++++++++-- lib/web/fetch/request.js | 19 ++++++++++++++++++- lib/web/fetch/util.js | 21 +++++---------------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/lib/core/util.js b/lib/core/util.js index 5fe8164bed6..94c45481df6 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -562,7 +562,7 @@ function errorRequest (client, request, err) { const kEnumerableProperty = Object.create(null) kEnumerableProperty.enumerable = true -const normalizedMethodRecords = { +const normalizedMethodRecordsBase = { delete: 'DELETE', DELETE: 'DELETE', get: 'GET', @@ -574,12 +574,17 @@ const normalizedMethodRecords = { post: 'POST', POST: 'POST', put: 'PUT', - PUT: 'PUT', + PUT: 'PUT' +} + +const normalizedMethodRecords = { + ...normalizedMethodRecordsBase, patch: 'patch', PATCH: 'PATCH' } // Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`. +Object.setPrototypeOf(normalizedMethodRecordsBase, null) Object.setPrototypeOf(normalizedMethodRecords, null) module.exports = { @@ -620,6 +625,7 @@ module.exports = { isValidHeaderChar, isTokenCharCode, parseRangeHeader, + normalizedMethodRecordsBase, normalizedMethodRecords, nodeMajor, nodeMinor, diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 12883d7e4be..a3ac08fc35b 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -8,11 +8,13 @@ const { FinalizationRegistry } = require('./dispatcher-weakref')() const util = require('../../core/util') const nodeUtil = require('node:util') const { + isValidHTTPToken, sameOrigin, makePolicyContainer, validateAndNormalizeMethod } = require('./util') const { + forbiddenMethodsSet, corsSafeListedMethodsSet, referrerPolicy, requestRedirect, @@ -21,7 +23,7 @@ const { requestCache, requestDuplex } = require('./constants') -const { kEnumerableProperty, normalizedMethodRecords } = util +const { kEnumerableProperty, normalizedMethodRecordsBase, normalizedMethodRecords } = util const { kHeaders, kSignal, kState, kGuard, kRealm, kDispatcher } = require('./symbols') const { webidl } = require('./webidl') const { getGlobalOrigin } = require('./global') @@ -36,6 +38,21 @@ const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => { signal.removeEventListener('abort', abort) }) +function validateAndNormalizeMethod (method) { + if (!isValidHTTPToken(method)) { + throw new TypeError(`'${method}' is not a valid HTTP method.`) + } + + const upperCase = method.toUpperCase() + + if (forbiddenMethodsSet.has(upperCase)) { + throw new TypeError(`'${method}' HTTP method is unsupported.`) + } + + // Note: must be in uppercase + return normalizedMethodRecordsBase[upperCase] ?? method +} + let patchMethodWarning = false // https://fetch.spec.whatwg.org/#request-class diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js index 5a6b7653f22..3fcc76feb4d 100644 --- a/lib/web/fetch/util.js +++ b/lib/web/fetch/util.js @@ -6,11 +6,11 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet const { getGlobalOrigin } = require('./global') const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url') const { performance } = require('node:perf_hooks') -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecords } = require('../../core/util') +const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util') const assert = require('node:assert') const { isUint8Array } = require('node:util/types') const { webidl } = require('./webidl') -const { forbiddenMethodsSet } = require('./constants') + let supportedHashes = [] // https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable @@ -787,19 +787,8 @@ function isCancelled (fetchParams) { * @see https://fetch.spec.whatwg.org/#concept-method-normalize * @param {string} method */ -function validateAndNormalizeMethod (method) { - if (!isValidHTTPToken(method)) { - throw new TypeError(`'${method}' is not a valid HTTP method.`) - } - - const upperCase = method.toUpperCase() - - if (forbiddenMethodsSet.has(upperCase)) { - throw new TypeError(`'${method}' HTTP method is unsupported.`) - } - - // Note: must be in uppercase - return normalizedMethodRecords[upperCase] ?? method +function normalizeMethod (method) { + return normalizedMethodRecordsBase[method.toLowerCase()] ?? method } // https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string @@ -1576,7 +1565,7 @@ module.exports = { isURLPotentiallyTrustworthy, isValidReasonPhrase, sameOrigin, - validateAndNormalizeMethod, + normalizeMethod, serializeJavascriptValueToJSONString, iteratorMixin, createIterator, From b79cbb79886cae4067a1b62bc2c220e04cc0e3ef Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 14 Apr 2024 06:26:36 +0900 Subject: [PATCH 06/14] fixup --- lib/web/fetch/request.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index a3ac08fc35b..9a8ecfefdc6 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -10,8 +10,7 @@ const nodeUtil = require('node:util') const { isValidHTTPToken, sameOrigin, - makePolicyContainer, - validateAndNormalizeMethod + makePolicyContainer } = require('./util') const { forbiddenMethodsSet, From 3aac6224d8b4af588534d4cfee3b8f150b75a6d3 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 14 Apr 2024 19:22:25 +0900 Subject: [PATCH 07/14] bench: add benchmark --- benchmarks/core/request-instantiation.mjs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 benchmarks/core/request-instantiation.mjs diff --git a/benchmarks/core/request-instantiation.mjs b/benchmarks/core/request-instantiation.mjs new file mode 100644 index 00000000000..4973a31fcb7 --- /dev/null +++ b/benchmarks/core/request-instantiation.mjs @@ -0,0 +1,12 @@ +import { bench, run } from 'mitata' + +import Request from '../../lib/core/request.js' +import DecoratorHandler from '../../lib/handler/decorator-handler.js' + +const handler = new DecoratorHandler({}) + +bench('new Request()', () => { + return new Request('https://localhost', { path: '/', method: 'get', body: null }, handler) +}) + +await run() From 4cf7be219d828a957972e876381346ad3dcc9b87 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 21 Apr 2024 10:43:17 +0900 Subject: [PATCH 08/14] Update request.js --- lib/web/fetch/request.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index f485e9b06d0..0d91def873c 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -10,9 +10,7 @@ const nodeUtil = require('node:util') const { isValidHTTPToken, sameOrigin, - normalizeMethod, - EnvironmentSettingsObject, - normalizeMethodRecord + EnvironmentSettingsObject } = require('./util') const { forbiddenMethodsSet, From dafcba2af74c61e3f7b01c4f99bc06f4453c60d6 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Tue, 4 Jun 2024 21:08:48 +0900 Subject: [PATCH 09/14] Update lib/web/fetch/request.js --- lib/web/fetch/request.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 23e16ed52f3..aed72ed26ec 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -36,6 +36,9 @@ const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => { signal.removeEventListener('abort', abort) }) +/** + * @see https://fetch.spec.whatwg.org/#concept-method-normalize + */ function validateAndNormalizeMethod (method) { if (!isValidHTTPToken(method)) { throw new TypeError(`'${method}' is not a valid HTTP method.`) From 9f0c7d4945f7cef5d46f5108ce1018351e13eea6 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:05:55 +0900 Subject: [PATCH 10/14] Apply suggestions from code review --- lib/web/fetch/request.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index aed72ed26ec..9b379b25906 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -365,11 +365,30 @@ class Request { // 1. Let method be init["method"]. const method = init.method + const mayBeNormalized = normalizedMethodRecords[method] // 2. If method is not a method or method is a forbidden method, then // throw a TypeError. - // 3. Normalize method. + if (mayBeNormalized !== undefined) { + // Note: Bypass validation DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH and these lowercase ones + request.method = mayBeNormalized + } else { + // 2. If method is not a method or method is a forbidden method, then + // throw a TypeError. + if (!isValidHTTPToken(method)) { + throw new TypeError(`'${method}' is not a valid HTTP method.`) + } + + if (forbiddenMethodsSet.has(method.toUpperCase())) { + throw new TypeError(`'${method}' HTTP method is unsupported.`) + } + + // 3. Normalize method. + method = normalizedMethodRecords[method] ?? validateAndNormalizeMethod(method) + // 4. Set request’s method to method. + request.method = method + } // 4. Set request’s method to method. request.method = normalizedMethodRecords[method] ?? validateAndNormalizeMethod(method) From 0c4086f43f232630682e67849fa61a124428bb63 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:08:45 +0900 Subject: [PATCH 11/14] Apply suggestions from code review --- lib/web/fetch/request.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 9b379b25906..7febbd5482e 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -363,11 +363,9 @@ class Request { // 25. If init["method"] exists, then: if (init.method !== undefined) { // 1. Let method be init["method"]. - const method = init.method + let method = init.method const mayBeNormalized = normalizedMethodRecords[method] - // 2. If method is not a method or method is a forbidden method, then - // throw a TypeError. if (mayBeNormalized !== undefined) { // Note: Bypass validation DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH and these lowercase ones @@ -389,8 +387,6 @@ class Request { // 4. Set request’s method to method. request.method = method } - // 4. Set request’s method to method. - request.method = normalizedMethodRecords[method] ?? validateAndNormalizeMethod(method) if (!patchMethodWarning && request.method === 'patch') { process.emitWarning('Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.', { From 0924dd14b88da50e090e47e42c9a6652e76508f8 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:10:33 +0900 Subject: [PATCH 12/14] Update lib/web/fetch/request.js --- lib/web/fetch/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 7febbd5482e..6593ec636b8 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -382,7 +382,7 @@ class Request { } // 3. Normalize method. - method = normalizedMethodRecords[method] ?? validateAndNormalizeMethod(method) + method = normalizedMethodRecords[method] ?? method // 4. Set request’s method to method. request.method = method From a5591aae6a9d615c3d4389b25e7bc3e496844fdb Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:13:33 +0900 Subject: [PATCH 13/14] Update request.js --- lib/web/fetch/request.js | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 6593ec636b8..6d03af82c5e 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -36,24 +36,6 @@ const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => { signal.removeEventListener('abort', abort) }) -/** - * @see https://fetch.spec.whatwg.org/#concept-method-normalize - */ -function validateAndNormalizeMethod (method) { - if (!isValidHTTPToken(method)) { - throw new TypeError(`'${method}' is not a valid HTTP method.`) - } - - const upperCase = method.toUpperCase() - - if (forbiddenMethodsSet.has(upperCase)) { - throw new TypeError(`'${method}' HTTP method is unsupported.`) - } - - // Note: must be in uppercase - return normalizedMethodRecordsBase[upperCase] ?? method -} - const dependentControllerMap = new WeakMap() function buildAbort (acRef) { @@ -377,12 +359,15 @@ class Request { throw new TypeError(`'${method}' is not a valid HTTP method.`) } - if (forbiddenMethodsSet.has(method.toUpperCase())) { + const upperCase = method.toUpperCase() + + if (forbiddenMethodsSet.has(upperCase)) { throw new TypeError(`'${method}' HTTP method is unsupported.`) } // 3. Normalize method. - method = normalizedMethodRecords[method] ?? method + // Note: must be in uppercase + method = normalizedMethodRecordsBase[upperCase] ?? method // 4. Set request’s method to method. request.method = method From 59a174d2d61a26ab7ad826cf93d1c30dcdb964b9 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:16:31 +0900 Subject: [PATCH 14/14] Update lib/web/fetch/request.js --- lib/web/fetch/request.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index 6d03af82c5e..bc436aa9705 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -366,6 +366,7 @@ class Request { } // 3. Normalize method. + // https://fetch.spec.whatwg.org/#concept-method-normalize // Note: must be in uppercase method = normalizedMethodRecordsBase[upperCase] ?? method