Socket
Socket
Sign inDemoInstall

undici

Package Overview
Dependencies
Maintainers
3
Versions
212
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

undici - npm Package Compare versions

Comparing version 6.11.1 to 6.12.0

6

docs/docs/api/Dispatcher.md

@@ -972,2 +972,8 @@ # Dispatcher

Emitted when the dispatcher has been disconnected from the origin.
> **Note**: For HTTP/2, this event is also emitted when the dispatcher has received the [GOAWAY Frame](https://webconcepts.info/concepts/http2-frame-type/0x7) with an Error with the message `HTTP/2: "GOAWAY" frame received` and the code `UND_ERR_INFO`.
> Due to nature of the protocol of using binary frames, it is possible that requests gets hanging as a frame can be received between the `HEADER` and `DATA` frames.
> It is recommended to handle this event and close the dispatcher to create a new HTTP/2 session.
### Event: `'connectionError'`

@@ -974,0 +980,0 @@

5

lib/api/abort-signal.js

@@ -11,7 +11,10 @@ const { addAbortListener } = require('../core/util')

} else {
self.onError(self[kSignal]?.reason ?? new RequestAbortedError())
self.reason = self[kSignal]?.reason ?? new RequestAbortedError()
}
removeSignal(self)
}
function addSignal (self, signal) {
self.reason = null
self[kSignal] = null

@@ -18,0 +21,0 @@ self[kListener] = null

10

lib/api/api-connect.js
'use strict'
const assert = require('node:assert')
const { AsyncResource } = require('node:async_hooks')
const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
const { InvalidArgumentError, SocketError } = require('../core/errors')
const util = require('../core/util')

@@ -35,6 +36,9 @@ const { addSignal, removeSignal } = require('./abort-signal')

onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
if (this.reason) {
abort(this.reason)
return
}
assert(this.callback)
this.abort = abort

@@ -41,0 +45,0 @@ this.context = context

@@ -150,8 +150,10 @@ 'use strict'

if (this.reason) {
abort(this.reason)
return
}
assert(!res, 'pipeline cannot be retried')
assert(!ret.destroyed)
if (ret.destroyed) {
throw new RequestAbortedError()
}
this.abort = abort

@@ -158,0 +160,0 @@ this.context = context

'use strict'
const assert = require('node:assert')
const { Readable } = require('./readable')
const {
InvalidArgumentError,
RequestAbortedError
} = require('../core/errors')
const { InvalidArgumentError } = require('../core/errors')
const util = require('../core/util')

@@ -72,6 +70,9 @@ const { getResolveErrorBodyCallback } = require('./util')

onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
if (this.reason) {
abort(this.reason)
return
}
assert(this.callback)
this.abort = abort

@@ -78,0 +79,0 @@ this.context = context

'use strict'
const assert = require('node:assert')
const { finished, PassThrough } = require('node:stream')
const {
InvalidArgumentError,
InvalidReturnValueError,
RequestAbortedError
} = require('../core/errors')
const { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')
const util = require('../core/util')

@@ -73,6 +70,9 @@ const { getResolveErrorBodyCallback } = require('./util')

onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
if (this.reason) {
abort(this.reason)
return
}
assert(this.callback)
this.abort = abort

@@ -79,0 +79,0 @@ this.context = context

'use strict'
const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
const { InvalidArgumentError, SocketError } = require('../core/errors')
const { AsyncResource } = require('node:async_hooks')

@@ -37,6 +37,9 @@ const util = require('../core/util')

onConnect (abort, context) {
if (!this.callback) {
throw new RequestAbortedError()
if (this.reason) {
abort(this.reason)
return
}
assert(this.callback)
this.abort = abort

@@ -43,0 +46,0 @@ this.context = null

'use strict'
const assert = require('node:assert')
const { kDestroyed, kBodyUsed } = require('./symbols')
const { kDestroyed, kBodyUsed, kListeners } = require('./symbols')
const { IncomingMessage } = require('node:http')

@@ -25,9 +25,16 @@ const stream = require('node:stream')

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])
)
if (object === null) {
return false
} else if (object instanceof Blob) {
return true
} else if (typeof object !== 'object') {
return false
} else {
const sTag = object[Symbol.toStringTag]
return (sTag === 'Blob' || sTag === 'File') && (
('stream' in object && typeof object.stream === 'function') ||
('arrayBuffer' in object && typeof object.arrayBuffer === 'function')
)
}
}

