summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNode.js GitHub Bot <github-bot@iojs.org>2023-03-13 20:36:46 +0000
committerMichaël Zasso <targos@protonmail.com>2023-03-14 08:15:24 +0100
commit84f5a1f94276df6e0a40951489740428b355cb91 (patch)
treecd94c512fd0e536d1d3765b153c2adbe9a5369d9
parented31316c2efb38a6234791e2791b3320d69c48a7 (diff)
downloadnode-new-84f5a1f94276df6e0a40951489740428b355cb91.tar.gz
deps: update undici to 5.21.0
PR-URL: https://github.com/nodejs/node/pull/47063 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
-rw-r--r--deps/undici/src/README.md2
-rw-r--r--deps/undici/src/docs/api/ProxyAgent.md4
-rw-r--r--deps/undici/src/lib/api/api-stream.js38
-rw-r--r--deps/undici/src/lib/api/readable.js20
-rw-r--r--deps/undici/src/lib/client.js14
-rw-r--r--deps/undici/src/lib/core/util.js48
-rw-r--r--deps/undici/src/lib/fetch/dataURL.js13
-rw-r--r--deps/undici/src/lib/fetch/index.js2
-rw-r--r--deps/undici/src/lib/fetch/request.js24
-rw-r--r--deps/undici/src/lib/fetch/util.js191
-rw-r--r--deps/undici/src/lib/fileapi/encoding.js6
-rw-r--r--deps/undici/src/lib/proxy-agent.js14
-rw-r--r--deps/undici/src/lib/timers.js34
-rw-r--r--deps/undici/src/lib/websocket/connection.js73
-rw-r--r--deps/undici/src/lib/websocket/symbols.js3
-rw-r--r--deps/undici/src/lib/websocket/websocket.js64
-rw-r--r--deps/undici/src/package.json6
-rw-r--r--deps/undici/src/types/balanced-pool.d.ts6
-rw-r--r--deps/undici/src/types/client.d.ts76
-rw-r--r--deps/undici/src/types/proxy-agent.d.ts3
-rw-r--r--deps/undici/src/types/websocket.d.ts1
-rw-r--r--deps/undici/undici.js254
-rw-r--r--src/undici_version.h2
23 files changed, 542 insertions, 356 deletions
diff --git a/deps/undici/src/README.md b/deps/undici/src/README.md
index e49064fa29..05a5d21ed1 100644
--- a/deps/undici/src/README.md
+++ b/deps/undici/src/README.md
@@ -407,7 +407,7 @@ Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
## Workarounds
-### Network address family autoselection.
+### Network address family autoselection.
If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case
diff --git a/deps/undici/src/docs/api/ProxyAgent.md b/deps/undici/src/docs/api/ProxyAgent.md
index 05d67a094c..6a8b07fe6b 100644
--- a/deps/undici/src/docs/api/ProxyAgent.md
+++ b/deps/undici/src/docs/api/ProxyAgent.md
@@ -19,6 +19,7 @@ Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
* **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string.
* **token** `string` (optional) - It can be passed by a string of token for authentication.
* **auth** `string` (**deprecated**) - Use token.
+* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
Examples:
@@ -83,7 +84,8 @@ import { setGlobalDispatcher, request, ProxyAgent } from 'undici';
const proxyAgent = new ProxyAgent({
uri: 'my.proxy.server',
- token: 'Bearer xxxx'
+ // token: 'Bearer xxxx'
+ token: `Basic ${Buffer.from('username:password').toString('base64')}`
});
setGlobalDispatcher(proxyAgent);
diff --git a/deps/undici/src/lib/api/api-stream.js b/deps/undici/src/lib/api/api-stream.js
index f33f459f9d..7560a2e650 100644
--- a/deps/undici/src/lib/api/api-stream.js
+++ b/deps/undici/src/lib/api/api-stream.js
@@ -1,10 +1,11 @@
'use strict'
-const { finished } = require('stream')
+const { finished, PassThrough } = require('stream')
const {
InvalidArgumentError,
InvalidReturnValueError,
- RequestAbortedError
+ RequestAbortedError,
+ ResponseStatusCodeError
} = require('../core/errors')
const util = require('../core/util')
const { AsyncResource } = require('async_hooks')
@@ -16,7 +17,7 @@ class StreamHandler extends AsyncResource {
throw new InvalidArgumentError('invalid opts')
}
- const { signal, method, opaque, body, onInfo, responseHeaders } = opts
+ const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts
try {
if (typeof callback !== 'function') {
@@ -57,6 +58,7 @@ class StreamHandler extends AsyncResource {
this.trailers = null
this.body = body
this.onInfo = onInfo || null
+ this.throwOnError = throwOnError || false
if (util.isStream(body)) {
body.on('error', (err) => {
@@ -76,8 +78,8 @@ class StreamHandler extends AsyncResource {
this.context = context
}
- onHeaders (statusCode, rawHeaders, resume) {
- const { factory, opaque, context } = this
+ onHeaders (statusCode, rawHeaders, resume, statusMessage) {
+ const { factory, opaque, context, callback } = this
if (statusCode < 200) {
if (this.onInfo) {
@@ -96,6 +98,32 @@ class StreamHandler extends AsyncResource {
context
})
+ if (this.throwOnError && statusCode >= 400) {
+ const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
+ const chunks = []
+ const pt = new PassThrough()
+ pt
+ .on('data', (chunk) => chunks.push(chunk))
+ .on('end', () => {
+ const payload = Buffer.concat(chunks).toString('utf8')
+ this.runInAsyncScope(
+ callback,
+ null,
+ new ResponseStatusCodeError(
+ `Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`,
+ statusCode,
+ headers,
+ payload
+ )
+ )
+ })
+ .on('error', (err) => {
+ this.onError(err)
+ })
+ this.res = pt
+ return
+ }
+
if (
!res ||
typeof res.write !== 'function' ||
diff --git a/deps/undici/src/lib/api/readable.js b/deps/undici/src/lib/api/readable.js
index 9c184d14e1..a184e8eb51 100644
--- a/deps/undici/src/lib/api/readable.js
+++ b/deps/undici/src/lib/api/readable.js
@@ -4,7 +4,7 @@
const assert = require('assert')
const { Readable } = require('stream')
-const { RequestAbortedError, NotSupportedError } = require('../core/errors')
+const { RequestAbortedError, NotSupportedError, InvalidArgumentError } = require('../core/errors')
const util = require('../core/util')
const { ReadableStreamFrom, toUSVString } = require('../core/util')
@@ -146,15 +146,31 @@ module.exports = class BodyReadable extends Readable {
async dump (opts) {
let limit = opts && Number.isFinite(opts.limit) ? opts.limit : 262144
+ const signal = opts && opts.signal
+ const abortFn = () => {
+ this.destroy()
+ }
+ if (signal) {
+ if (typeof signal !== 'object' || !('aborted' in signal)) {
+ throw new InvalidArgumentError('signal must be an AbortSignal')
+ }
+ util.throwIfAborted(signal)
+ signal.addEventListener('abort', abortFn, { once: true })
+ }
try {
for await (const chunk of this) {
+ util.throwIfAborted(signal)
limit -= Buffer.byteLength(chunk)
if (limit < 0) {
return
}
}
} catch {
- // Do nothing...
+ util.throwIfAborted(signal)
+ } finally {
+ if (signal) {
+ signal.removeEventListener('abort', abortFn)
+ }
}
}
}
diff --git a/deps/undici/src/lib/client.js b/deps/undici/src/lib/client.js
index 9dbd2eefe4..b230c368da 100644
--- a/deps/undici/src/lib/client.js
+++ b/deps/undici/src/lib/client.js
@@ -1,3 +1,5 @@
+// @ts-check
+
'use strict'
/* global WebAssembly */
@@ -85,7 +87,15 @@ try {
channels.connected = { hasSubscribers: false }
}
+/**
+ * @type {import('../types/client').default}
+ */
class Client extends DispatcherBase {
+ /**
+ *
+ * @param {string|URL} url
+ * @param {import('../types/client').Client.Options} options
+ */
constructor (url, {
interceptors,
maxHeaderSize,
@@ -1658,6 +1668,8 @@ class AsyncWriter {
process.emitWarning(new RequestContentLengthMismatchError())
}
+ socket.cork()
+
if (bytesWritten === 0) {
if (!expectsPayload) {
socket[kReset] = true
@@ -1678,6 +1690,8 @@ class AsyncWriter {
const ret = socket.write(chunk)
+ socket.uncork()
+
request.onBodySent(chunk)
if (!ret) {
diff --git a/deps/undici/src/lib/core/util.js b/deps/undici/src/lib/core/util.js
index ef9b4570dc..ab94bcfe51 100644
--- a/deps/undici/src/lib/core/util.js
+++ b/deps/undici/src/lib/core/util.js
@@ -15,7 +15,7 @@ const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(
function nop () {}
function isStream (obj) {
- return obj && typeof obj.pipe === 'function'
+ return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
}
// based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
@@ -46,6 +46,12 @@ function buildURL (url, queryParams) {
function parseURL (url) {
if (typeof url === 'string') {
url = new URL(url)
+
+ if (!/^https?:/.test(url.origin || url.protocol)) {
+ throw new InvalidArgumentError('invalid protocol')
+ }
+
+ return url
}
if (!url || typeof url !== 'object') {
@@ -375,23 +381,34 @@ function ReadableStreamFrom (iterable) {
// The chunk should be a FormData instance and contains
// all the required methods.
-function isFormDataLike (chunk) {
- return (chunk &&
- chunk.constructor && chunk.constructor.name === 'FormData' &&
- typeof chunk === 'object' &&
- (typeof chunk.append === 'function' &&
- typeof chunk.delete === 'function' &&
- typeof chunk.get === 'function' &&
- typeof chunk.getAll === 'function' &&
- typeof chunk.has === 'function' &&
- typeof chunk.set === 'function' &&
- typeof chunk.entries === 'function' &&
- typeof chunk.keys === 'function' &&
- typeof chunk.values === 'function' &&
- typeof chunk.forEach === 'function')
+function isFormDataLike (object) {
+ return (
+ object &&
+ typeof object === 'object' &&
+ typeof object.append === 'function' &&
+ typeof object.delete === 'function' &&
+ typeof object.get === 'function' &&
+ typeof object.getAll === 'function' &&
+ typeof object.has === 'function' &&
+ typeof object.set === 'function' &&
+ object[Symbol.toStringTag] === 'FormData'
)
}
+function throwIfAborted (signal) {
+ if (!signal) { return }
+ if (typeof signal.throwIfAborted === 'function') {
+ signal.throwIfAborted()
+ } else {
+ if (signal.aborted) {
+ // DOMException not available < v17.0.0
+ const err = new Error('The operation was aborted')
+ err.name = 'AbortError'
+ throw err
+ }
+ }
+}
+
const kEnumerableProperty = Object.create(null)
kEnumerableProperty.enumerable = true
@@ -423,6 +440,7 @@ module.exports = {
getSocketInfo,
isFormDataLike,
buildURL,
+ throwIfAborted,
nodeMajor,
nodeMinor,
nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13)
diff --git a/deps/undici/src/lib/fetch/dataURL.js b/deps/undici/src/lib/fetch/dataURL.js
index 0d4a46956d..beefad1548 100644
--- a/deps/undici/src/lib/fetch/dataURL.js
+++ b/deps/undici/src/lib/fetch/dataURL.js
@@ -1,6 +1,5 @@
const assert = require('assert')
const { atob } = require('buffer')
-const { format } = require('url')
const { isValidHTTPToken, isomorphicDecode } = require('./util')
const encoder = new TextEncoder()
@@ -118,7 +117,17 @@ function dataURLProcessor (dataURL) {
* @param {boolean} excludeFragment
*/
function URLSerializer (url, excludeFragment = false) {
- return format(url, { fragment: !excludeFragment })
+ const href = url.href
+
+ if (!excludeFragment) {
+ return href
+ }
+
+ const hash = href.lastIndexOf('#')
+ if (hash === -1) {
+ return href
+ }
+ return href.slice(0, hash)
}
// https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
diff --git a/deps/undici/src/lib/fetch/index.js b/deps/undici/src/lib/fetch/index.js
index 6e356de812..e3834a7f1c 100644
--- a/deps/undici/src/lib/fetch/index.js
+++ b/deps/undici/src/lib/fetch/index.js
@@ -297,7 +297,7 @@ function finalizeAndReportTiming (response, initiatorType = 'other') {
// capability.
// TODO: given global’s relevant settings object’s cross-origin isolated
// capability?
- response.timingInfo.endTime = coarsenedSharedCurrentTime()
+ timingInfo.endTime = coarsenedSharedCurrentTime()
// 10. Set response’s timing info to timingInfo.
response.timingInfo = timingInfo
diff --git a/deps/undici/src/lib/fetch/request.js b/deps/undici/src/lib/fetch/request.js
index eca7b060e0..080a5d7bfa 100644
--- a/deps/undici/src/lib/fetch/request.js
+++ b/deps/undici/src/lib/fetch/request.js
@@ -9,7 +9,8 @@ const util = require('../core/util')
const {
isValidHTTPToken,
sameOrigin,
- normalizeMethod
+ normalizeMethod,
+ makePolicyContainer
} = require('./util')
const {
forbiddenMethods,
@@ -51,10 +52,14 @@ class Request {
input = webidl.converters.RequestInfo(input)
init = webidl.converters.RequestInit(init)
- // TODO
+ // https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object
this[kRealm] = {
settingsObject: {
- baseUrl: getGlobalOrigin()
+ baseUrl: getGlobalOrigin(),
+ get origin () {
+ return this.baseUrl?.origin
+ },
+ policyContainer: makePolicyContainer()
}
}
@@ -349,14 +354,17 @@ class Request {
if (signal.aborted) {
ac.abort(signal.reason)
} else {
- const acRef = new WeakRef(ac)
const abort = function () {
- acRef.deref()?.abort(this.reason)
+ ac.abort(this.reason)
}
- if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
- setMaxListeners(100, signal)
- }
+ // Third-party AbortControllers may not work with these.
+ // See https://github.com/nodejs/undici/pull/1910#issuecomment-1464495619
+ try {
+ if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
+ setMaxListeners(100, signal)
+ }
+ } catch {}
signal.addEventListener('abort', abort, { once: true })
requestFinalizer.register(this, { signal, abort })
diff --git a/deps/undici/src/lib/fetch/util.js b/deps/undici/src/lib/fetch/util.js
index a0faed9135..2d8977f17b 100644
--- a/deps/undici/src/lib/fetch/util.js
+++ b/deps/undici/src/lib/fetch/util.js
@@ -1,6 +1,7 @@
'use strict'
const { redirectStatus, badPorts, referrerPolicy: referrerPolicyTokens } = require('./constants')
+const { getGlobalOrigin } = require('./global')
const { performance } = require('perf_hooks')
const { isBlobLike, toUSVString, ReadableStreamFrom } = require('../core/util')
const assert = require('assert')
@@ -36,9 +37,11 @@ function responseLocationURL (response, requestFragment) {
// `Location` and response’s header list.
let location = response.headersList.get('location')
- // 3. If location is a value, then set location to the result of parsing
- // location with response’s URL.
- location = location ? new URL(location, responseURL(response)) : null
+ // 3. If location is a header value, then set location to the result of
+ // parsing location with response’s URL.
+ if (location !== null && isValidHeaderValue(location)) {
+ location = new URL(location, responseURL(response))
+ }
// 4. If location is a URL whose fragment is null, then set location’s
// fragment to requestFragment.
@@ -267,7 +270,7 @@ function appendRequestOriginHeader (request) {
// 2. If request’s response tainting is "cors" or request’s mode is "websocket", then append (`Origin`, serializedOrigin) to request’s header list.
if (request.responseTainting === 'cors' || request.mode === 'websocket') {
if (serializedOrigin) {
- request.headersList.append('Origin', serializedOrigin)
+ request.headersList.append('origin', serializedOrigin)
}
// 3. Otherwise, if request’s method is neither `GET` nor `HEAD`, then:
@@ -298,7 +301,7 @@ function appendRequestOriginHeader (request) {
if (serializedOrigin) {
// 2. Append (`Origin`, serializedOrigin) to request’s header list.
- request.headersList.append('Origin', serializedOrigin)
+ request.headersList.append('origin', serializedOrigin)
}
}
}
@@ -327,14 +330,17 @@ function createOpaqueTimingInfo (timingInfo) {
// https://html.spec.whatwg.org/multipage/origin.html#policy-container
function makePolicyContainer () {
- // TODO
- return {}
+ // Note: the fetch spec doesn't make use of embedder policy or CSP list
+ return {
+ referrerPolicy: 'strict-origin-when-cross-origin'
+ }
}
// https://html.spec.whatwg.org/multipage/origin.html#clone-a-policy-container
-function clonePolicyContainer () {
- // TODO
- return {}
+function clonePolicyContainer (policyContainer) {
+ return {
+ referrerPolicy: policyContainer.referrerPolicy
+ }
}
// https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
@@ -342,104 +348,76 @@ function determineRequestsReferrer (request) {
// 1. Let policy be request's referrer policy.
const policy = request.referrerPolicy
- // Return no-referrer when empty or policy says so
- if (policy == null || policy === '' || policy === 'no-referrer') {
- return 'no-referrer'
- }
+ // Note: policy cannot (shouldn't) be null or an empty string.
+ assert(policy)
+
+ // 2. Let environment be request’s client.
- // 2. Let environment be the request client
- const environment = request.client
let referrerSource = null
- /**
- * 3, Switch on request’s referrer:
- "client"
- If environment’s global object is a Window object, then
- Let document be the associated Document of environment’s global object.
- If document’s origin is an opaque origin, return no referrer.
- While document is an iframe srcdoc document,
- let document be document’s browsing context’s browsing context container’s node document.
- Let referrerSource be document’s URL.
-
- Otherwise, let referrerSource be environment’s creation URL.
-
- a URL
- Let referrerSource be request’s referrer.
- */
+ // 3. Switch on request’s referrer:
if (request.referrer === 'client') {
- // Not defined in Node but part of the spec
- if (request.client?.globalObject?.constructor?.name === 'Window' ) { // eslint-disable-line
- const origin = environment.globalObject.self?.origin ?? environment.globalObject.location?.origin
-
- // If document’s origin is an opaque origin, return no referrer.
- if (origin == null || origin === 'null') return 'no-referrer'
-
- // Let referrerSource be document’s URL.
- referrerSource = new URL(environment.globalObject.location.href)
- } else {
- // 3(a)(II) If environment's global object is not Window,
- // Let referrerSource be environments creationURL
- if (environment?.globalObject?.location == null) {
- return 'no-referrer'
- }
+ // Note: node isn't a browser and doesn't implement document/iframes,
+ // so we bypass this step and replace it with our own.
+
+ const globalOrigin = getGlobalOrigin()
- referrerSource = new URL(environment.globalObject.location.href)
+ if (!globalOrigin || globalOrigin.origin === 'null') {
+ return 'no-referrer'
}
+
+ // note: we need to clone it as it's mutated
+ referrerSource = new URL(globalOrigin)
} else if (request.referrer instanceof URL) {
- // 3(b) If requests's referrer is a URL instance, then make
- // referrerSource be requests's referrer.
+ // Let referrerSource be request’s referrer.
referrerSource = request.referrer
- } else {
- // If referrerSource neither client nor instance of URL
- // then return "no-referrer".
- return 'no-referrer'
}
- const urlProtocol = referrerSource.protocol
+ // 4. Let request’s referrerURL be the result of stripping referrerSource for
+ // use as a referrer.
+ let referrerURL = stripURLForReferrer(referrerSource)
- // If url's scheme is a local scheme (i.e. one of "about", "data", "javascript", "file")
- // then return "no-referrer".
- if (
- urlProtocol === 'about:' || urlProtocol === 'data:' ||
- urlProtocol === 'blob:'
- ) {
- return 'no-referrer'
+ // 5. Let referrerOrigin be the result of stripping referrerSource for use as
+ // a referrer, with the origin-only flag set to true.
+ const referrerOrigin = stripURLForReferrer(referrerSource, true)
+
+ // 6. If the result of serializing referrerURL is a string whose length is
+ // greater than 4096, set referrerURL to referrerOrigin.
+ if (referrerURL.toString().length > 4096) {
+ referrerURL = referrerOrigin
}
- let temp
- let referrerOrigin
- // 4. Let requests's referrerURL be the result of stripping referrer
- // source for use as referrer (using util function, without origin only)
- const referrerUrl = (temp = stripURLForReferrer(referrerSource)).length > 4096
- // 5. Let referrerOrigin be the result of stripping referrer
- // source for use as referrer (using util function, with originOnly true)
- ? (referrerOrigin = stripURLForReferrer(referrerSource, true))
- // 6. If result of seralizing referrerUrl is a string whose length is greater than
- // 4096, then set referrerURL to referrerOrigin
- : temp
- const areSameOrigin = sameOrigin(request, referrerUrl)
- const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy(referrerUrl) &&
+ const areSameOrigin = sameOrigin(request, referrerURL)
+ const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy(referrerURL) &&
!isURLPotentiallyTrustworthy(request.url)
- // NOTE: How to treat step 7?
// 8. Execute the switch statements corresponding to the value of policy:
switch (policy) {
case 'origin': return referrerOrigin != null ? referrerOrigin : stripURLForReferrer(referrerSource, true)
- case 'unsafe-url': return referrerUrl
+ case 'unsafe-url': return referrerURL
case 'same-origin':
return areSameOrigin ? referrerOrigin : 'no-referrer'
case 'origin-when-cross-origin':
- return areSameOrigin ? referrerUrl : referrerOrigin
- case 'strict-origin-when-cross-origin':
- /**
- * 1. If the origin of referrerURL and the origin of request’s current URL are the same,
- * then return referrerURL.
- * 2. If referrerURL is a potentially trustworthy URL and request’s current URL is not a
- * potentially trustworthy URL, then return no referrer.
- * 3. Return referrerOrigin
- */
- if (areSameOrigin) return referrerOrigin
- // else return isNonPotentiallyTrustWorthy ? 'no-referrer' : referrerOrigin
+ return areSameOrigin ? referrerURL : referrerOrigin
+ case 'strict-origin-when-cross-origin': {
+ const currentURL = requestCurrentURL(request)
+
+ // 1. If the origin of referrerURL and the origin of request’s current
+ // URL are the same, then return referrerURL.
+ if (sameOrigin(referrerURL, currentURL)) {
+ return referrerURL
+ }
+
+ // 2. If referrerURL is a potentially trustworthy URL and request’s
+ // current URL is not a potentially trustworthy URL, then return no
+ // referrer.
+ if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
+ return 'no-referrer'
+ }
+
+ // 3. Return referrerOrigin.
+ return referrerOrigin
+ }
case 'strict-origin': // eslint-disable-line
/**
* 1. If referrerURL is a potentially trustworthy URL and
@@ -458,15 +436,42 @@ function determineRequestsReferrer (request) {
default: // eslint-disable-line
return isNonPotentiallyTrustWorthy ? 'no-referrer' : referrerOrigin
}
+}
- function stripURLForReferrer (url, originOnly = false) {
- const urlObject = new URL(url.href)
- urlObject.username = ''
- urlObject.password = ''
- urlObject.hash = ''
+/**
+ * @see https://w3c.github.io/webappsec-referrer-policy/#strip-url
+ * @param {URL} url
+ * @param {boolean|undefined} originOnly
+ */
+function stripURLForReferrer (url, originOnly) {
+ // 1. Assert: url is a URL.
+ assert(url instanceof URL)
- return originOnly ? urlObject.origin : urlObject.href
+ // 2. If url’s scheme is a local scheme, then return no referrer.
+ if (url.protocol === 'file:' || url.protocol === 'about:' || url.protocol === 'blank:') {
+ return 'no-referrer'
}
+
+ // 3. Set url’s username to the empty string.
+ url.username = ''
+
+ // 4. Set url’s password to the empty string.
+ url.password = ''
+
+ // 5. Set url’s fragment to null.
+ url.hash = ''
+
+ // 6. If the origin-only flag is true, then:
+ if (originOnly) {
+ // 1. Set url’s path to « the empty string ».
+ url.pathname = ''
+
+ // 2. Set url’s query to null.
+ url.search = ''
+ }
+
+ // 7. Return url.
+ return url
}
function isURLPotentiallyTrustworthy (url) {
diff --git a/deps/undici/src/lib/fileapi/encoding.js b/deps/undici/src/lib/fileapi/encoding.js
index 4aac20cce1..1d1d2b6544 100644
--- a/deps/undici/src/lib/fileapi/encoding.js
+++ b/deps/undici/src/lib/fileapi/encoding.js
@@ -2,9 +2,13 @@
/**
* @see https://encoding.spec.whatwg.org/#concept-encoding-get
- * @param {string} label
+ * @param {string|undefined} label
*/
function getEncoding (label) {
+ if (!label) {
+ return 'failure'
+ }
+
// 1. Remove any leading and trailing ASCII whitespace from label.
// 2. If label is an ASCII case-insensitive match for any of the
// labels listed in the table below, then return the
diff --git a/deps/undici/src/lib/proxy-agent.js b/deps/undici/src/lib/proxy-agent.js
index 128daddbef..c710948cc5 100644
--- a/deps/undici/src/lib/proxy-agent.js
+++ b/deps/undici/src/lib/proxy-agent.js
@@ -3,7 +3,7 @@
const { kProxy, kClose, kDestroy, kInterceptors } = require('./core/symbols')
const { URL } = require('url')
const Agent = require('./agent')
-const Client = require('./client')
+const Pool = require('./pool')
const DispatcherBase = require('./dispatcher-base')
const { InvalidArgumentError, RequestAbortedError } = require('./core/errors')
const buildConnector = require('./core/connect')
@@ -34,6 +34,10 @@ function buildProxyOptions (opts) {
}
}
+function defaultFactory (origin, opts) {
+ return new Pool(origin, opts)
+}
+
class ProxyAgent extends DispatcherBase {
constructor (opts) {
super(opts)
@@ -51,6 +55,12 @@ class ProxyAgent extends DispatcherBase {
throw new InvalidArgumentError('Proxy opts.uri is mandatory')
}
+ const { clientFactory = defaultFactory } = opts
+
+ if (typeof clientFactory !== 'function') {
+ throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')
+ }
+
this[kRequestTls] = opts.requestTls
this[kProxyTls] = opts.proxyTls
this[kProxyHeaders] = opts.headers || {}
@@ -69,7 +79,7 @@ class ProxyAgent extends DispatcherBase {
const connect = buildConnector({ ...opts.proxyTls })
this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })
- this[kClient] = new Client(resolvedUrl, { connect })
+ this[kClient] = clientFactory(resolvedUrl, { connect })
this[kAgent] = new Agent({
...opts,
connect: async (opts, callback) => {
diff --git a/deps/undici/src/lib/timers.js b/deps/undici/src/lib/timers.js
index f96bc62f28..5782217a0e 100644
--- a/deps/undici/src/lib/timers.js
+++ b/deps/undici/src/lib/timers.js
@@ -13,13 +13,15 @@ function onTimeout () {
while (idx < len) {
const timer = fastTimers[idx]
- if (timer.expires && fastNow >= timer.expires) {
- timer.expires = 0
+ if (timer.state === 0) {
+ timer.state = fastNow + timer.delay
+ } else if (timer.state > 0 && fastNow >= timer.state) {
+ timer.state = -1
timer.callback(timer.opaque)
}
- if (timer.expires === 0) {
- timer.active = false
+ if (timer.state === -1) {
+ timer.state = -2
if (idx !== len - 1) {
fastTimers[idx] = fastTimers.pop()
} else {
@@ -53,37 +55,43 @@ class Timeout {
this.callback = callback
this.delay = delay
this.opaque = opaque
- this.expires = 0
- this.active = false
+
+ // -2 not in timer list
+ // -1 in timer list but inactive
+ // 0 in timer list waiting for time
+ // > 0 in timer list waiting for time to expire
+ this.state = -2
this.refresh()
}
refresh () {
- if (!this.active) {
- this.active = true
+ if (this.state === -2) {
fastTimers.push(this)
if (!fastNowTimeout || fastTimers.length === 1) {
refreshTimeout()
- fastNow = Date.now()
}
}
- this.expires = fastNow + this.delay
+ this.state = 0
}
clear () {
- this.expires = 0
+ this.state = -1
}
}
module.exports = {
setTimeout (callback, delay, opaque) {
- return new Timeout(callback, delay, opaque)
+ return delay < 1e3
+ ? setTimeout(callback, delay, opaque)
+ : new Timeout(callback, delay, opaque)
},
clearTimeout (timeout) {
- if (timeout && timeout.clear) {
+ if (timeout instanceof Timeout) {
timeout.clear()
+ } else {
+ clearTimeout(timeout)
}
}
}
diff --git a/deps/undici/src/lib/websocket/connection.js b/deps/undici/src/lib/websocket/connection.js
index df8e551c26..09770247e3 100644
--- a/deps/undici/src/lib/websocket/connection.js
+++ b/deps/undici/src/lib/websocket/connection.js
@@ -5,19 +5,15 @@ const diagnosticsChannel = require('diagnostics_channel')
const { uid, states } = require('./constants')
const {
kReadyState,
- kResponse,
- kExtensions,
- kProtocol,
kSentClose,
kByteParser,
kReceivedClose
} = require('./symbols')
const { fireEvent, failWebsocketConnection } = require('./util')
const { CloseEvent } = require('./events')
-const { ByteParser } = require('./receiver')
const { makeRequest } = require('../fetch/request')
const { fetching } = require('../fetch/index')
-const { getGlobalDispatcher } = require('../..')
+const { getGlobalDispatcher } = require('../global')
const channels = {}
channels.open = diagnosticsChannel.channel('undici:websocket:open')
@@ -29,8 +25,9 @@ channels.socketError = diagnosticsChannel.channel('undici:websocket:socket_error
* @param {URL} url
* @param {string|string[]} protocols
* @param {import('./websocket').WebSocket} ws
+ * @param {(response: any) => void} onEstablish
*/
-function establishWebSocketConnection (url, protocols, ws) {
+function establishWebSocketConnection (url, protocols, ws, onEstablish) {
// 1. Let requestURL be a copy of url, with its scheme set to "http", if url’s
// scheme is "ws", and to "https" otherwise.
const requestURL = url
@@ -173,21 +170,19 @@ function establishWebSocketConnection (url, protocols, ws) {
return
}
- // processResponse is called when the "response’s header list has been received and initialized."
- // once this happens, the connection is open
- ws[kResponse] = response
-
- const parser = new ByteParser(ws)
- response.socket.ws = ws // TODO: use symbol
- ws[kByteParser] = parser
-
- whenConnectionEstablished(ws)
-
response.socket.on('data', onSocketData)
response.socket.on('close', onSocketClose)
response.socket.on('error', onSocketError)
- parser.on('drain', onParserDrain)
+ if (channels.open.hasSubscribers) {
+ channels.open.publish({
+ address: response.socket.address(),
+ protocol: secProtocol,
+ extensions: secExtension
+ })
+ }
+
+ onEstablish(response)
}
})
@@ -195,46 +190,6 @@ function establishWebSocketConnection (url, protocols, ws) {
}
/**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- * @param {import('./websocket').WebSocket} ws
- */
-function whenConnectionEstablished (ws) {
- const { [kResponse]: response } = ws
-
- // 1. Change the ready state to OPEN (1).
- ws[kReadyState] = states.OPEN
-
- // 2. Change the extensions attribute’s value to the extensions in use, if
- // it is not the null value.
- // https://datatracker.ietf.org/doc/html/rfc6455#section-9.1
- const extensions = response.headersList.get('sec-websocket-extensions')
-
- if (extensions !== null) {
- ws[kExtensions] = extensions
- }
-
- // 3. Change the protocol attribute’s value to the subprotocol in use, if
- // it is not the null value.
- // https://datatracker.ietf.org/doc/html/rfc6455#section-1.9
- const protocol = response.headersList.get('sec-websocket-protocol')
-
- if (protocol !== null) {
- ws[kProtocol] = protocol
- }
-
- // 4. Fire an event named open at the WebSocket object.
- fireEvent('open', ws)
-
- if (channels.open.hasSubscribers) {
- channels.open.publish({
- address: response.socket.address(),
- protocol,
- extensions
- })
- }
-}
-
-/**
* @param {Buffer} chunk
*/
function onSocketData (chunk) {
@@ -243,10 +198,6 @@ function onSocketData (chunk) {
}
}
-function onParserDrain () {
- this.ws[kResponse].socket.resume()
-}
-
/**
* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
* @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4
diff --git a/deps/undici/src/lib/websocket/symbols.js b/deps/undici/src/lib/websocket/symbols.js
index 5e135862f8..11d03e38a8 100644
--- a/deps/undici/src/lib/websocket/symbols.js
+++ b/deps/undici/src/lib/websocket/symbols.js
@@ -5,10 +5,7 @@ module.exports = {
kReadyState: Symbol('ready state'),
kController: Symbol('controller'),
kResponse: Symbol('response'),
- kExtensions: Symbol('extensions'),
- kProtocol: Symbol('protocol'),
kBinaryType: Symbol('binary type'),
- kClosingFrame: Symbol('closing frame'),
kSentClose: Symbol('sent close'),
kReceivedClose: Symbol('received close'),
kByteParser: Symbol('byte parser')
diff --git a/deps/undici/src/lib/websocket/websocket.js b/deps/undici/src/lib/websocket/websocket.js
index 79c9be439c..164d24c6f8 100644
--- a/deps/undici/src/lib/websocket/websocket.js
+++ b/deps/undici/src/lib/websocket/websocket.js
@@ -8,15 +8,15 @@ const {
kWebSocketURL,
kReadyState,
kController,
- kExtensions,
- kProtocol,
kBinaryType,
kResponse,
- kSentClose
+ kSentClose,
+ kByteParser
} = require('./symbols')
-const { isEstablished, isClosing, isValidSubprotocol, failWebsocketConnection } = require('./util')
+const { isEstablished, isClosing, isValidSubprotocol, failWebsocketConnection, fireEvent } = require('./util')
const { establishWebSocketConnection } = require('./connection')
const { WebsocketFrameSend } = require('./frame')
+const { ByteParser } = require('./receiver')
const { kEnumerableProperty, isBlobLike } = require('../core/util')
const { types } = require('util')
@@ -32,6 +32,8 @@ class WebSocket extends EventTarget {
}
#bufferedAmount = 0
+ #protocol = ''
+ #extensions = ''
/**
* @param {string} url
@@ -104,7 +106,12 @@ class WebSocket extends EventTarget {
// 1. Establish a WebSocket connection given urlRecord, protocols,
// and client.
- this[kController] = establishWebSocketConnection(urlRecord, protocols, this)
+ this[kController] = establishWebSocketConnection(
+ urlRecord,
+ protocols,
+ this,
+ (response) => this.#onConnectionEstablished(response)
+ )
// Each WebSocket object has an associated ready state, which is a
// number representing the state of the connection. Initially it must
@@ -112,10 +119,8 @@ class WebSocket extends EventTarget {
this[kReadyState] = WebSocket.CONNECTING
// The extensions attribute must initially return the empty string.
- this[kExtensions] = ''
// The protocol attribute must initially return the empty string.
- this[kProtocol] = ''
// Each WebSocket object has an associated binary type, which is a
// BinaryType. Initially it must be "blob".
@@ -368,13 +373,13 @@ class WebSocket extends EventTarget {
get extensions () {
webidl.brandCheck(this, WebSocket)
- return this[kExtensions]
+ return this.#extensions
}
get protocol () {
webidl.brandCheck(this, WebSocket)
- return this[kProtocol]
+ return this.#protocol
}
get onopen () {
@@ -476,6 +481,47 @@ class WebSocket extends EventTarget {
this[kBinaryType] = type
}
}
+
+ /**
+ * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
+ */
+ #onConnectionEstablished (response) {
+ // processResponse is called when the "response’s header list has been received and initialized."
+ // once this happens, the connection is open
+ this[kResponse] = response
+
+ const parser = new ByteParser(this)
+ parser.on('drain', function onParserDrain () {
+ this.ws[kResponse].socket.resume()
+ })
+
+ response.socket.ws = this
+ this[kByteParser] = parser
+
+ // 1. Change the ready state to OPEN (1).
+ this[kReadyState] = states.OPEN
+
+ // 2. Change the extensions attribute’s value to the extensions in use, if
+ // it is not the null value.
+ // https://datatracker.ietf.org/doc/html/rfc6455#section-9.1
+ const extensions = response.headersList.get('sec-websocket-extensions')
+
+ if (extensions !== null) {
+ this.#extensions = extensions
+ }
+
+ // 3. Change the protocol attribute’s value to the subprotocol in use, if
+ // it is not the null value.
+ // https://datatracker.ietf.org/doc/html/rfc6455#section-1.9
+ const protocol = response.headersList.get('sec-websocket-protocol')
+
+ if (protocol !== null) {
+ this.#protocol = protocol
+ }
+
+ // 4. Fire an event named open at the WebSocket object.
+ fireEvent('open', this)
+ }
}
// https://websockets.spec.whatwg.org/#dom-websocket-connecting
diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json
index 37baa25665..39be1736f0 100644
--- a/deps/undici/src/package.json
+++ b/deps/undici/src/package.json
@@ -1,6 +1,6 @@
{
"name": "undici",
- "version": "5.20.0",
+ "version": "5.21.0",
"description": "An HTTP/1.1 client, written from scratch for Node.js",
"homepage": "https://undici.nodejs.org",
"bugs": {
@@ -86,6 +86,7 @@
"husky": "^8.0.1",
"import-fresh": "^3.3.0",
"jest": "^29.0.2",
+ "jsdom": "^21.1.0",
"jsfuzz": "^1.0.15",
"mocha": "^10.0.0",
"p-timeout": "^3.2.0",
@@ -112,8 +113,7 @@
"ignore": [
"lib/llhttp/constants.js",
"lib/llhttp/utils.js",
- "test/wpt/tests",
- "test/wpt/runner/resources"
+ "test/wpt/tests"
]
},
"tsd": {
diff --git a/deps/undici/src/types/balanced-pool.d.ts b/deps/undici/src/types/balanced-pool.d.ts
index b5de726eec..d1e9375875 100644
--- a/deps/undici/src/types/balanced-pool.d.ts
+++ b/deps/undici/src/types/balanced-pool.d.ts
@@ -5,10 +5,10 @@ import { URL } from 'url'
export default BalancedPool
declare class BalancedPool extends Dispatcher {
- constructor(url: string | URL | string[], options?: Pool.Options);
+ constructor(url: string | string[] | URL | URL[], options?: Pool.Options);
- addUpstream(upstream: string): BalancedPool;
- removeUpstream(upstream: string): BalancedPool;
+ addUpstream(upstream: string | URL): BalancedPool;
+ removeUpstream(upstream: string | URL): BalancedPool;
upstreams: Array<string>;
/** `true` after `pool.close()` has been called. */
diff --git a/deps/undici/src/types/client.d.ts b/deps/undici/src/types/client.d.ts
index 871e3c2448..56074a15ae 100644
--- a/deps/undici/src/types/client.d.ts
+++ b/deps/undici/src/types/client.d.ts
@@ -4,10 +4,10 @@ import Dispatcher from './dispatcher'
import DispatchInterceptor from './dispatcher'
import buildConnector from "./connector";
-export default Client
-
-/** A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default. */
-declare class Client extends Dispatcher {
+/**
+ * A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default.
+ */
+export class Client extends Dispatcher {
constructor(url: string | URL, options?: Client.Options);
/** Property to get and set the pipelining factor. */
pipelining: number;
@@ -17,40 +17,62 @@ declare class Client extends Dispatcher {
destroyed: boolean;
}
-declare namespace Client {
+export declare namespace Client {
+ export interface OptionsInterceptors {
+ Client: readonly DispatchInterceptor[];
+ }
export interface Options {
+ /** TODO */
+ interceptors?: OptionsInterceptors;
+ /** The maximum length of request headers in bytes. Default: `16384` (16KiB). */
+ maxHeaderSize?: number;
+ /** The amount of time the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */
+ headersTimeout?: number;
+ /** @deprecated unsupported socketTimeout, use headersTimeout & bodyTimeout instead */
+ socketTimeout?: never;
+ /** @deprecated unsupported requestTimeout, use headersTimeout & bodyTimeout instead */
+ requestTimeout?: never;
+ /** TODO */
+ connectTimeout?: number;
+ /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */
+ bodyTimeout?: number;
+ /** @deprecated unsupported idleTimeout, use keepAliveTimeout instead */
+ idleTimeout?: never;
+ /** @deprecated unsupported keepAlive, use pipelining=0 instead */
+ keepAlive?: never;
/** the timeout after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. Default: `4e3` milliseconds (4s). */
- keepAliveTimeout?: number | null;
+ keepAliveTimeout?: number;
+ /** @deprecated unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead */
+ maxKeepAliveTimeout?: never;
/** the maximum allowed `idleTimeout` when overridden by *keep-alive* hints from the server. Default: `600e3` milliseconds (10min). */
- keepAliveMaxTimeout?: number | null;
+ keepAliveMaxTimeout?: number;
/** A number subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */
- keepAliveTimeoutThreshold?: number | null;
+ keepAliveTimeoutThreshold?: number;
+ /** TODO */
+ socketPath?: string;
/** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */
- pipelining?: number | null;
- /** **/
- connect?: buildConnector.BuildOptions | buildConnector.connector | null;
- /** The maximum length of request headers in bytes. Default: `16384` (16KiB). */
- maxHeaderSize?: number | null;
- /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */
- bodyTimeout?: number | null;
- /** The amount of time the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */
- headersTimeout?: number | null;
+ pipelining?: number;
+ /** @deprecated use the connect option instead */
+ tls?: never;
/** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */
strictContentLength?: boolean;
- /** @deprecated use the connect option instead */
- tls?: TlsOptions | null;
- /** */
+ /** TODO */
+ maxCachedSessions?: number;
+ /** TODO */
+ maxRedirections?: number;
+ /** TODO */
+ connect?: buildConnector.BuildOptions | buildConnector.connector;
+ /** TODO */
maxRequestsPerClient?: number;
+ /** TODO */
+ localAddress?: string;
/** Max response body size in bytes, -1 is disabled */
- maxResponseSize?: number | null;
+ maxResponseSize?: number;
/** Enables a family autodetection algorithm that loosely implements section 5 of RFC 8305. */
autoSelectFamily?: boolean;
/** The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. */
- autoSelectFamilyAttemptTimeout?: number;
-
- interceptors?: {Client: readonly DispatchInterceptor[] | undefined}
+ autoSelectFamilyAttemptTimeout?: number;
}
-
export interface SocketInfo {
localAddress?: string
localPort?: number
@@ -61,6 +83,6 @@ declare namespace Client {
bytesWritten?: number
bytesRead?: number
}
-
-
}
+
+export default Client;
diff --git a/deps/undici/src/types/proxy-agent.d.ts b/deps/undici/src/types/proxy-agent.d.ts
index d312cb3f9a..96b26381ce 100644
--- a/deps/undici/src/types/proxy-agent.d.ts
+++ b/deps/undici/src/types/proxy-agent.d.ts
@@ -1,7 +1,9 @@
import Agent from './agent'
import buildConnector from './connector';
+import Client from './client'
import Dispatcher from './dispatcher'
import { IncomingHttpHeaders } from './header'
+import Pool from './pool'
export default ProxyAgent
@@ -23,5 +25,6 @@ declare namespace ProxyAgent {
headers?: IncomingHttpHeaders;
requestTls?: buildConnector.BuildOptions;
proxyTls?: buildConnector.BuildOptions;
+ clientFactory?(origin: URL, opts: object): Dispatcher;
}
}
diff --git a/deps/undici/src/types/websocket.d.ts b/deps/undici/src/types/websocket.d.ts
index 25c46a14c3..dadd8013c1 100644
--- a/deps/undici/src/types/websocket.d.ts
+++ b/deps/undici/src/types/websocket.d.ts
@@ -1,5 +1,6 @@
/// <reference types="node" />
+import type { MessagePort } from 'worker_threads'
import {
EventTarget,
Event,
diff --git a/deps/undici/undici.js b/deps/undici/undici.js
index 145977f83d..cccf3f1043 100644
--- a/deps/undici/undici.js
+++ b/deps/undici/undici.js
@@ -300,7 +300,7 @@ var require_util = __commonJS({
function nop() {
}
function isStream(obj) {
- return obj && typeof obj.pipe === "function";
+ return obj && typeof obj === "object" && typeof obj.pipe === "function" && typeof obj.on === "function";
}
function isBlobLike(object) {
return Blob && object instanceof Blob || object && typeof object === "object" && (typeof object.stream === "function" || typeof object.arrayBuffer === "function") && /^(Blob|File)$/.test(object[Symbol.toStringTag]);
@@ -318,6 +318,10 @@ var require_util = __commonJS({
function parseURL(url) {
if (typeof url === "string") {
url = new URL(url);
+ if (!/^https?:/.test(url.origin || url.protocol)) {
+ throw new InvalidArgumentError("invalid protocol");
+ }
+ return url;
}
if (!url || typeof url !== "object") {
throw new InvalidArgumentError("invalid url");
@@ -549,8 +553,22 @@ var require_util = __commonJS({
}
}, 0);
}
- function isFormDataLike(chunk) {
- return chunk && chunk.constructor && chunk.constructor.name === "FormData" && typeof chunk === "object" && (typeof chunk.append === "function" && typeof chunk.delete === "function" && typeof chunk.get === "function" && typeof chunk.getAll === "function" && typeof chunk.has === "function" && typeof chunk.set === "function" && typeof chunk.entries === "function" && typeof chunk.keys === "function" && typeof chunk.values === "function" && typeof chunk.forEach === "function");
+ function isFormDataLike(object) {
+ return object && typeof object === "object" && typeof object.append === "function" && typeof object.delete === "function" && typeof object.get === "function" && typeof object.getAll === "function" && typeof object.has === "function" && typeof object.set === "function" && object[Symbol.toStringTag] === "FormData";
+ }
+ function throwIfAborted(signal) {
+ if (!signal) {
+ return;
+ }
+ if (typeof signal.throwIfAborted === "function") {
+ signal.throwIfAborted();
+ } else {
+ if (signal.aborted) {
+ const err = new Error("The operation was aborted");
+ err.name = "AbortError";
+ throw err;
+ }
+ }
}
var kEnumerableProperty = /* @__PURE__ */ Object.create(null);
kEnumerableProperty.enumerable = true;
@@ -582,6 +600,7 @@ var require_util = __commonJS({
getSocketInfo,
isFormDataLike,
buildURL,
+ throwIfAborted,
nodeMajor,
nodeMinor,
nodeHasAutoSelectFamily: nodeMajor > 18 || nodeMajor === 18 && nodeMinor >= 13
@@ -767,11 +786,51 @@ var require_constants = __commonJS({
}
});
+// lib/fetch/global.js
+var require_global = __commonJS({
+ "lib/fetch/global.js"(exports2, module2) {
+ "use strict";
+ var globalOrigin = Symbol.for("undici.globalOrigin.1");
+ function getGlobalOrigin() {
+ return globalThis[globalOrigin];
+ }
+ function setGlobalOrigin(newOrigin) {
+ if (newOrigin !== void 0 && typeof newOrigin !== "string" && !(newOrigin instanceof URL)) {
+ throw new Error("Invalid base url");
+ }
+ if (newOrigin === void 0) {
+ Object.defineProperty(globalThis, globalOrigin, {
+ value: void 0,
+ writable: true,
+ enumerable: false,
+ configurable: false
+ });
+ return;
+ }
+ const parsedURL = new URL(newOrigin);
+ if (parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:") {
+ throw new TypeError(`Only http & https urls are allowed, received ${parsedURL.protocol}`);
+ }
+ Object.defineProperty(globalThis, globalOrigin, {
+ value: parsedURL,
+ writable: true,
+ enumerable: false,
+ configurable: false
+ });
+ }
+ module2.exports = {
+ getGlobalOrigin,
+ setGlobalOrigin
+ };
+ }
+});
+
// lib/fetch/util.js
var require_util2 = __commonJS({
"lib/fetch/util.js"(exports2, module2) {
"use strict";
var { redirectStatus, badPorts, referrerPolicy: referrerPolicyTokens } = require_constants();
+ var { getGlobalOrigin } = require_global();
var { performance: performance2 } = require("perf_hooks");
var { isBlobLike, toUSVString, ReadableStreamFrom } = require_util();
var assert = require("assert");
@@ -791,7 +850,9 @@ var require_util2 = __commonJS({
return null;
}
let location = response.headersList.get("location");
- location = location ? new URL(location, responseURL(response)) : null;
+ if (location !== null && isValidHeaderValue(location)) {
+ location = new URL(location, responseURL(response));
+ }
if (location && !location.hash) {
location.hash = requestFragment;
}
@@ -884,7 +945,7 @@ var require_util2 = __commonJS({
let serializedOrigin = request.origin;
if (request.responseTainting === "cors" || request.mode === "websocket") {
if (serializedOrigin) {
- request.headersList.append("Origin", serializedOrigin);
+ request.headersList.append("origin", serializedOrigin);
}
} else if (request.method !== "GET" && request.method !== "HEAD") {
switch (request.referrerPolicy) {
@@ -906,7 +967,7 @@ var require_util2 = __commonJS({
default:
}
if (serializedOrigin) {
- request.headersList.append("Origin", serializedOrigin);
+ request.headersList.append("origin", serializedOrigin);
}
}
}
@@ -929,68 +990,73 @@ var require_util2 = __commonJS({
};
}
function makePolicyContainer() {
- return {};
+ return {
+ referrerPolicy: "strict-origin-when-cross-origin"
+ };
}
- function clonePolicyContainer() {
- return {};
+ function clonePolicyContainer(policyContainer) {
+ return {
+ referrerPolicy: policyContainer.referrerPolicy
+ };
}
function determineRequestsReferrer(request) {
const policy = request.referrerPolicy;
- if (policy == null || policy === "" || policy === "no-referrer") {
- return "no-referrer";
- }
- const environment = request.client;
+ assert(policy);
let referrerSource = null;
if (request.referrer === "client") {
- if (request.client?.globalObject?.constructor?.name === "Window") {
- const origin = environment.globalObject.self?.origin ?? environment.globalObject.location?.origin;
- if (origin == null || origin === "null")
- return "no-referrer";
- referrerSource = new URL(environment.globalObject.location.href);
- } else {
- if (environment?.globalObject?.location == null) {
- return "no-referrer";
- }
- referrerSource = new URL(environment.globalObject.location.href);
+ const globalOrigin = getGlobalOrigin();
+ if (!globalOrigin || globalOrigin.origin === "null") {
+ return "no-referrer";
}
+ referrerSource = new URL(globalOrigin);
} else if (request.referrer instanceof URL) {
referrerSource = request.referrer;
- } else {
- return "no-referrer";
}
- const urlProtocol = referrerSource.protocol;
- if (urlProtocol === "about:" || urlProtocol === "data:" || urlProtocol === "blob:") {
- return "no-referrer";
+ let referrerURL = stripURLForReferrer(referrerSource);
+ const referrerOrigin = stripURLForReferrer(referrerSource, true);
+ if (referrerURL.toString().length > 4096) {
+ referrerURL = referrerOrigin;
}
- let temp;
- let referrerOrigin;
- const referrerUrl = (temp = stripURLForReferrer(referrerSource)).length > 4096 ? referrerOrigin = stripURLForReferrer(referrerSource, true) : temp;
- const areSameOrigin = sameOrigin(request, referrerUrl);
- const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy(referrerUrl) && !isURLPotentiallyTrustworthy(request.url);
+ const areSameOrigin = sameOrigin(request, referrerURL);
+ const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(request.url);
switch (policy) {
case "origin":
return referrerOrigin != null ? referrerOrigin : stripURLForReferrer(referrerSource, true);
case "unsafe-url":
- return referrerUrl;
+ return referrerURL;
case "same-origin":
return areSameOrigin ? referrerOrigin : "no-referrer";
case "origin-when-cross-origin":
- return areSameOrigin ? referrerUrl : referrerOrigin;
- case "strict-origin-when-cross-origin":
- if (areSameOrigin)
- return referrerOrigin;
+ return areSameOrigin ? referrerURL : referrerOrigin;
+ case "strict-origin-when-cross-origin": {
+ const currentURL = requestCurrentURL(request);
+ if (sameOrigin(referrerURL, currentURL)) {
+ return referrerURL;
+ }
+ if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
+ return "no-referrer";
+ }
+ return referrerOrigin;
+ }
case "strict-origin":
case "no-referrer-when-downgrade":
default:
return isNonPotentiallyTrustWorthy ? "no-referrer" : referrerOrigin;
}
- function stripURLForReferrer(url, originOnly = false) {
- const urlObject = new URL(url.href);
- urlObject.username = "";
- urlObject.password = "";
- urlObject.hash = "";
- return originOnly ? urlObject.origin : urlObject.href;
+ }
+ function stripURLForReferrer(url, originOnly) {
+ assert(url instanceof URL);
+ if (url.protocol === "file:" || url.protocol === "about:" || url.protocol === "blank:") {
+ return "no-referrer";
+ }
+ url.username = "";
+ url.password = "";
+ url.hash = "";
+ if (originOnly) {
+ url.pathname = "";
+ url.search = "";
}
+ return url;
}
function isURLPotentiallyTrustworthy(url) {
if (!(url instanceof URL)) {
@@ -5620,7 +5686,6 @@ var require_dataURL = __commonJS({
"lib/fetch/dataURL.js"(exports2, module2) {
var assert = require("assert");
var { atob: atob2 } = require("buffer");
- var { format } = require("url");
var { isValidHTTPToken, isomorphicDecode } = require_util2();
var encoder = new TextEncoder();
var HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+-.^_|~A-z0-9]+$/;
@@ -5660,7 +5725,15 @@ var require_dataURL = __commonJS({
return { mimeType: mimeTypeRecord, body };
}
function URLSerializer(url, excludeFragment = false) {
- return format(url, { fragment: !excludeFragment });
+ const href = url.href;
+ if (!excludeFragment) {
+ return href;
+ }
+ const hash = href.lastIndexOf("#");
+ if (hash === -1) {
+ return href;
+ }
+ return href.slice(0, hash);
}
function collectASequenceOfCodePoints(condition, input, position) {
let result = "";
@@ -6514,45 +6587,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r
}
});
-// lib/fetch/global.js
-var require_global = __commonJS({
- "lib/fetch/global.js"(exports2, module2) {
- "use strict";
- var globalOrigin = Symbol.for("undici.globalOrigin.1");
- function getGlobalOrigin() {
- return globalThis[globalOrigin];
- }
- function setGlobalOrigin(newOrigin) {
- if (newOrigin !== void 0 && typeof newOrigin !== "string" && !(newOrigin instanceof URL)) {
- throw new Error("Invalid base url");
- }
- if (newOrigin === void 0) {
- Object.defineProperty(globalThis, globalOrigin, {
- value: void 0,
- writable: true,
- enumerable: false,
- configurable: false
- });
- return;
- }
- const parsedURL = new URL(newOrigin);
- if (parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:") {
- throw new TypeError(`Only http & https urls are allowed, received ${parsedURL.protocol}`);
- }
- Object.defineProperty(globalThis, globalOrigin, {
- value: parsedURL,
- writable: true,
- enumerable: false,
- configurable: false
- });
- }
- module2.exports = {
- getGlobalOrigin,
- setGlobalOrigin
- };
- }
-});
-
// lib/fetch/response.js
var require_response = __commonJS({
"lib/fetch/response.js"(exports2, module2) {
@@ -6954,7 +6988,8 @@ var require_request = __commonJS({
var {
isValidHTTPToken,
sameOrigin,
- normalizeMethod
+ normalizeMethod,
+ makePolicyContainer
} = require_util2();
var {
forbiddenMethods,
@@ -6989,7 +7024,11 @@ var require_request = __commonJS({
init = webidl.converters.RequestInit(init);
this[kRealm] = {
settingsObject: {
- baseUrl: getGlobalOrigin()
+ baseUrl: getGlobalOrigin(),
+ get origin() {
+ return this.baseUrl?.origin;
+ },
+ policyContainer: makePolicyContainer()
}
};
let request = null;
@@ -7131,12 +7170,14 @@ var require_request = __commonJS({
if (signal.aborted) {
ac.abort(signal.reason);
} else {
- const acRef = new WeakRef(ac);
const abort = function() {
- acRef.deref()?.abort(this.reason);
+ ac.abort(this.reason);
};
- if (getEventListeners(signal, "abort").length >= defaultMaxListeners) {
- setMaxListeners(100, signal);
+ try {
+ if (getEventListeners(signal, "abort").length >= defaultMaxListeners) {
+ setMaxListeners(100, signal);
+ }
+ } catch {
}
signal.addEventListener("abort", abort, { once: true });
requestFinalizer.register(this, { signal, abort });
@@ -7900,12 +7941,14 @@ var require_timers = __commonJS({
let idx = 0;
while (idx < len) {
const timer = fastTimers[idx];
- if (timer.expires && fastNow >= timer.expires) {
- timer.expires = 0;
+ if (timer.state === 0) {
+ timer.state = fastNow + timer.delay;
+ } else if (timer.state > 0 && fastNow >= timer.state) {
+ timer.state = -1;
timer.callback(timer.opaque);
}
- if (timer.expires === 0) {
- timer.active = false;
+ if (timer.state === -1) {
+ timer.state = -2;
if (idx !== len - 1) {
fastTimers[idx] = fastTimers.pop();
} else {
@@ -7936,32 +7979,31 @@ var require_timers = __commonJS({
this.callback = callback;
this.delay = delay;
this.opaque = opaque;
- this.expires = 0;
- this.active = false;
+ this.state = -2;
this.refresh();
}
refresh() {
- if (!this.active) {
- this.active = true;
+ if (this.state === -2) {
fastTimers.push(this);
if (!fastNowTimeout || fastTimers.length === 1) {
refreshTimeout();
- fastNow = Date.now();
}
}
- this.expires = fastNow + this.delay;
+ this.state = 0;
}
clear() {
- this.expires = 0;
+ this.state = -1;
}
};
module2.exports = {
setTimeout(callback, delay, opaque) {
- return new Timeout(callback, delay, opaque);
+ return delay < 1e3 ? setTimeout(callback, delay, opaque) : new Timeout(callback, delay, opaque);
},
clearTimeout(timeout) {
- if (timeout && timeout.clear) {
+ if (timeout instanceof Timeout) {
timeout.clear();
+ } else {
+ clearTimeout(timeout);
}
}
};
@@ -10140,6 +10182,7 @@ upgrade: ${upgrade}\r
}
process.emitWarning(new RequestContentLengthMismatchError());
}
+ socket.cork();
if (bytesWritten === 0) {
if (!expectsPayload) {
socket[kReset] = true;
@@ -10160,6 +10203,7 @@ ${len.toString(16)}\r
}
this.bytesWritten += len;
const ret = socket.write(chunk);
+ socket.uncork();
request.onBodySent(chunk);
if (!ret) {
if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {
@@ -10627,7 +10671,7 @@ var require_fetch = __commonJS({
});
cacheState = "";
}
- response.timingInfo.endTime = coarsenedSharedCurrentTime();
+ timingInfo.endTime = coarsenedSharedCurrentTime();
response.timingInfo = timingInfo;
markResourceTiming(timingInfo, originalURL, initiatorType, globalThis, cacheState);
}
diff --git a/src/undici_version.h b/src/undici_version.h
index d0534a8138..6ef1d2afc6 100644
--- a/src/undici_version.h
+++ b/src/undici_version.h
@@ -2,5 +2,5 @@
// Refer to tools/update-undici.sh
#ifndef SRC_UNDICI_VERSION_H_
#define SRC_UNDICI_VERSION_H_
-#define UNDICI_VERSION "5.20.0"
+#define UNDICI_VERSION "5.21.0"
#endif // SRC_UNDICI_VERSION_H_