From ae0b500f66746c2f88aa725eff56d8fff254249f Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Wed, 18 Aug 2021 22:15:10 +0200 Subject: [PATCH 1/4] fix(fetch): toUSVString --- lib/fetch/body.js | 6 +++--- lib/fetch/request.js | 4 ++-- lib/fetch/response.js | 4 ++-- lib/fetch/util.js | 18 ++++++++++++++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/fetch/body.js b/lib/fetch/body.js index e82e76ac526..199485eaa10 100644 --- a/lib/fetch/body.js +++ b/lib/fetch/body.js @@ -1,7 +1,7 @@ 'use strict' const util = require('../core/util') -const { ReadableStreamFrom } = require('./util') +const { ReadableStreamFrom, toUSVString } = require('./util') const { FormData } = require('./formdata') const { kState } = require('./symbols') const { Blob } = require('buffer') @@ -156,7 +156,7 @@ function extractBody (object, keepalive = false) { // TODO: byte sequence? // TODO: scalar value string? // TODO: else? - source = String(object) + source = toUSVString(object) contentType = 'text/plain;charset=UTF-8' } @@ -293,7 +293,7 @@ const methods = { async text () { const blob = await this.blob() - return await blob.text() + return toUSVString(await blob.text()) }, async json () { diff --git a/lib/fetch/request.js b/lib/fetch/request.js index a32247e4dd9..c97438acc46 100644 --- a/lib/fetch/request.js +++ b/lib/fetch/request.js @@ -5,7 +5,7 @@ const { extractBody, mixinBody, cloneBody } = require('./body') const { Headers, fill: fillHeaders, HeadersList } = require('./headers') const util = require('../core/util') -const { isValidHTTPToken, EnvironmentSettingsObject } = require('./util') +const { isValidHTTPToken, EnvironmentSettingsObject, toUSVString } = require('./util') const { forbiddenMethods, corsSafeListedMethods, @@ -46,7 +46,7 @@ class Request { "Failed to construct 'Request': cannot convert to dictionary." ) } - const input = args[0] instanceof Request ? args[0] : String(args[0]) + const input = args[0] instanceof Request ? args[0] : toUSVString(args[0]) const init = args.length >= 1 ? args[1] ?? {} : {} // TODO diff --git a/lib/fetch/response.js b/lib/fetch/response.js index 624ef4f782b..e4bcc959c0c 100644 --- a/lib/fetch/response.js +++ b/lib/fetch/response.js @@ -4,7 +4,7 @@ const { Headers, HeadersList, fill } = require('./headers') const { extractBody, cloneBody, mixinBody } = require('./body') const util = require('../core/util') const { kEnumerableProperty } = util -const { responseURL, isValidReasonPhrase } = require('./util') +const { responseURL, isValidReasonPhrase, toUSVString } = require('./util') const { redirectStatus, nullBodyStatus, @@ -44,7 +44,7 @@ class Response { } const status = args.length >= 2 ? args[1] : 302 - const url = String(args[0]) + const url = toUSVString(args[0]) // 1. Let parsedURL be the result of parsing url with current settings // object’s API base URL. diff --git a/lib/fetch/util.js b/lib/fetch/util.js index 6fc25b02495..b39587ca363 100644 --- a/lib/fetch/util.js +++ b/lib/fetch/util.js @@ -280,6 +280,23 @@ function tryUpgradeRequestToAPotentiallyTrustworthyURL (request) { // TODO } +function _toUSVString (val) { + // TODO: This is internal to node core... + return val +} + +const unpairedSurrogateRe = + /(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])/ + +// https://github.com/nodejs/node/blob/7ca38f05a023666274569343f128c5aed81599f3/lib/internal/url.js#L144 +function toUSVString (val) { + const str = `${val}` + // As of V8 5.5, `str.search()` (and `unpairedSurrogateRe[@@search]()`) are + // slower than `unpairedSurrogateRe.exec()`. + const match = unpairedSurrogateRe.exec(str) + return match ? _toUSVString(str, match.index) : str +} + class ServiceWorkerGlobalScope {} // dummy class Window {} // dummy class EnvironmentSettingsObject {} // dummy @@ -290,6 +307,7 @@ module.exports = { ServiceWorkerGlobalScope, Window, EnvironmentSettingsObject, + toUSVString, tryUpgradeRequestToAPotentiallyTrustworthyURL, coarsenedSharedCurrentTime, matchRequestIntegrity, From 5633ec33d85ca61fd0c71fc917eae9e82b7876da Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Wed, 18 Aug 2021 22:28:25 +0200 Subject: [PATCH 2/4] fixup --- lib/fetch/formdata.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/fetch/formdata.js b/lib/fetch/formdata.js index f4060aeb3ab..c96d0320ef8 100644 --- a/lib/fetch/formdata.js +++ b/lib/fetch/formdata.js @@ -3,7 +3,7 @@ const { Blob } = require('buffer') const { kState } = require('./symbols') const { File } = require('./file') -const { HTMLFormElement } = require('./util') +const { HTMLFormElement, toUSVString } = require('./util') class FormData { constructor (...args) { @@ -30,11 +30,11 @@ class FormData { "Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'" ) } - const name = String(args[0]) - const filename = args.length === 3 ? String(args[2]) : undefined + const name = toUSVString(args[0]) + const filename = args.length === 3 ? toUSVString(args[2]) : undefined // 1. Let value be value if given; otherwise blobValue. - const value = args[1] instanceof Blob ? args[1] : String(args[1]) + const value = args[1] instanceof Blob ? args[1] : toUSVString(args[1]) // 2. Let entry be the result of creating an entry with // name, value, and filename if given. @@ -53,7 +53,7 @@ class FormData { `Failed to execute 'delete' on 'FormData': 1 arguments required, but only ${args.length} present.` ) } - const name = String(args[0]) + const name = toUSVString(args[0]) // The delete(name) method steps are to remove all entries whose name // is name from this’s entry list. @@ -76,7 +76,7 @@ class FormData { `Failed to execute 'get' on 'FormData': 1 arguments required, but only ${args.length} present.` ) } - const name = String(args[0]) + const name = toUSVString(args[0]) // 1. If there is no entry whose name is name in this’s entry list, // then return null. @@ -99,7 +99,7 @@ class FormData { `Failed to execute 'getAll' on 'FormData': 1 arguments required, but only ${args.length} present.` ) } - const name = String(args[0]) + const name = toUSVString(args[0]) // 1. If there is no entry whose name is name in this’s entry list, // then return the empty list. @@ -119,7 +119,7 @@ class FormData { `Failed to execute 'has' on 'FormData': 1 arguments required, but only ${args.length} present.` ) } - const name = String(args[0]) + const name = toUSVString(args[0]) // The has(name) method steps are to return true if there is an entry // whose name is name in this’s entry list; otherwise false. @@ -140,14 +140,14 @@ class FormData { "Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'" ) } - const name = String(args[0]) - const filename = args.length === 3 ? String(args[2]) : undefined + const name = toUSVString(args[0]) + const filename = args.length === 3 ? toUSVString(args[2]) : undefined // The set(name, value) and set(name, blobValue, filename) method steps // are: // 1. Let value be value if given; otherwise blobValue. - const value = args[1] instanceof Blob ? args[1] : String(args[1]) + const value = args[1] instanceof Blob ? args[1] : toUSVString(args[1]) // 2. Let entry be the result of creating an entry with name, value, and // filename if given. From 9acb99f3390d8523655d2ea6b7fecb7a7113d3e7 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Thu, 19 Aug 2021 04:16:10 +0200 Subject: [PATCH 3/4] fixup --- lib/api/readable.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/api/readable.js b/lib/api/readable.js index b7d6eb0384b..453e6148013 100644 --- a/lib/api/readable.js +++ b/lib/api/readable.js @@ -6,7 +6,7 @@ const assert = require('assert') const { Readable } = require('stream') const { RequestAbortedError, NotSupportedError } = require('../core/errors') const util = require('../core/util') -const { ReadableStreamFrom } = require('../fetch/util') +const { ReadableStreamFrom, toUSVString } = require('../fetch/util') let Blob @@ -98,27 +98,27 @@ module.exports = class BodyReadable extends Readable { } // https://fetch.spec.whatwg.org/#dom-body-text - text () { - return consume(this, 'text') + async text () { + return toUSVString(await consume(this, 'text')) } // https://fetch.spec.whatwg.org/#dom-body-json - json () { + async json () { return consume(this, 'json') } // https://fetch.spec.whatwg.org/#dom-body-blob - blob () { + async blob () { return consume(this, 'blob') } // https://fetch.spec.whatwg.org/#dom-body-arraybuffer - arrayBuffer () { + async arrayBuffer () { return consume(this, 'arrayBuffer') } // https://fetch.spec.whatwg.org/#dom-body-formdata - formData () { + async formData () { // TODO: Implement. throw new NotSupportedError() } From 1169e94cccd0a265ae10684e4938574b093ab458 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Thu, 19 Aug 2021 21:43:54 +0200 Subject: [PATCH 4/4] fixup --- lib/fetch/body.js | 13 +++++++------ lib/fetch/request.js | 6 +++++- lib/fetch/util.js | 20 ++------------------ 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/lib/fetch/body.js b/lib/fetch/body.js index 199485eaa10..e5c4b222d67 100644 --- a/lib/fetch/body.js +++ b/lib/fetch/body.js @@ -75,8 +75,9 @@ function extractBody (object, keepalive = false) { const prefix = `--${boundary}\r\nContent-Disposition: form-data` /*! formdata-polyfill. MIT License. Jimmy Wärting */ - const escape = str => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22') - const normalizeLinefeeds = value => value.replace(/\r?\n|\r/g, '\r\n') + const escape = (str) => + str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22') + const normalizeLinefeeds = (value) => value.replace(/\r?\n|\r/g, '\r\n') // Set action to this step: run the multipart/form-data // encoding algorithm, with object’s entry list and UTF-8. @@ -94,7 +95,8 @@ function extractBody (object, keepalive = false) { yield enc.encode( prefix + `; name="${escape(normalizeLinefeeds(name))}"` + - (value.filename ? `; filename="${escape(value.filename)}"` : '') + '\r\n' + + (value.filename ? `; filename="${escape(value.filename)}"` : '') + + '\r\n' + `Content-Type: ${ value.type || 'application/octet-stream' }\r\n\r\n` @@ -149,9 +151,8 @@ function extractBody (object, keepalive = false) { ) } - stream = object instanceof ReadableStream - ? object - : ReadableStreamFrom(object) + stream = + object instanceof ReadableStream ? object : ReadableStreamFrom(object) } else { // TODO: byte sequence? // TODO: scalar value string? diff --git a/lib/fetch/request.js b/lib/fetch/request.js index c97438acc46..152d888f91b 100644 --- a/lib/fetch/request.js +++ b/lib/fetch/request.js @@ -5,7 +5,11 @@ const { extractBody, mixinBody, cloneBody } = require('./body') const { Headers, fill: fillHeaders, HeadersList } = require('./headers') const util = require('../core/util') -const { isValidHTTPToken, EnvironmentSettingsObject, toUSVString } = require('./util') +const { + isValidHTTPToken, + EnvironmentSettingsObject, + toUSVString +} = require('./util') const { forbiddenMethods, corsSafeListedMethods, diff --git a/lib/fetch/util.js b/lib/fetch/util.js index b39587ca363..fcf187e3a83 100644 --- a/lib/fetch/util.js +++ b/lib/fetch/util.js @@ -2,6 +2,7 @@ const { redirectStatus } = require('./constants') const { performance } = require('perf_hooks') +const nodeUtil = require('util') let ReadableStream @@ -280,23 +281,6 @@ function tryUpgradeRequestToAPotentiallyTrustworthyURL (request) { // TODO } -function _toUSVString (val) { - // TODO: This is internal to node core... - return val -} - -const unpairedSurrogateRe = - /(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])/ - -// https://github.com/nodejs/node/blob/7ca38f05a023666274569343f128c5aed81599f3/lib/internal/url.js#L144 -function toUSVString (val) { - const str = `${val}` - // As of V8 5.5, `str.search()` (and `unpairedSurrogateRe[@@search]()`) are - // slower than `unpairedSurrogateRe.exec()`. - const match = unpairedSurrogateRe.exec(str) - return match ? _toUSVString(str, match.index) : str -} - class ServiceWorkerGlobalScope {} // dummy class Window {} // dummy class EnvironmentSettingsObject {} // dummy @@ -307,7 +291,7 @@ module.exports = { ServiceWorkerGlobalScope, Window, EnvironmentSettingsObject, - toUSVString, + toUSVString: nodeUtil.toUSVString || ((val) => `${val}`), tryUpgradeRequestToAPotentiallyTrustworthyURL, coarsenedSharedCurrentTime, matchRequestIntegrity,