From 0be1e994b4ef91a046ebea36bc557141afa725a6 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sat, 2 Mar 2024 16:12:02 +0900 Subject: [PATCH] perf(fetch): avoid redundant conversion --- lib/web/fetch/body.js | 18 +++++++++++------- lib/web/fetch/index.js | 2 +- lib/web/fetch/request.js | 11 +++++++---- lib/web/fetch/response.js | 14 ++++++++------ test/fetch/request.js | 5 +++++ test/fetch/response.js | 5 +++++ 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/web/fetch/body.js b/lib/web/fetch/body.js index 12377cb511d..bce062df3ef 100644 --- a/lib/web/fetch/body.js +++ b/lib/web/fetch/body.js @@ -125,17 +125,21 @@ function extractBody (object, keepalive = false) { for (const [name, value] of object) { if (typeof value === 'string') { - const chunk = textEncoder.encode(prefix + - `; name="${escape(normalizeLinefeeds(name))}"` + - `\r\n\r\n${normalizeLinefeeds(value)}\r\n`) + const chunk = textEncoder.encode( + `${prefix}; name="${escape( + normalizeLinefeeds(name) + )}"\r\n\r\n${normalizeLinefeeds(value)}\r\n` + ) blobParts.push(chunk) length += chunk.byteLength } else { - const chunk = textEncoder.encode(`${prefix}; name="${escape(normalizeLinefeeds(name))}"` + - (value.name ? `; filename="${escape(value.name)}"` : '') + '\r\n' + - `Content-Type: ${ + const chunk = textEncoder.encode( + `${prefix}; name="${escape(normalizeLinefeeds(name))}"${ + value.name ? `; filename="${escape(value.name)}"` : '' + }\r\nContent-Type: ${ value.type || 'application/octet-stream' - }\r\n\r\n`) + }\r\n\r\n` + ) blobParts.push(chunk, value, rn) if (typeof value.size === 'number') { length += chunk.byteLength + value.size + rn.byteLength diff --git a/lib/web/fetch/index.js b/lib/web/fetch/index.js index d8c20c59bf7..516472fd226 100644 --- a/lib/web/fetch/index.js +++ b/lib/web/fetch/index.js @@ -1475,7 +1475,7 @@ async function httpNetworkOrCacheFetch ( // user agents should append `User-Agent`/default `User-Agent` value to // httpRequest’s header list. if (!httpRequest.headersList.contains('user-agent', true)) { - httpRequest.headersList.append('user-agent', defaultUserAgent) + httpRequest.headersList.append('user-agent', defaultUserAgent, true) } // 15. If httpRequest’s cache mode is "default" and httpRequest’s header diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index be89ed0d8d7..d6d34d05393 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -467,7 +467,8 @@ class Request { // list, append header’s name/header’s value to this’s headers. if (headers instanceof HeadersList) { for (const [key, val] of headers) { - headersList.append(key, val) + // Note: The header names are already in lowercase. + headersList.append(key, val, true) } // Note: Copy the `set-cookie` meta-data. headersList.cookies = headers.cookies @@ -892,16 +893,18 @@ webidl.converters.Request = webidl.interfaceConverter( ) // https://fetch.spec.whatwg.org/#requestinfo + +// DOMString is used because the value is converted to a USVString in `new URL()`. webidl.converters.RequestInfo = function (V) { if (typeof V === 'string') { - return webidl.converters.USVString(V) + return webidl.converters.DOMString(V) } if (V instanceof Request) { return webidl.converters.Request(V) } - return webidl.converters.USVString(V) + return webidl.converters.DOMString(V) } webidl.converters.AbortSignal = webidl.interfaceConverter( @@ -921,7 +924,7 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([ { key: 'body', converter: webidl.nullableConverter( - webidl.converters.BodyInit + webidl.converters.BodyInit_DOMString ) }, { diff --git a/lib/web/fetch/response.js b/lib/web/fetch/response.js index e31f619590f..3f48448da92 100644 --- a/lib/web/fetch/response.js +++ b/lib/web/fetch/response.js @@ -77,7 +77,8 @@ class Response { webidl.argumentLengthCheck(arguments, 1, { header: 'Response.redirect' }) - url = webidl.converters.USVString(url) + // DOMString is used because the value is converted to a USVString in `new URL()`. + url = webidl.converters.DOMString(url) status = webidl.converters['unsigned short'](status) // 1. Let parsedURL be the result of parsing url with current settings @@ -120,7 +121,7 @@ class Response { } if (body !== null) { - body = webidl.converters.BodyInit(body) + body = webidl.converters.BodyInit_DOMString(body) } init = webidl.converters.ResponseInit(init) @@ -516,9 +517,10 @@ webidl.converters.URLSearchParams = webidl.interfaceConverter( ) // https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit -webidl.converters.XMLHttpRequestBodyInit = function (V) { + +webidl.converters.XMLHttpRequestBodyInit_DOMString = function (V) { if (typeof V === 'string') { - return webidl.converters.USVString(V) + return webidl.converters.DOMString(V) } if (isBlobLike(V)) { @@ -541,7 +543,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V) { } // https://fetch.spec.whatwg.org/#bodyinit -webidl.converters.BodyInit = function (V) { +webidl.converters.BodyInit_DOMString = function (V) { if (V instanceof ReadableStream) { return webidl.converters.ReadableStream(V) } @@ -552,7 +554,7 @@ webidl.converters.BodyInit = function (V) { return V } - return webidl.converters.XMLHttpRequestBodyInit(V) + return webidl.converters.XMLHttpRequestBodyInit_DOMString(V) } webidl.converters.ResponseInit = webidl.dictionaryConverter([ diff --git a/test/fetch/request.js b/test/fetch/request.js index 41ae8a836d7..8665de441d4 100644 --- a/test/fetch/request.js +++ b/test/fetch/request.js @@ -504,3 +504,8 @@ test('fromInnerRequest', () => { assert.strictEqual(request[kHeaders][kGuard], 'immutable') assert.strictEqual(request[kHeaders][kRealm], realm) }) + +test('surrogate pair url', async () => { + const request = new Request('https://asd/\ud801') + assert.deepStrictEqual(request.url, 'https://asd/%EF%BF%BD') +}) diff --git a/test/fetch/response.js b/test/fetch/response.js index 1f44f99f15d..b224d0e9e4f 100644 --- a/test/fetch/response.js +++ b/test/fetch/response.js @@ -288,3 +288,8 @@ test('fromInnerResponse', () => { assert.strictEqual(response[kHeaders][kGuard], 'immutable') assert.strictEqual(response[kHeaders][kRealm], realm) }) + +test('surrogate pair', async () => { + const response = new Response('\ud801') + assert.deepStrictEqual(await response.text(), '\ufffd') +})