Skip to content

Commit

Permalink
deps: update undici to 5.14.0
Browse files Browse the repository at this point in the history
PR-URL: nodejs#45812
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
  • Loading branch information
nodejs-github-bot authored Dec 13, 2022
1 parent 8b89d4d commit 0429774
Show file tree
Hide file tree
Showing 16 changed files with 359 additions and 137 deletions.
4 changes: 3 additions & 1 deletion deps/undici/src/docs/api/Connector.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ Once you call `buildConnector`, it will return a connector function, which takes
* **hostname** `string` (required)
* **host** `string` (optional)
* **protocol** `string` (required)
* **port** `number` (required)
* **port** `string` (required)
* **servername** `string` (optional)
* **localAddress** `string | null` (optional) Local address the socket should connect from.
* **httpSocket** `Socket` (optional) Establish secure connection on a given socket rather than creating a new socket. It can only be sent on TLS update.

### Basic example

Expand Down
81 changes: 62 additions & 19 deletions deps/undici/src/lib/core/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,81 @@ const net = require('net')
const assert = require('assert')
const util = require('./util')
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')

let tls // include tls conditionally since it is not always available

// TODO: session re-use does not wait for the first
// connection to resolve the session and might therefore
// resolve the same servername multiple times even when
// re-use is enabled.

let SessionCache
if (global.FinalizationRegistry) {
SessionCache = class WeakSessionCache {
constructor (maxCachedSessions) {
this._maxCachedSessions = maxCachedSessions
this._sessionCache = new Map()
this._sessionRegistry = new global.FinalizationRegistry((key) => {
if (this._sessionCache.size < this._maxCachedSessions) {
return
}

const ref = this._sessionCache.get(key)
if (ref !== undefined && ref.deref() === undefined) {
this._sessionCache.delete(key)
}
})
}

get (sessionKey) {
const ref = this._sessionCache.get(sessionKey)
return ref ? ref.deref() : null
}

set (sessionKey, session) {
if (this._maxCachedSessions === 0) {
return
}

this._sessionCache.set(sessionKey, new WeakRef(session))
this._sessionRegistry.register(session, sessionKey)
}
}
} else {
SessionCache = class SimpleSessionCache {
constructor (maxCachedSessions) {
this._maxCachedSessions = maxCachedSessions
this._sessionCache = new Map()
}

get (sessionKey) {
return this._sessionCache.get(sessionKey)
}

set (sessionKey, session) {
if (this._maxCachedSessions === 0) {
return
}

if (this._sessionCache.size >= this._maxCachedSessions) {
// remove the oldest session
const { value: oldestKey } = this._sessionCache.keys().next()
this._sessionCache.delete(oldestKey)
}

this._sessionCache.set(sessionKey, session)
}
}
}

function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
}

const options = { path: socketPath, ...opts }
const sessionCache = new Map()
const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)
timeout = timeout == null ? 10e3 : timeout
maxCachedSessions = maxCachedSessions == null ? 100 : maxCachedSessions