@@ -538,2 +545,25 @@

function addListener (obj, name, listener) {
const listeners = (obj[kListeners] ??= [])
listeners.push([name, listener])
obj.on(name, listener)
return obj
}
function removeAllListeners (obj) {
for (const [name, listener] of obj[kListeners] ?? []) {
obj.removeListener(name, listener)
}
obj[kListeners] = null
}
function errorRequest (client, request, err) {
try {
request.onError(err)
assert(request.aborted)
} catch (err) {
client.emit('error', err)
}
}
const kEnumerableProperty = Object.create(null)

@@ -561,2 +591,5 @@ kEnumerableProperty.enumerable = true

bufferToLowerCasedHeaderName,
addListener,
removeAllListeners,
errorRequest,
parseRawHeaders,

@@ -563,0 +596,0 @@ parseHeaders,

@@ -50,3 +50,2 @@ 'use strict'

kMaxResponseSize,
kListeners,
kOnError,

@@ -60,19 +59,7 @@ kResume,

const FastBuffer = Buffer[Symbol.species]
const addListener = util.addListener
const removeAllListeners = util.removeAllListeners
let extractBody
function addListener (obj, name, listener) {
const listeners = (obj[kListeners] ??= [])
listeners.push([name, listener])
obj.on(name, listener)
return obj
}
function removeAllListeners (obj) {
for (const [name, listener] of obj[kListeners] ?? []) {
obj.removeListener(name, listener)
}
obj[kListeners] = null
}
async function lazyllhttp () {

@@ -724,3 +711,3 @@ const llhttpWasmData = process.env.JEST_WORKER_ID ? require('../llhttp/llhttp-wasm.js') : undefined

const request = requests[i]
errorRequest(client, request, err)
util.errorRequest(client, request, err)
}

@@ -732,3 +719,3 @@ } else if (client[kRunning] > 0 && err.code !== 'UND_ERR_INFO') {

errorRequest(client, request, err)
util.errorRequest(client, request, err)
}

@@ -838,11 +825,2 @@

function errorRequest (client, request, err) {
try {
request.onError(err)
assert(request.aborted)
} catch (err) {
client.emit('error', err)
}
}
// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2