return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
let socket
Expand Down Expand Up @@ -47,25 +106,9 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {

socket
.on('session', function (session) {
// cache is disabled
if (maxCachedSessions === 0) {
return
}

if (sessionCache.size >= maxCachedSessions) {
// remove the oldest session
const { value: oldestKey } = sessionCache.keys().next()
sessionCache.delete(oldestKey)
}

// TODO (fix): Can a session become invalid once established? Don't think so?
sessionCache.set(sessionKey, session)
})
.on('error', function (err) {
if (sessionKey && err.code !== 'UND_ERR_INFO') {
// TODO (fix): Only delete for session related errors.
sessionCache.delete(sessionKey)
}
})
} else {
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
socket = net.connect({
Expand Down
45 changes: 38 additions & 7 deletions deps/undici/src/lib/fetch/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ const { FormData } = require('./formdata')
const { kState } = require('./symbols')
const { webidl } = require('./webidl')
const { DOMException, structuredClone } = require('./constants')
const { Blob } = require('buffer')
const { Blob, File: NativeFile } = require('buffer')
const { kBodyUsed } = require('../core/symbols')
const assert = require('assert')
const { isErrored } = require('../core/util')
const { isUint8Array, isArrayBuffer } = require('util/types')
const { File } = require('./file')
const { File: UndiciFile } = require('./file')
const { StringDecoder } = require('string_decoder')
const { parseMIMEType, serializeAMimeType } = require('./dataURL')

/** @type {globalThis['ReadableStream']} */
let ReadableStream
let ReadableStream = globalThis.ReadableStream

/** @type {globalThis['File']} */
const File = NativeFile ?? UndiciFile

// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
function extractBody (object, keepalive = false) {
Expand Down Expand Up @@ -142,7 +144,33 @@ function extractBody (object, keepalive = false) {
source = object

// Set length to unclear, see html/6424 for improving this.
// TODO
length = (() => {
const prefixLength = prefix.length
const boundaryLength = boundary.length
let bodyLength = 0

for (const [name, value] of object) {
if (typeof value === 'string') {
bodyLength +=
prefixLength +
Buffer.byteLength(`; name="${escape(normalizeLinefeeds(name))}"\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
} else {
bodyLength +=
prefixLength +
Buffer.byteLength(`; name="${escape(normalizeLinefeeds(name))}"` + (value.name ? `; filename="${escape(value.name)}"` : '')) +
2 + // \r\n
`Content-Type: ${
value.type || 'application/octet-stream'
}\r\n\r\n`.length

// value is a Blob or File, and \r\n
bodyLength += value.size + 2
}
}

bodyLength += boundaryLength + 4 // --boundary--
return bodyLength
})()

// Set type to `multipart/form-data; boundary=`,
// followed by the multipart/form-data boundary string generated
Expand Down Expand Up @@ -348,7 +376,10 @@ function bodyMixinMethods (instance) {
let busboy

try {
busboy = Busboy({ headers })
busboy = Busboy({
headers,
defParamCharset: 'utf8'
})
} catch (err) {
// Error due to headers:
throw Object.assign(new TypeError(), { cause: err })
Expand All @@ -361,7 +392,7 @@ function bodyMixinMethods (instance) {
const { filename, encoding, mimeType } = info
const chunks = []

if (encoding.toLowerCase() === 'base64') {
if (encoding === 'base64' || encoding.toLowerCase() === 'base64') {
let base64chunk = ''

value.on('data', (chunk) => {
Expand Down
15 changes: 9 additions & 6 deletions deps/undici/src/lib/fetch/file.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { Blob } = require('buffer')
const { Blob, File: NativeFile } = require('buffer')
const { types } = require('util')
const { kState } = require('./symbols')
const { isBlobLike } = require('./util')
Expand Down Expand Up @@ -329,11 +329,14 @@ function convertLineEndingsNative (s) {
// rollup) will warn about circular dependencies. See:
// https://github.com/nodejs/undici/issues/1629
function isFileLike (object) {
return object instanceof File || (
object &&
(typeof object.stream === 'function' ||
typeof object.arrayBuffer === 'function') &&
object[Symbol.toStringTag] === 'File'
return (
(NativeFile && object instanceof NativeFile) ||
object instanceof File || (
object &&
(typeof object.stream === 'function' ||
typeof object.arrayBuffer === 'function') &&
object[Symbol.toStringTag] === 'File'
)
)
}

Expand Down
7 changes: 5 additions & 2 deletions deps/undici/src/lib/fetch/formdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

const { isBlobLike, toUSVString, makeIterator } = require('./util')
const { kState } = require('./symbols')
const { File, FileLike, isFileLike } = require('./file')
const { File: UndiciFile, FileLike, isFileLike } = require('./file')
const { webidl } = require('./webidl')
const { Blob } = require('buffer')
const { Blob, File: NativeFile } = require('buffer')

/** @type {globalThis['File']} */
const File = NativeFile ?? UndiciFile

// https://xhr.spec.whatwg.org/#formdata
class FormData {
Expand Down
32 changes: 22 additions & 10 deletions deps/undici/src/lib/fetch/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'use strict'

const { kHeadersList } = require('../core/symbols')
const { kGuard } = require('./symbols')
const { kGuard, kHeadersCaseInsensitive } = require('./symbols')
const { kEnumerableProperty } = require('../core/util')
const {
makeIterator,
Expand Down Expand Up @@ -96,27 +96,27 @@ class HeadersList {

// 1. If list contains name, then set name to the first such
// header’s name.
name = name.toLowerCase()
const exists = this[kHeadersMap].get(name)
const lowercaseName = name.toLowerCase()
const exists = this[kHeadersMap].get(lowercaseName)

// 2. Append (name, value) to list.
if (exists) {
this[kHeadersMap].set(name, `${exists}, ${value}`)
this[kHeadersMap].set(lowercaseName, { name: exists.name, value: `${exists.value}, ${value}` })
} else {
this[kHeadersMap].set(name, `${value}`)
this[kHeadersMap].set(lowercaseName, { name, value })
}
}

// https://fetch.spec.whatwg.org/#concept-header-list-set
set (name, value) {
this[kHeadersSortedMap] = null
name = name.toLowerCase()
const lowercaseName = name.toLowerCase()

// 1. If list contains name, then set the value of
// the first such header to value and remove the
// others.
// 2. Otherwise, append header (name, value) to list.
return this[kHeadersMap].set(name, value)
return this[kHeadersMap].set(lowercaseName, { name, value })
}

// https://fetch.spec.whatwg.org/#concept-header-list-delete
Expand All @@ -137,14 +137,26 @@ class HeadersList {
// 2. Return the values of all headers in list whose name
// is a byte-case-insensitive match for name,
// separated from each other by 0x2C 0x20, in order.
return this[kHeadersMap].get(name.toLowerCase()) ?? null
return this[kHeadersMap].get(name.toLowerCase())?.value ?? null
}

* [Symbol.iterator] () {
for (const pair of this[kHeadersMap]) {
yield pair
// use the lowercased name
for (const [name, { value }] of this[kHeadersMap]) {
yield [name, value]
}
}

get [kHeadersCaseInsensitive] () {
/** @type {string[]} */
const flatList = []

for (const { name, value } of this[kHeadersMap].values()) {
flatList.push(name, value)
}

return flatList
}
}

// https://fetch.spec.whatwg.org/#headers-class
Expand Down
Loading

0 comments on commit 0429774

Please sign in to comment.