@@ -914,3 +892,3 @@ function shouldSendContentLength (method) {

if (client[kStrictContentLength]) {
errorRequest(client, request, new RequestContentLengthMismatchError())
util.errorRequest(client, request, new RequestContentLengthMismatchError())
return false

@@ -924,18 +902,20 @@ }

try {
request.onConnect((err) => {
if (request.aborted || request.completed) {
return
}
const abort = (err) => {
if (request.aborted || request.completed) {
return
}
errorRequest(client, request, err || new RequestAbortedError())
util.errorRequest(client, request, err || new RequestAbortedError())
util.destroy(socket, new InformationalError('aborted'))
})
util.destroy(body)
util.destroy(socket, new InformationalError('aborted'))
}
try {
request.onConnect(abort)
} catch (err) {
errorRequest(client, request, err)
util.errorRequest(client, request, err)
}
if (request.aborted) {
util.destroy(body)
return false

@@ -1008,31 +988,15 @@ }

if (!body || bodyLength === 0) {
if (contentLength === 0) {
socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1')
} else {
assert(contentLength === null, 'no body must not have content length')
socket.write(`${header}\r\n`, 'latin1')
}
request.onRequestSent()
writeBuffer({ abort, body: null, client, request, socket, contentLength, header, expectsPayload })
} else if (util.isBuffer(body)) {
assert(contentLength === body.byteLength, 'buffer body must have content length')
socket.cork()
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
socket.write(body)
socket.uncork()
request.onBodySent(body)
request.onRequestSent()
if (!expectsPayload) {
socket[kReset] = true
}
writeBuffer({ abort, body, client, request, socket, contentLength, header, expectsPayload })
} else if (util.isBlobLike(body)) {
if (typeof body.stream === 'function') {
writeIterable({ body: body.stream(), client, request, socket, contentLength, header, expectsPayload })
writeIterable({ abort, body: body.stream(), client, request, socket, contentLength, header, expectsPayload })
} else {
writeBlob({ body, client, request, socket, contentLength, header, expectsPayload })
writeBlob({ abort, body, client, request, socket, contentLength, header, expectsPayload })
}
} else if (util.isStream(body)) {
writeStream({ body, client, request, socket, contentLength, header, expectsPayload })
writeStream({ abort, body, client, request, socket, contentLength, header, expectsPayload })
} else if (util.isIterable(body)) {
writeIterable({ body, client, request, socket, contentLength, header, expectsPayload })
writeIterable({ abort, body, client, request, socket, contentLength, header, expectsPayload })
} else {

@@ -1045,3 +1009,3 @@ assert(false)

function writeStream ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) {
function writeStream ({ abort, body, client, request, socket, contentLength, header, expectsPayload }) {
assert(contentLength !== 0 || client[kRunning] === 0, 'stream body cannot be pipelined')

@@ -1051,3 +1015,3 @@

const writer = new AsyncWriter({ socket, request, contentLength, client, expectsPayload, header })
const writer = new AsyncWriter({ abort, socket, request, contentLength, client, expectsPayload, header })

@@ -1150,3 +1114,33 @@ const onData = function (chunk) {

async function writeBlob ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) {
async function writeBuffer ({ abort, body, client, request, socket, contentLength, header, expectsPayload }) {
try {
if (!body) {
if (contentLength === 0) {
socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1')
} else {
assert(contentLength === null, 'no body must not have content length')
socket.write(`${header}\r\n`, 'latin1')
}
} else if (util.isBuffer(body)) {
assert(contentLength === body.byteLength, 'buffer body must have content length')
socket.cork()
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
socket.write(body)
socket.uncork()
request.onBodySent(body)
if (!expectsPayload) {
socket[kReset] = true
}
}
request.onRequestSent()
client[kResume]()
} catch (err) {
abort(err)
}
}
async function writeBlob ({ abort, body, client, request, socket, contentLength, header, expectsPayload }) {
assert(contentLength === body.size, 'blob body must have content length')

@@ -1175,7 +1169,7 @@

} catch (err) {
util.destroy(socket, err)
abort(err)
}
}
async function writeIterable ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) {
async function writeIterable ({ abort, body, client, request, socket, contentLength, header, expectsPayload }) {
assert(contentLength !== 0 || client[kRunning] === 0, 'iterator body cannot be pipelined')

@@ -1206,3 +1200,3 @@

const writer = new AsyncWriter({ socket, request, contentLength, client, expectsPayload, header })
const writer = new AsyncWriter({ abort, socket, request, contentLength, client, expectsPayload, header })
try {

@@ -1231,3 +1225,3 @@ // It's up to the user to somehow abort the async iterable.

class AsyncWriter {
constructor ({ socket, request, contentLength, client, expectsPayload, header }) {
constructor ({ abort, socket, request, contentLength, client, expectsPayload, header }) {
this.socket = socket

@@ -1240,2 +1234,3 @@ this.request = request

this.header = header
this.abort = abort

@@ -1356,3 +1351,3 @@ socket[kWriting] = true

destroy (err) {
const { socket, client } = this
const { socket, client, abort } = this

@@ -1363,3 +1358,3 @@ socket[kWriting] = false

assert(client[kRunning] <= 1, 'pipeline should only contain this request')
util.destroy(socket, err)
abort(err)
}

@@ -1366,0 +1361,0 @@ }

@@ -25,3 +25,2 @@ 'use strict'

kOnError,
// HTTP2
kMaxConcurrentStreams,

@@ -59,10 +58,16 @@ kHTTP2Session,

function parseH2Headers (headers) {
// set-cookie is always an array. Duplicates are added to the array.
// For duplicate cookie headers, the values are joined together with '; '.
headers = Object.entries(headers).flat(2)
const result = []
for (const header of headers) {
result.push(Buffer.from(header))
for (const [name, value] of Object.entries(headers)) {
// h2 may concat the header value by array
// e.g. Set-Cookie
if (Array.isArray(value)) {
for (const subvalue of value) {
// we need to provide each header value of header name
// because the headers handler expect name-value pair
result.push(Buffer.from(name), Buffer.from(subvalue))
}
} else {
result.push(Buffer.from(name), Buffer.from(value))
}
}

@@ -91,12 +96,14 @@

session[kSocket] = socket
session.on('error', onHttp2SessionError)
session.on('frameError', onHttp2FrameError)
session.on('end', onHttp2SessionEnd)
session.on('goaway', onHTTP2GoAway)
session.on('close', function () {
util.addListener(session, 'error', onHttp2SessionError)
util.addListener(session, 'frameError', onHttp2FrameError)
util.addListener(session, 'end', onHttp2SessionEnd)
util.addListener(session, 'goaway', onHTTP2GoAway)
util.addListener(session, 'close', function () {
const { [kClient]: client } = this
const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
const err = this[kSocket][kError] || new SocketError('closed', util.getSocketInfo(this))
client[kSocket] = null
client[kHTTP2Session] = null

@@ -109,3 +116,3 @@ assert(client[kPending] === 0)

const request = requests[i]
errorRequest(client, request, err)
util.errorRequest(client, request, err)
}

@@ -121,2 +128,3 @@

})
session.unref()

@@ -127,3 +135,3 @@

socket.on('error', function (err) {
util.addListener(socket, 'error', function (err) {
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')

@@ -135,3 +143,4 @@

})
socket.on('end', function () {
util.addListener(socket, 'end', function () {
util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))

@@ -176,3 +185,2 @@ })

this[kSocket][kError] = err
this[kClient][kOnError](err)

@@ -182,5 +190,4 @@ }

function onHttp2FrameError (type, code, id) {
const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`)
if (id === 0) {
const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`)
this[kSocket][kError] = err

@@ -192,51 +199,28 @@ this[kClient][kOnError](err)

function onHttp2SessionEnd () {
this.destroy(new SocketError('other side closed'))
util.destroy(this[kSocket], new SocketError('other side closed'))
const err = new SocketError('other side closed', util.getSocketInfo(this[kSocket]))
this.destroy(err)
util.destroy(this[kSocket], err)
}
/**
* This is the root cause of #3011
* We need to handle GOAWAY frames properly, and trigger the session close
* along with the socket right away
* Find a way to trigger the close cycle from here on.
*/
function onHTTP2GoAway (code) {
const client = this[kClient]
const err = new InformationalError(`HTTP/2: "GOAWAY" frame received with code ${code}`)
client[kSocket] = null
client[kHTTP2Session] = null
if (client.destroyed) {
assert(this[kPending] === 0)
// We need to trigger the close cycle right away
// We need to destroy the session and the socket
// Requests should be failed with the error after the current one is handled
this[kSocket][kError] = err
this[kClient][kOnError](err)
// Fail entire queue.
const requests = client[kQueue].splice(client[kRunningIdx])
for (let i = 0; i < requests.length; i++) {
const request = requests[i]
errorRequest(this, request, err)
}
} else if (client[kRunning] > 0) {
// Fail head of pipeline.
const request = client[kQueue][client[kRunningIdx]]
client[kQueue][client[kRunningIdx]++] = null
errorRequest(client, request, err)
}
client[kPendingIdx] = client[kRunningIdx]
assert(client[kRunning] === 0)
client.emit('disconnect',
client[kUrl],
[client],
err
)
client[kResume]()
this.unref()
// We send the GOAWAY frame response as no error
this.destroy()
util.destroy(this[kSocket], err)
}
function errorRequest (client, request, err) {
try {
request.onError(err)
assert(request.aborted)
} catch (err) {
client.emit('error', err)
}
}
// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2

@@ -252,3 +236,3 @@ function shouldSendContentLength (method) {

if (upgrade) {
errorRequest(client, request, new Error('Upgrade not supported for H2'))
util.errorRequest(client, request, new Error('Upgrade not supported for H2'))
return false

@@ -306,6 +290,6 @@ }

errorRequest(client, request, err)
util.errorRequest(client, request, err)
})
} catch (err) {
errorRequest(client, request, err)
util.errorRequest(client, request, err)
}

@@ -385,3 +369,3 @@

if (client[kStrictContentLength]) {
errorRequest(client, request, new RequestContentLengthMismatchError())
util.errorRequest(client, request, new RequestContentLengthMismatchError())
return false

@@ -428,3 +412,3 @@ }

const err = new RequestAbortedError()
errorRequest(client, request, err)
util.errorRequest(client, request, err)
util.destroy(stream, err)

@@ -463,3 +447,3 @@ return

const err = new InformationalError('HTTP/2: stream half-closed (remote)')
errorRequest(client, request, err)
util.errorRequest(client, request, err)
util.destroy(stream, err)

@@ -470,3 +454,2 @@ })

session[kOpenStreams] -= 1
// TODO(HTTP/2): unref only if current streams count is 0
if (session[kOpenStreams] === 0) {

@@ -480,2 +463,3 @@ session.unref()

session[kOpenStreams] -= 1
util.errorRequest(client, request, err)
util.destroy(stream, err)

@@ -487,3 +471,3 @@ }

const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`)
errorRequest(client, request, err)
util.errorRequest(client, request, err)

@@ -490,0 +474,0 @@ if (client[kHTTP2Session] && !client[kHTTP2Session].destroyed && !this.closed && !this.destroyed) {

@@ -341,3 +341,3 @@ // @ts-check

const request = requests[i]
errorRequest(this, request, err)
util.errorRequest(this, request, err)
}

@@ -382,3 +382,3 @@

const request = requests[i]
errorRequest(client, request, err)
util.errorRequest(client, request, err)
}

@@ -507,3 +507,3 @@ assert(client[kSize] === 0)

const request = client[kQueue][client[kPendingIdx]++]
errorRequest(client, request, err)
util.errorRequest(client, request, err)
}

@@ -587,3 +587,6 @@ } else {

client[kServerName] = request.servername
client[kHTTPContext]?.destroy(new InformationalError('servername changed'))
client[kHTTPContext]?.destroy(new InformationalError('servername changed'), () => {
client[kHTTPContext] = null
resume(client)
})
}

@@ -616,11 +619,2 @@

function errorRequest (client, request, err) {
try {
request.onError(err)
assert(request.aborted)
} catch (err) {
client.emit('error', err)
}
}
module.exports = Client

@@ -67,5 +67,5 @@ 'use strict'

connect: async (opts, callback) => {
let requestedHost = opts.host
let requestedPath = opts.host
if (!opts.port) {
requestedHost += `:${defaultProtocolPort(opts.protocol)}`
requestedPath += `:${defaultProtocolPort(opts.protocol)}`
}

@@ -76,7 +76,7 @@ try {

port,
path: requestedHost,
path: requestedPath,
signal: opts.signal,
headers: {
...this[kProxyHeaders],
host: requestedHost
host: opts.host
},

@@ -113,12 +113,14 @@ servername: this[kProxyTls]?.servername || proxyHostname

dispatch (opts, handler) {
const { host } = new URL(opts.origin)
const headers = buildHeaders(opts.headers)
throwIfProxyAuthIsSent(headers)
if (headers && !('host' in headers) && !('Host' in headers)) {
const { host } = new URL(opts.origin)
headers.host = host
}
return this[kAgent].dispatch(
{
...opts,
headers: {
...headers,
host
}
headers
},

@@ -125,0 +127,0 @@ handler

@@ -93,3 +93,3 @@ 'use strict'

createMockScopeDispatchData (statusCode, data, responseOptions = {}) {
createMockScopeDispatchData ({ statusCode, data, responseOptions }) {
const responseData = getResponseData(data)

@@ -103,10 +103,7 @@ const contentLength = this[kContentLength] ? { 'content-length': responseData.length } : {}

validateReplyParameters (statusCode, data, responseOptions) {
if (typeof statusCode === 'undefined') {
validateReplyParameters (replyParameters) {
if (typeof replyParameters.statusCode === 'undefined') {
throw new InvalidArgumentError('statusCode must be defined')
}
if (typeof data === 'undefined') {
throw new InvalidArgumentError('data must be defined')
}
if (typeof responseOptions !== 'object' || responseOptions === null) {
if (typeof replyParameters.responseOptions !== 'object' || replyParameters.responseOptions === null) {
throw new InvalidArgumentError('responseOptions must be an object')

@@ -119,6 +116,6 @@ }

*/
reply (replyData) {
reply (replyOptionsCallbackOrStatusCode) {
// Values of reply aren't available right now as they
// can only be available when the reply callback is invoked.
if (typeof replyData === 'function') {
if (typeof replyOptionsCallbackOrStatusCode === 'function') {
// We'll first wrap the provided callback in another function,

@@ -129,15 +126,15 @@ // this function will properly resolve the data from the callback

// Our reply options callback contains the parameter for statusCode, data and options.
const resolvedData = replyData(opts)
const resolvedData = replyOptionsCallbackOrStatusCode(opts)
// Check if it is in the right format
if (typeof resolvedData !== 'object') {
if (typeof resolvedData !== 'object' || resolvedData === null) {
throw new InvalidArgumentError('reply options callback must return an object')
}
const { statusCode, data = '', responseOptions = {} } = resolvedData
this.validateReplyParameters(statusCode, data, responseOptions)
const replyParameters = { data: '', responseOptions: {}, ...resolvedData }
this.validateReplyParameters(replyParameters)
// Since the values can be obtained immediately we return them
// from this higher order function that will be resolved later.
return {
...this.createMockScopeDispatchData(statusCode, data, responseOptions)
...this.createMockScopeDispatchData(replyParameters)
}

@@ -155,7 +152,11 @@ }

// just be the statusCode.
const [statusCode, data = '', responseOptions = {}] = [...arguments]
this.validateReplyParameters(statusCode, data, responseOptions)
const replyParameters = {
statusCode: replyOptionsCallbackOrStatusCode,
data: arguments[1] === undefined ? '' : arguments[1],
responseOptions: arguments[2] === undefined ? {} : arguments[2]
}
this.validateReplyParameters(replyParameters)
// Send in-already provided data like usual
const dispatchData = this.createMockScopeDispatchData(statusCode, data, responseOptions)
const dispatchData = this.createMockScopeDispatchData(replyParameters)
const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], dispatchData)

@@ -162,0 +163,0 @@ return new MockScope(newMockDispatch)

@@ -11,3 +11,3 @@ 'use strict'

} = require('./mock-symbols')
const { buildURL, nop } = require('../core/util')
const { buildURL } = require('../core/util')
const { STATUS_CODES } = require('node:http')

@@ -289,6 +289,6 @@ const {

handler.abort = nop
handler.onHeaders(statusCode, responseHeaders, resume, getStatusText(statusCode))
handler.onData(Buffer.from(responseData))
handler.onComplete(responseTrailers)
handler.onConnect?.(err => handler.onError(err), null)
handler.onHeaders?.(statusCode, responseHeaders, resume, getStatusText(statusCode))
handler.onData?.(Buffer.from(responseData))
handler.onComplete?.(responseTrailers)
deleteMockDispatch(mockDispatches, key)

@@ -295,0 +295,0 @@ }

@@ -17,4 +17,4 @@ 'use strict'

'540', '548', '554', '556', '563', '587', '601', '636', '989', '990', '993', '995', '1719', '1720', '1723',
'2049', '3659', '4045', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6697',
'10080'
'2049', '3659', '4045', '4190', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6679',
'6697', '10080'
]

@@ -21,0 +21,0 @@

@@ -7,3 +7,3 @@ 'use strict'

const { channels } = require('../../core/diagnostics')
const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived } = require('./util')
const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived, utf8Decode } = require('./util')
const { WebsocketFrameSend } = require('./frame')

@@ -318,4 +318,3 @@

try {
// TODO: optimize this
reason = new TextDecoder('utf-8', { fatal: true }).decode(reason)
reason = utf8Decode(reason)
} catch {

@@ -322,0 +321,0 @@ return null

@@ -6,2 +6,3 @@ 'use strict'

const { MessageEvent, ErrorEvent } = require('./events')
const { isUtf8 } = require('node:buffer')

@@ -91,3 +92,3 @@ /* globals Blob */

try {
dataForEvent = new TextDecoder('utf-8', { fatal: true }).decode(data)
dataForEvent = utf8Decode(data)
} catch {

@@ -205,2 +206,29 @@ failWebsocketConnection(ws, 'Received invalid UTF-8 in text frame.')

// https://nodejs.org/api/intl.html#detecting-internationalization-support
const hasIntl = typeof process.versions.icu === 'string'
const fatalDecoder = hasIntl ? new TextDecoder('utf-8', { fatal: true }) : undefined
/**
* Converts a Buffer to utf-8, even on platforms without icu.
* @param {Buffer} buffer
*/
function utf8Decode (buffer) {
if (hasIntl) {
return fatalDecoder.decode(buffer)
} else {
if (!isUtf8?.(buffer)) {
// TODO: remove once node 18 or < node v18.14.0 is dropped
if (!isUtf8) {
process.emitWarning('ICU is not supported and no fallback exists. Please upgrade to at least Node v18.14.0.', {
code: 'UNDICI-WS-NO-ICU'
})
}
throw new TypeError('Invalid utf-8 received.')
}
return buffer.toString('utf-8')
}
}
module.exports = {

@@ -215,3 +243,4 @@ isConnecting,

failWebsocketConnection,
websocketMessageReceived
websocketMessageReceived,
utf8Decode
}
{
"name": "undici",
"version": "6.11.1",
"version": "6.12.0",
"description": "An HTTP/1.1 client, written from scratch for Node.js",

@@ -71,12 +71,16 @@ "homepage": "https://undici.nodejs.org",

"test": "npm run test:javascript && cross-env NODE_V8_COVERAGE= npm run test:typescript",
"test:javascript": "node scripts/generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:eventsource && npm run test:wpt && npm run test:websocket && npm run test:node-test && npm run test:jest",
"test:javascript:withoutintl": "node scripts/generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:fetch:nobuild && npm run test:cookies && npm run test:eventsource:nobuild && npm run test:wpt:withoutintl && npm run test:node-test",
"test:javascript": "node scripts/generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:cache && npm run test:interceptors && npm run test:fetch && npm run test:cookies && npm run test:eventsource && npm run test:wpt && npm run test:websocket && npm run test:node-test && npm run test:jest",
"test:javascript:withoutintl": "node scripts/generate-pem && npm run test:unit && npm run test:node-fetch && npm run test:fetch:nobuild && npm run test:cache && npm run test:interceptors && npm run test:cookies && npm run test:eventsource:nobuild && npm run test:wpt:withoutintl && npm run test:node-test",
"test:busboy": "borp -p \"test/busboy/*.js\"",
"test:cache": "borp -p \"test/cache/*.js\"",
"test:cookies": "borp -p \"test/cookie/*.js\"",
"test:node-fetch": "borp -p \"test/node-fetch/**/*.js\"",
"test:eventsource": "npm run build:node && npm run test:eventsource:nobuild",
"test:eventsource:nobuild": "borp --expose-gc -p \"test/eventsource/*.js\"",
"test:fuzzing": "node test/fuzzing/fuzzing.test.js",
"test:fetch": "npm run build:node && npm run test:fetch:nobuild",
"test:fetch:nobuild": "borp --expose-gc -p \"test/fetch/*.js\" && borp -p \"test/webidl/*.js\" && borp -p \"test/busboy/*.js\"",
"test:fetch:nobuild": "borp --expose-gc -p \"test/fetch/*.js\" && npm run test:webidl && npm run test:busboy",
"test:interceptors": "borp -p \"test/interceptors/*.js\"",
"test:jest": "cross-env NODE_V8_COVERAGE= jest",
"test:unit": "borp --expose-gc -p \"test/*.js\"",
"test:node-fetch": "borp -p \"test/node-fetch/**/*.js\"",
"test:node-test": "borp -p \"test/node-test/**/*.js\"",

@@ -86,2 +90,3 @@ "test:tdd": "borp --expose-gc -p \"test/*.js\"",

"test:typescript": "tsd && tsc --skipLibCheck test/imports/undici-import.ts",
"test:webidl": "borp -p \"test/webidl/*.js\"",
"test:websocket": "borp -p \"test/websocket/*.js\"",

@@ -97,4 +102,3 @@ "test:wpt": "node test/wpt/start-fetch.mjs && node test/wpt/start-FileAPI.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node test/wpt/start-websockets.mjs && node test/wpt/start-cacheStorage.mjs && node test/wpt/start-eventsource.mjs",

"serve:website": "echo \"Error: Documentation has been moved to '/docs'\" && exit 1",
"prepare": "husky install && node ./scripts/platform-shell.js",
"fuzz": "jsfuzz test/fuzzing/fuzz.js corpus"
"prepare": "husky install && node ./scripts/platform-shell.js"
},

@@ -110,2 +114,3 @@ "devDependencies": {

"dns-packet": "^5.4.0",
"fast-check": "^3.17.1",
"form-data": "^4.0.0",

@@ -117,3 +122,2 @@ "formdata-node": "^6.0.3",

"jsdom": "^24.0.0",
"jsfuzz": "^1.0.15",
"node-forge": "^1.3.1",

@@ -120,0 +124,0 @@ "pre-commit": "^1.2.2",

@@ -228,3 +228,3 @@ import { URL } from 'url'

/** Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. */
onHeaders?(statusCode: number, headers: Buffer[] | string[] | null, resume: () => void, statusText: string): boolean;
onHeaders?(statusCode: number, headers: Buffer[], resume: () => void, statusText: string): boolean;
/** Invoked when response payload data is received. */

@@ -231,0 +231,0 @@ onData?(chunk: Buffer): boolean;

@@ -166,3 +166,3 @@ // based on https://github.com/Ethan-Arrowood/undici-fetch/blob/249269714db874351589d2d364a0645d5160ae71/index.d.ts (MIT license)

readonly redirect: RequestRedirect
readonly referrerPolicy: string
readonly referrerPolicy: ReferrerPolicy
readonly url: string

@@ -169,0 +169,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc