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 5.20.0 to 5.22.1

docs/api/CacheStorage.md

32

docs/api/Errors.md

@@ -10,15 +10,21 @@ # Errors

| Error | Error Codes | Description |
| ------------------------------------ | ------------------------------------- | -------------------------------------------------- |
| `InvalidArgumentError` | `UND_ERR_INVALID_ARG` | passed an invalid argument. |
| `InvalidReturnValueError` | `UND_ERR_INVALID_RETURN_VALUE` | returned an invalid value. |
| `RequestAbortedError` | `UND_ERR_ABORTED` | the request has been aborted by the user |
| `ClientDestroyedError` | `UND_ERR_DESTROYED` | trying to use a destroyed client. |
| `ClientClosedError` | `UND_ERR_CLOSED` | trying to use a closed client. |
| `SocketError` | `UND_ERR_SOCKET` | there is an error with the socket. |
| `NotSupportedError` | `UND_ERR_NOT_SUPPORTED` | encountered unsupported functionality. |
| `RequestContentLengthMismatchError` | `UND_ERR_REQ_CONTENT_LENGTH_MISMATCH` | request body does not match content-length header |
| `ResponseContentLengthMismatchError` | `UND_ERR_RES_CONTENT_LENGTH_MISMATCH` | response body does not match content-length header |
| `InformationalError` | `UND_ERR_INFO` | expected error with reason |
| `ResponseExceededMaxSizeError` | `UND_ERR_RES_EXCEEDED_MAX_SIZE` | response body exceed the max size allowed |
| Error | Error Codes | Description |
| ------------------------------------ | ------------------------------------- | ------------------------------------------------------------------------- |
| `UndiciError` | `UND_ERR` | all errors below are extended from `UndiciError`. |
| `ConnectTimeoutError` | `UND_ERR_CONNECT_TIMEOUT` | socket is destroyed due to connect timeout. |
| `HeadersTimeoutError` | `UND_ERR_HEADERS_TIMEOUT` | socket is destroyed due to headers timeout. |
| `HeadersOverflowError` | `UND_ERR_HEADERS_OVERFLOW` | socket is destroyed due to headers' max size being exceeded. |
| `BodyTimeoutError` | `UND_ERR_BODY_TIMEOUT` | socket is destroyed due to body timeout. |
| `ResponseStatusCodeError` | `UND_ERR_RESPONSE_STATUS_CODE` | an error is thrown when `throwOnError` is `true` for status codes >= 400. |
| `InvalidArgumentError` | `UND_ERR_INVALID_ARG` | passed an invalid argument. |
| `InvalidReturnValueError` | `UND_ERR_INVALID_RETURN_VALUE` | returned an invalid value. |
| `RequestAbortedError` | `UND_ERR_ABORTED` | the request has been aborted by the user |
| `ClientDestroyedError` | `UND_ERR_DESTROYED` | trying to use a destroyed client. |
| `ClientClosedError` | `UND_ERR_CLOSED` | trying to use a closed client. |
| `SocketError` | `UND_ERR_SOCKET` | there is an error with the socket. |
| `NotSupportedError` | `UND_ERR_NOT_SUPPORTED` | encountered unsupported functionality. |
| `RequestContentLengthMismatchError` | `UND_ERR_REQ_CONTENT_LENGTH_MISMATCH` | request body does not match content-length header |
| `ResponseContentLengthMismatchError` | `UND_ERR_RES_CONTENT_LENGTH_MISMATCH` | response body does not match content-length header |
| `InformationalError` | `UND_ERR_INFO` | expected error with reason |
| `ResponseExceededMaxSizeError` | `UND_ERR_RES_EXCEEDED_MAX_SIZE` | response body exceed the max size allowed |

@@ -25,0 +31,0 @@ ### `SocketError`

@@ -11,2 +11,4 @@ # Fetch

In Node versions v18.13.0 and above and v19.2.0 and above, undici will default to using Node's [File](https://nodejs.org/api/buffer.html#class-file) class. In versions where it's not available, it will default to the undici one.
## FormData

@@ -13,0 +15,0 @@

@@ -22,2 +22,3 @@ # Class: ProxyAgent

* **auth** `string` (**deprecated**) - Use token.
* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`

@@ -87,3 +88,4 @@ Examples:

uri: 'my.proxy.server',
token: 'Bearer xxxx'
// token: 'Bearer xxxx'
token: `Basic ${Buffer.from('username:password').toString('base64')}`
});

@@ -90,0 +92,0 @@ setGlobalDispatcher(proxyAgent);

# Class: WebSocket
> ⚠️ Warning: the WebSocket API is experimental and has known bugs.
> ⚠️ Warning: the WebSocket API is experimental.
Extends: [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
The WebSocket object provides a way to manage a WebSocket connection to a server, allowing bidirectional communication. The API follows the [WebSocket spec](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket).
The WebSocket object provides a way to manage a WebSocket connection to a server, allowing bidirectional communication. The API follows the [WebSocket spec](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455).

@@ -14,4 +14,27 @@ ## `new WebSocket(url[, protocol])`

* **url** `URL | string` - The url's protocol *must* be `ws` or `wss`.
* **protocol** `string | string[]` (optional) - Subprotocol(s) to request the server use.
* **protocol** `string | string[] | WebSocketInit` (optional) - Subprotocol(s) to request the server use, or a [`Dispatcher`](./Dispatcher.md).
### Example:
This example will not work in browsers or other platforms that don't allow passing an object.
```mjs
import { WebSocket, ProxyAgent } from 'undici'
const proxyAgent = new ProxyAgent('my.proxy.server')
const ws = new WebSocket('wss://echo.websocket.events', {
dispatcher: proxyAgent,
protocols: ['echo', 'chat']
})
```
If you do not need a custom Dispatcher, it's recommended to use the following pattern:
```mjs
import { WebSocket } from 'undici'
const ws = new WebSocket('wss://echo.websocket.events', ['echo', 'chat'])
```
## Read More

@@ -18,0 +41,0 @@

@@ -27,2 +27,3 @@ import Dispatcher from'./types/dispatcher'

export * from './types/content-type'
export * from './types/cache'
export { Interceptable } from './types/mock-interceptor'

@@ -56,2 +57,3 @@

var fetch: typeof import('./types/fetch').fetch;
var caches: typeof import('./types/cache').caches;
}

@@ -124,2 +124,9 @@ 'use strict'

module.exports.getGlobalOrigin = getGlobalOrigin
const { CacheStorage } = require('./lib/cache/cachestorage')
const { kConstruct } = require('./lib/cache/symbols')
// Cache & CacheStorage are tightly coupled with fetch. Even if it may run
// in an older version of Node, it doesn't have any use without fetch.
module.exports.caches = new CacheStorage(kConstruct)
}

@@ -126,0 +133,0 @@

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

InvalidArgumentError,
RequestAbortedError,
ResponseStatusCodeError
RequestAbortedError
} = require('../core/errors')
const util = require('../core/util')
const { getResolveErrorBodyCallback } = require('./util')
const { AsyncResource } = require('async_hooks')

@@ -20,3 +20,3 @@ const { addSignal, removeSignal } = require('./abort-signal')

const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts
const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError, highWaterMark } = opts

@@ -28,2 +28,6 @@ try {

if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) {
throw new InvalidArgumentError('invalid highWaterMark')
}
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {

@@ -59,2 +63,3 @@ throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')

this.throwOnError = throwOnError
this.highWaterMark = highWaterMark

@@ -80,7 +85,8 @@ if (util.isStream(body)) {

onHeaders (statusCode, rawHeaders, resume, statusMessage) {
const { callback, opaque, abort, context } = this
const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this
const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
if (statusCode < 200) {
if (this.onInfo) {
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
this.onInfo({ statusCode, headers })

@@ -91,9 +97,8 @@ }

const parsedHeaders = util.parseHeaders(rawHeaders)
const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
const contentType = parsedHeaders['content-type']
const body = new Readable(resume, abort, contentType)
const body = new Readable({ resume, abort, contentType, highWaterMark })
this.callback = null
this.res = body
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)

@@ -105,13 +110,12 @@ if (callback !== null) {

)
return
} else {
this.runInAsyncScope(callback, null, null, {
statusCode,
headers,
trailers: this.trailers,
opaque,
body,
context
})
}
this.runInAsyncScope(callback, null, null, {
statusCode,
headers,
trailers: this.trailers,
opaque,
body,
context
})
}

@@ -163,29 +167,2 @@ }

async function getResolveErrorBodyCallback ({ callback, body, contentType, statusCode, statusMessage, headers }) {
if (statusCode === 204 || !contentType) {
body.dump()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
return
}
try {
if (contentType.startsWith('application/json')) {
const payload = await body.json()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
}
if (contentType.startsWith('text/')) {
const payload = await body.text()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
}
} catch (err) {
// Process in a fallback if error
}
body.dump()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
}
function request (opts, callback) {

@@ -192,0 +169,0 @@ if (callback === undefined) {

'use strict'
const { finished } = require('stream')
const { finished, PassThrough } = require('stream')
const {

@@ -10,2 +10,3 @@ InvalidArgumentError,

const util = require('../core/util')
const { getResolveErrorBodyCallback } = require('./util')
const { AsyncResource } = require('async_hooks')

@@ -20,3 +21,3 @@ const { addSignal, removeSignal } = require('./abort-signal')

const { signal, method, opaque, body, onInfo, responseHeaders } = opts
const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts

@@ -62,2 +63,3 @@ try {

this.onInfo = onInfo || null
this.throwOnError = throwOnError || false

@@ -82,8 +84,9 @@ if (util.isStream(body)) {

onHeaders (statusCode, rawHeaders, resume) {
const { factory, opaque, context } = this
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
const { factory, opaque, context, callback, responseHeaders } = this
const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
if (statusCode < 200) {
if (this.onInfo) {
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
this.onInfo({ statusCode, headers })

@@ -95,37 +98,51 @@ }

this.factory = null
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
const res = this.runInAsyncScope(factory, null, {
statusCode,
headers,
opaque,
context
})
if (
!res ||
typeof res.write !== 'function' ||
typeof res.end !== 'function' ||
typeof res.on !== 'function'
) {
throw new InvalidReturnValueError('expected Writable')
}
let res
res.on('drain', resume)
// TODO: Avoid finished. It registers an unnecessary amount of listeners.
finished(res, { readable: false }, (err) => {
const { callback, res, opaque, trailers, abort } = this
if (this.throwOnError && statusCode >= 400) {
const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
const contentType = parsedHeaders['content-type']
res = new PassThrough()
this.res = null
if (err || !res.readable) {
util.destroy(res, err)
}
this.callback = null
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
this.runInAsyncScope(getResolveErrorBodyCallback, null,
{ callback, body: res, contentType, statusCode, statusMessage, headers }
)
} else {
res = this.runInAsyncScope(factory, null, {
statusCode,
headers,
opaque,
context
})
if (err) {
abort()
if (
!res ||
typeof res.write !== 'function' ||
typeof res.end !== 'function' ||
typeof res.on !== 'function'
) {
throw new InvalidReturnValueError('expected Writable')
}
})
// TODO: Avoid finished. It registers an unnecessary amount of listeners.
finished(res, { readable: false }, (err) => {
const { callback, res, opaque, trailers, abort } = this
this.res = null
if (err || !res.readable) {
util.destroy(res, err)
}
this.callback = null
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
if (err) {
abort()
}
})
}
res.on('drain', resume)
this.res = res

@@ -132,0 +149,0 @@

@@ -7,3 +7,3 @@ // Ported from https://github.com/nodejs/undici/pull/907

const { Readable } = require('stream')
const { RequestAbortedError, NotSupportedError } = require('../core/errors')
const { RequestAbortedError, NotSupportedError, InvalidArgumentError } = require('../core/errors')
const util = require('../core/util')

@@ -21,7 +21,12 @@ const { ReadableStreamFrom, toUSVString } = require('../core/util')

module.exports = class BodyReadable extends Readable {
constructor (resume, abort, contentType = '') {
constructor ({
resume,
abort,
contentType = '',
highWaterMark = 64 * 1024 // Same as nodejs fs streams.
}) {
super({
autoDestroy: true,
read: resume,
highWaterMark: 64 * 1024 // Same as nodejs fs streams.
highWaterMark
})

@@ -151,4 +156,16 @@

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)

@@ -160,3 +177,7 @@ if (limit < 0) {

} catch {
// Do nothing...
util.throwIfAborted(signal)
} finally {
if (signal) {
signal.removeEventListener('abort', abortFn)
}
}

@@ -163,0 +184,0 @@ }

@@ -0,1 +1,3 @@

// @ts-check
'use strict'

@@ -22,3 +24,4 @@

HTTPParserError,
ResponseExceededMaxSizeError
ResponseExceededMaxSizeError,
ClientDestroyedError
} = require('./core/errors')

@@ -89,3 +92,11 @@ const buildConnector = require('./core/connect')

/**
* @type {import('../types/client').default}
*/
class Client extends DispatcherBase {
/**
*
* @param {string|URL} url
* @param {import('../types/client').Client.Options} options
*/
constructor (url, {

@@ -315,3 +326,3 @@ interceptors,

if (!this[kSize]) {
this.destroy(resolve)
resolve(null)
} else {

@@ -333,2 +344,3 @@ this[kClosedResolve] = resolve

if (this[kClosedResolve]) {
// TODO (fix): Should we error here with ClientDestroyedError?
this[kClosedResolve]()

@@ -356,7 +368,7 @@ this[kClosedResolve] = null

async function lazyllhttp () {
const llhttpWasmData = process.env.JEST_WORKER_ID ? require('./llhttp/llhttp.wasm.js') : undefined
const llhttpWasmData = process.env.JEST_WORKER_ID ? require('./llhttp/llhttp-wasm.js') : undefined
let mod
try {
mod = await WebAssembly.compile(Buffer.from(require('./llhttp/llhttp_simd.wasm.js'), 'base64'))
mod = await WebAssembly.compile(Buffer.from(require('./llhttp/llhttp_simd-wasm.js'), 'base64'))
} catch (e) {

@@ -369,3 +381,3 @@ /* istanbul ignore next */

// got me to remove that check to avoid breaking Node 12.
mod = await WebAssembly.compile(Buffer.from(llhttpWasmData || require('./llhttp/llhttp.wasm.js'), 'base64'))
mod = await WebAssembly.compile(Buffer.from(llhttpWasmData || require('./llhttp/llhttp-wasm.js'), 'base64'))
}

@@ -566,3 +578,6 @@

const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
message = Buffer.from(llhttp.memory.buffer, ptr, len).toString()
message =
'Response does not match the HTTP/1.1 protocol (' +
Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
')'
}

@@ -1082,2 +1097,7 @@ throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))

if (client.destroyed) {
util.destroy(socket.on('error', () => {}), new ClientDestroyedError())
return
}
if (!llhttpInstance) {

@@ -1125,2 +1145,6 @@ llhttpInstance = await llhttpPromise

} catch (err) {
if (client.destroyed) {
return
}
client[kConnecting] = false

@@ -1188,4 +1212,5 @@

if (client.closed && !client[kSize]) {
client.destroy()
if (client[kClosedResolve] && !client[kSize]) {
client[kClosedResolve]()
client[kClosedResolve] = null
return

@@ -1442,6 +1467,6 @@ }

if (contentLength === 0) {
socket.write(`${header}content-length: 0\r\n\r\n`, 'ascii')
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`, 'ascii')
socket.write(`${header}\r\n`, 'latin1')
}

@@ -1453,3 +1478,3 @@ request.onRequestSent()

socket.cork()
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'ascii')
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
socket.write(body)

@@ -1487,5 +1512,7 @@ socket.uncork()

const onData = function (chunk) {
if (finished) {
return
}
try {
assert(!finished)
if (!writer.write(chunk) && this.pause) {

@@ -1499,3 +1526,5 @@ this.pause()

const onDrain = function () {
assert(!finished)
if (finished) {
return
}

@@ -1571,3 +1600,3 @@ if (body.resume) {

socket.cork()
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'ascii')
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
socket.write(buffer)

@@ -1676,2 +1705,4 @@ socket.uncork()

socket.cork()
if (bytesWritten === 0) {

@@ -1683,5 +1714,5 @@ if (!expectsPayload) {

if (contentLength === null) {
socket.write(`${header}transfer-encoding: chunked\r\n`, 'ascii')
socket.write(`${header}transfer-encoding: chunked\r\n`, 'latin1')
} else {
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'ascii')
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
}

@@ -1691,3 +1722,3 @@ }

if (contentLength === null) {
socket.write(`\r\n${len.toString(16)}\r\n`, 'ascii')
socket.write(`\r\n${len.toString(16)}\r\n`, 'latin1')
}

@@ -1699,2 +1730,4 @@

socket.uncork()
request.onBodySent(chunk)

@@ -1735,8 +1768,8 @@

socket.write(`${header}content-length: 0\r\n\r\n`, 'ascii')
socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1')
} else {
socket.write(`${header}\r\n`, 'ascii')
socket.write(`${header}\r\n`, 'latin1')
}
} else if (contentLength === null) {
socket.write('\r\n0\r\n\r\n', 'ascii')
socket.write('\r\n0\r\n\r\n', 'latin1')
}

@@ -1743,0 +1776,0 @@

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

this.body = bodyStream.stream
this.contentLength = bodyStream.length
} else if (util.isBlobLike(body) && this.contentType == null && body.type) {

@@ -187,0 +188,0 @@ this.contentType = body.type

@@ -44,3 +44,3 @@ module.exports = {

kOnDestroyed: Symbol('destroy callbacks'),
kPipelining: Symbol('pipelinig'),
kPipelining: Symbol('pipelining'),
kSocket: Symbol('socket'),

@@ -47,0 +47,0 @@ kHostHeader: Symbol('host header'),

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

function isStream (obj) {
return obj && typeof obj.pipe === 'function'
return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
}

@@ -50,30 +50,36 @@

url = new URL(url)
if (!/^https?:/.test(url.origin || url.protocol)) {
throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
}
return url
}
if (!url || typeof url !== 'object') {
throw new InvalidArgumentError('invalid url')
throw new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.')
}
if (url.port != null && url.port !== '' && !Number.isFinite(parseInt(url.port))) {
throw new InvalidArgumentError('invalid port')
throw new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.')
}
if (url.path != null && typeof url.path !== 'string') {
throw new InvalidArgumentError('invalid path')
throw new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.')
}
if (url.pathname != null && typeof url.pathname !== 'string') {
throw new InvalidArgumentError('invalid pathname')
throw new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.')
}
if (url.hostname != null && typeof url.hostname !== 'string') {
throw new InvalidArgumentError('invalid hostname')
throw new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.')
}
if (url.origin != null && typeof url.origin !== 'string') {
throw new InvalidArgumentError('invalid origin')
throw new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.')
}
if (!/^https?:/.test(url.origin || url.protocol)) {
throw new InvalidArgumentError('invalid protocol')
throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
}

@@ -221,6 +227,2 @@

const encoding = key.length === 19 && key === 'content-disposition'
? 'latin1'
: 'utf8'
if (!val) {

@@ -230,3 +232,3 @@ if (Array.isArray(headers[i + 1])) {

} else {
obj[key] = headers[i + 1].toString(encoding)
obj[key] = headers[i + 1].toString('utf8')
}

@@ -238,5 +240,11 @@ } else {

}
val.push(headers[i + 1].toString(encoding))
val.push(headers[i + 1].toString('utf8'))
}
}
// See https://github.com/nodejs/node/pull/46528
if ('content-length' in obj && 'content-disposition' in obj) {
obj['content-disposition'] = Buffer.from(obj['content-disposition']).toString('latin1')
}
return obj

@@ -247,13 +255,24 @@ }

const ret = []
let hasContentLength = false
let contentDispositionIdx = -1
for (let n = 0; n < headers.length; n += 2) {
const key = headers[n + 0].toString()
const val = headers[n + 1].toString('utf8')
const encoding = key.length === 19 && key.toLowerCase() === 'content-disposition'
? 'latin1'
: 'utf8'
if (key.length === 14 && (key === 'content-length' || key.toLowerCase() === 'content-length')) {
ret.push(key, val)
hasContentLength = true
} else if (key.length === 19 && (key === 'content-disposition' || key.toLowerCase() === 'content-disposition')) {
contentDispositionIdx = ret.push(key, val) - 1
} else {
ret.push(key, val)
}
}
const val = headers[n + 1].toString(encoding)
// See https://github.com/nodejs/node/pull/46528
if (hasContentLength && contentDispositionIdx !== -1) {
ret[contentDispositionIdx] = Buffer.from(ret[contentDispositionIdx]).toString('latin1')
}
ret.push(key, val)
}
return ret

@@ -384,19 +403,45 @@ }

// 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 hasToWellFormed = !!String.prototype.toWellFormed
/**
* @param {string} val
*/
function toUSVString (val) {
if (hasToWellFormed) {
return `${val}`.toWellFormed()
} else if (nodeUtil.toUSVString) {
return nodeUtil.toUSVString(val)
}
return `${val}`
}
const kEnumerableProperty = Object.create(null)

@@ -411,3 +456,3 @@ kEnumerableProperty.enumerable = true

isReadable,
toUSVString: nodeUtil.toUSVString || ((val) => `${val}`),
toUSVString,
isReadableAborted,

@@ -434,2 +479,3 @@ isBlobLike,

buildURL,
throwIfAborted,
nodeMajor,

@@ -436,0 +482,0 @@ nodeMinor,

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

this[kDestroyed] = false
this[kOnDestroyed] = []
this[kOnDestroyed] = null
this[kClosed] = false

@@ -131,2 +131,3 @@ this[kOnClosed] = []

this[kDestroyed] = true
this[kOnDestroyed] = this[kOnDestroyed] || []
this[kOnDestroyed].push(callback)

@@ -172,3 +173,3 @@

if (this[kDestroyed]) {
if (this[kDestroyed] || this[kOnDestroyed]) {
throw new ClientDestroyedError()

@@ -175,0 +176,0 @@ }

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

length = 0
let hasUnknownSizeValue = false

@@ -142,3 +143,7 @@ for (const [name, value] of object) {

blobParts.push(chunk, value, rn)
length += chunk.byteLength + value.size + rn.byteLength
if (typeof value.size === 'number') {
length += chunk.byteLength + value.size + rn.byteLength
} else {
hasUnknownSizeValue = true
}
}

@@ -150,2 +155,5 @@ }

length += chunk.byteLength
if (hasUnknownSizeValue) {
length = null
}

@@ -152,0 +160,0 @@ // Set source to object.

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

// https://fetch.spec.whatwg.org/#request-body-header-name
const requestBodyHeader = [

@@ -56,3 +57,8 @@ 'content-encoding',

'content-location',
'content-type'
'content-type',
// See https://github.com/nodejs/undici/issues/2021
// 'Content-Length' is a forbidden header name, which is typically
// removed in the Headers implementation. However, undici doesn't
// filter out headers, so we add it here.
'content-length'
]

@@ -59,0 +65,0 @@

const assert = require('assert')
const { atob } = require('buffer')
const { format } = require('url')
const { isValidHTTPToken, isomorphicDecode } = require('./util')
const { isomorphicDecode } = require('./util')
const encoder = new TextEncoder()
// Regex
const HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+-.^_|~A-z0-9]+$/
/**
* @see https://mimesniff.spec.whatwg.org/#http-token-code-point
*/
const HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+-.^_|~A-Za-z0-9]+$/
const HTTP_WHITESPACE_REGEX = /(\u000A|\u000D|\u0009|\u0020)/ // eslint-disable-line
// https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
const HTTP_QUOTED_STRING_TOKENS = /^(\u0009|\x{0020}-\x{007E}|\x{0080}-\x{00FF})+$/ // eslint-disable-line
/**
* @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
*/
const HTTP_QUOTED_STRING_TOKENS = /[\u0009|\u0020-\u007E|\u0080-\u00FF]/ // eslint-disable-line

@@ -42,4 +45,2 @@ // https://fetch.spec.whatwg.org/#data-url-processor

// from mimeType.
// Note: This will only remove U+0020 SPACE code
// points, if any.
// Undici implementation note: we need to store the

@@ -50,3 +51,3 @@ // length because if the mimetype has spaces removed,

const mimeTypeLength = mimeType.length
mimeType = mimeType.replace(/^(\u0020)+|(\u0020)+$/g, '')
mimeType = removeASCIIWhitespace(mimeType, true, true)

@@ -123,3 +124,13 @@ // 7. If position is past the end of input, then

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)
}

@@ -230,3 +241,3 @@

// from input.
input = input.trim()
input = removeHTTPWhitespace(input, true, true)

@@ -272,3 +283,3 @@ // 2. Let position be a position variable for input,

// 8. Remove any trailing HTTP whitespace from subtype.
subtype = subtype.trimEnd()
subtype = removeHTTPWhitespace(subtype, false, true)

@@ -281,2 +292,5 @@ // 9. If subtype is the empty string or does not solely

const typeLowercase = type.toLowerCase()
const subtypeLowercase = subtype.toLowerCase()
// 10. Let mimeType be a new MIME type record whose type

@@ -287,8 +301,8 @@ // is type, in ASCII lowercase, and subtype is subtype,

const mimeType = {
type: type.toLowerCase(),
subtype: subtype.toLowerCase(),
type: typeLowercase,
subtype: subtypeLowercase,
/** @type {Map<string, string>} */
parameters: new Map(),
// https://mimesniff.spec.whatwg.org/#mime-type-essence
essence: `${type}/${subtype}`
essence: `${typeLowercase}/${subtypeLowercase}`
}

@@ -371,4 +385,3 @@

// 2. Remove any trailing HTTP whitespace from parameterValue.
// Note: it says "trailing" whitespace; leading is fine.
parameterValue = parameterValue.trimEnd()
parameterValue = removeHTTPWhitespace(parameterValue, false, true)

@@ -390,3 +403,3 @@ // 3. If parameterValue is the empty string, then continue.

HTTP_TOKEN_CODEPOINTS.test(parameterName) &&
!HTTP_QUOTED_STRING_TOKENS.test(parameterValue) &&
(parameterValue.length === 0 || HTTP_QUOTED_STRING_TOKENS.test(parameterValue)) &&
!mimeType.parameters.has(parameterName)

@@ -525,7 +538,7 @@ ) {

assert(mimeType !== 'failure')
const { type, subtype, parameters } = mimeType
const { parameters, essence } = mimeType
// 1. Let serialization be the concatenation of mimeType’s
// type, U+002F (/), and mimeType’s subtype.
let serialization = `${type}/${subtype}`
let serialization = essence

@@ -545,3 +558,3 @@ // 2. For each name → value of mimeType’s parameters:

// points or value is the empty string, then:
if (!isValidHTTPToken(value)) {
if (!HTTP_TOKEN_CODEPOINTS.test(value)) {
// 1. Precede each occurence of U+0022 (") or

@@ -566,2 +579,55 @@ // U+005C (\) in value with U+005C (\).

/**
* @see https://fetch.spec.whatwg.org/#http-whitespace
* @param {string} char
*/
function isHTTPWhiteSpace (char) {
return char === '\r' || char === '\n' || char === '\t' || char === ' '
}
/**
* @see https://fetch.spec.whatwg.org/#http-whitespace
* @param {string} str
*/
function removeHTTPWhitespace (str, leading = true, trailing = true) {
let lead = 0
let trail = str.length - 1
if (leading) {
for (; lead < str.length && isHTTPWhiteSpace(str[lead]); lead++);
}
if (trailing) {
for (; trail > 0 && isHTTPWhiteSpace(str[trail]); trail--);
}
return str.slice(lead, trail + 1)
}
/**
* @see https://infra.spec.whatwg.org/#ascii-whitespace
* @param {string} char
*/
function isASCIIWhitespace (char) {
return char === '\r' || char === '\n' || char === '\t' || char === '\f' || char === ' '
}
/**
* @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
*/
function removeASCIIWhitespace (str, leading = true, trailing = true) {
let lead = 0
let trail = str.length - 1
if (leading) {
for (; lead < str.length && isASCIIWhitespace(str[lead]); lead++);
}
if (trailing) {
for (; trail > 0 && isASCIIWhitespace(str[trail]); trail--);
}
return str.slice(lead, trail + 1)
}
module.exports = {

@@ -568,0 +634,0 @@ dataURLProcessor,

@@ -64,10 +64,3 @@ 'use strict'

// is name from this’s entry list.
const next = []
for (const entry of this[kState]) {
if (entry.name !== name) {
next.push(entry)
}
}
this[kState] = next
this[kState] = this[kState].filter(entry => entry.name !== name)
}

@@ -74,0 +67,0 @@

@@ -6,3 +6,3 @@ // https://github.com/Ethan-Arrowood/undici-fetch

const { kHeadersList } = require('../core/symbols')
const { kGuard, kHeadersCaseInsensitive } = require('./symbols')
const { kGuard } = require('./symbols')
const { kEnumerableProperty } = require('../core/util')

@@ -99,2 +99,3 @@ const {

this[kHeadersSortedMap] = null
this.cookies = null
}

@@ -177,11 +178,12 @@

get [kHeadersCaseInsensitive] () {
/** @type {string[]} */
const flatList = []
get entries () {
const headers = {}
for (const { name, value } of this[kHeadersMap].values()) {
flatList.push(name, value)
if (this[kHeadersMap].size) {
for (const { name, value } of this[kHeadersMap].values()) {
headers[name] = value
}
}
return flatList
return headers
}

@@ -521,2 +523,3 @@ }

set: kEnumerableProperty,
getSetCookie: kEnumerableProperty,
keys: kEnumerableProperty,

@@ -523,0 +526,0 @@ values: kEnumerableProperty,

@@ -12,3 +12,4 @@ /* globals AbortController */

sameOrigin,
normalizeMethod
normalizeMethod,
makePolicyContainer
} = require('./util')

@@ -32,3 +33,3 @@ const {

const assert = require('assert')
const { setMaxListeners, getEventListeners, defaultMaxListeners } = require('events')
const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('events')

@@ -38,2 +39,3 @@ let TransformStream = globalThis.TransformStream

const kInit = Symbol('init')
const kAbortController = Symbol('abortController')

@@ -57,6 +59,10 @@ const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {

// 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()
}

@@ -130,3 +136,3 @@ }

// 10. If init["window"] exists and is non-null, then throw a TypeError.
if (init.window !== undefined && init.window != null) {
if (init.window != null) {
throw new TypeError(`'window' option '${window}' must be null`)

@@ -136,3 +142,3 @@ }

// 11. If init["window"] exists, then set window to "no-window".
if (init.window !== undefined) {
if ('window' in init) {
window = 'no-window'

@@ -358,13 +364,30 @@ }

} else {
// Keep a strong ref to ac while request object
// is alive. This is needed to prevent AbortController
// from being prematurely garbage collected.
// See, https://github.com/nodejs/undici/issues/1926.
this[kAbortController] = ac
const acRef = new WeakRef(ac)
const abort = function () {
acRef.deref()?.abort(this.reason)
const ac = acRef.deref()
if (ac !== undefined) {
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 the max amount of listeners is equal to the default, increase it
// This is only available in node >= v19.9.0
if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) {
setMaxListeners(100, signal)
} else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
setMaxListeners(100, signal)
}
} catch {}
signal.addEventListener('abort', abort, { once: true })
requestFinalizer.register(this, { signal, abort })
requestFinalizer.register(ac, { signal, abort })
}

@@ -429,3 +452,3 @@ }

if (
((init.body !== undefined && init.body != null) || inputBody != null) &&
(init.body != null || inputBody != null) &&
(request.method === 'GET' || request.method === 'HEAD')

@@ -440,3 +463,3 @@ ) {

// 36. If init["body"] exists and is non-null, then:
if (init.body !== undefined && init.body != null) {
if (init.body != null) {
// 1. Let Content-Type be null.

@@ -443,0 +466,0 @@ // 2. Set initBody and Content-Type to the result of extracting

@@ -351,5 +351,3 @@ 'use strict'

? reason
: new Error(reason ? String(reason) : reason, {
cause: isError ? reason : undefined
}),
: new Error(reason ? String(reason) : reason),
aborted: reason && reason.name === 'AbortError'

@@ -473,3 +471,3 @@ })

if ('headers' in init && init.headers != null) {
fill(response[kState].headersList, init.headers)
fill(response[kHeaders], init.headers)
}

@@ -576,3 +574,4 @@

filterResponse,
Response
Response,
cloneResponse
}

@@ -9,4 +9,3 @@ 'use strict'

kGuard: Symbol('guard'),
kRealm: Symbol('realm'),
kHeadersCaseInsensitive: Symbol('headers case insensitive')
kRealm: Symbol('realm')
}
'use strict'
const { redirectStatus, badPorts, referrerPolicy: referrerPolicyTokens } = require('./constants')
const { getGlobalOrigin } = require('./global')
const { performance } = require('perf_hooks')

@@ -39,5 +40,7 @@ const { isBlobLike, toUSVString, ReadableStreamFrom } = require('../core/util')

// 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))
}

@@ -65,3 +68,3 @@ // 4. If location is a URL whose fragment is null, then set location’s

// then return blocked.
if (/^https?:/.test(url.protocol) && badPorts.includes(url.port)) {
if (urlIsHttpHttpsScheme(url) && badPorts.includes(url.port)) {
return 'blocked'

@@ -272,3 +275,3 @@ }

if (serializedOrigin) {
request.headersList.append('Origin', serializedOrigin)
request.headersList.append('origin', serializedOrigin)
}

@@ -288,3 +291,3 @@

// If request’s origin is a tuple origin, its scheme is "https", and request’s current URL’s scheme is not "https", then set serializedOrigin to `null`.
if (/^https:/.test(request.origin) && !/^https:/.test(requestCurrentURL(request))) {
if (request.origin && urlHasHttpsScheme(request.origin) && !urlHasHttpsScheme(requestCurrentURL(request))) {
serializedOrigin = null

@@ -305,3 +308,3 @@ }

// 2. Append (`Origin`, serializedOrigin) to request’s header list.
request.headersList.append('Origin', serializedOrigin)
request.headersList.append('origin', serializedOrigin)
}

@@ -335,10 +338,13 @@ }

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
}
}

@@ -351,100 +357,72 @@

// 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 the request client
const environment = request.client
// 2. Let environment be request’s 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
// Note: node isn't a browser and doesn't implement document/iframes,
// so we bypass this step and replace it with our own.
// If document’s origin is an opaque origin, return no referrer.
if (origin == null || origin === 'null') return 'no-referrer'
const globalOrigin = getGlobalOrigin()
// 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'
}
if (!globalOrigin || globalOrigin.origin === 'null') {
return 'no-referrer'
}
referrerSource = new URL(environment.globalObject.location.href)
}
// 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

@@ -468,11 +446,38 @@ /**

}
}
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
}

@@ -644,3 +649,5 @@

// 1. If A and B are the same opaque origin, then return true.
// "opaque origin" is an internal value we cannot access, ignore.
if (A.origin === B.origin && A.origin === 'null') {
return true
}

@@ -952,2 +959,37 @@ // 2. If A and B are both tuple origins and their schemes,

/**
* @see https://fetch.spec.whatwg.org/#is-local
* @param {URL} url
*/
function urlIsLocal (url) {
assert('protocol' in url) // ensure it's a url object
const protocol = url.protocol
return protocol === 'about:' || protocol === 'blob:' || protocol === 'data:'
}
/**
* @param {string|URL} url
*/
function urlHasHttpsScheme (url) {
if (typeof url === 'string') {
return url.startsWith('https:')
}
return url.protocol === 'https:'
}
/**
* @see https://fetch.spec.whatwg.org/#http-scheme
* @param {URL} url
*/
function urlIsHttpHttpsScheme (url) {
assert('protocol' in url) // ensure it's a url object
const protocol = url.protocol
return protocol === 'http:' || protocol === 'https:'
}
/**
* Fetch supports node >= 16.8.0, but Object.hasOwn was added in v16.9.0.

@@ -996,3 +1038,7 @@ */

isomorphicEncode,
isomorphicDecode
isomorphicDecode,
urlIsLocal,
urlHasHttpsScheme,
urlIsHttpHttpsScheme,
readAllBytes
}

@@ -54,2 +54,9 @@ 'use strict'

webidl.illegalConstructor = function () {
throw webidl.errors.exception({
header: 'TypeError',
message: 'Illegal constructor'
})
}
// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values

@@ -56,0 +63,0 @@ webidl.util.Type = function (V) {

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

* @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.

@@ -10,0 +14,0 @@ // 2. If label is an ASCII case-insensitive match for any of the

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

const Agent = require('./agent')
const Client = require('./client')
const Pool = require('./pool')
const DispatcherBase = require('./dispatcher-base')

@@ -38,2 +38,6 @@ const { InvalidArgumentError, RequestAbortedError } = require('./core/errors')

function defaultFactory (origin, opts) {
return new Pool(origin, opts)
}
class ProxyAgent extends DispatcherBase {

@@ -56,2 +60,8 @@ constructor (opts) {

const { clientFactory = defaultFactory } = opts
if (typeof clientFactory !== 'function') {
throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')
}
this[kRequestTls] = opts.requestTls

@@ -75,3 +85,3 @@ this[kProxyTls] = opts.proxyTls

this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })
this[kClient] = new Client(resolvedUrl, { connect })
this[kClient] = clientFactory(resolvedUrl, { connect })
this[kAgent] = new Agent({

@@ -78,0 +88,0 @@ ...opts,

@@ -16,9 +16,11 @@ 'use strict'

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) {

@@ -57,5 +59,9 @@ fastTimers[idx] = fastTimers.pop()

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()

@@ -65,16 +71,14 @@ }

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
}

@@ -85,9 +89,13 @@ }

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)
}
}
}

@@ -8,5 +8,2 @@ 'use strict'

kReadyState,
kResponse,
kExtensions,
kProtocol,
kSentClose,

@@ -18,6 +15,7 @@ kByteParser,

const { CloseEvent } = require('./events')
const { ByteParser } = require('./receiver')
const { makeRequest } = require('../fetch/request')
const { fetching } = require('../fetch/index')
const { getGlobalDispatcher } = require('../..')
const { Headers } = require('../fetch/headers')
const { getGlobalDispatcher } = require('../global')
const { kHeadersList } = require('../core/symbols')

@@ -34,4 +32,6 @@ const channels = {}

* @param {import('./websocket').WebSocket} ws
* @param {(response: any) => void} onEstablish
* @param {Partial<import('../../types/websocket').WebSocketInit>} options
*/
function establishWebSocketConnection (url, protocols, ws) {
function establishWebSocketConnection (url, protocols, ws, onEstablish, options) {
// 1. Let requestURL be a copy of url, with its scheme set to "http", if url’s

@@ -57,2 +57,9 @@ // scheme is "ws", and to "https" otherwise.

// Note: undici extension, allow setting custom headers.
if (options.headers) {
const headersList = new Headers(options.headers)[kHeadersList]
request.headersList = headersList
}
// 3. Append (`Upgrade`, `websocket`) to request’s header list.

@@ -98,3 +105,3 @@ // 4. Append (`Connection`, `Upgrade`) to request’s header list.

useParallelQueue: true,
dispatcher: getGlobalDispatcher(),
dispatcher: options.dispatcher ?? getGlobalDispatcher(),
processResponse (response) {

@@ -181,12 +188,2 @@ // 1. If response is a network error or its status is not 101,

// 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)

@@ -196,3 +193,11 @@ response.socket.on('close', onSocketClose)

parser.on('drain', onParserDrain)
if (channels.open.hasSubscribers) {
channels.open.publish({
address: response.socket.address(),
protocol: secProtocol,
extensions: secExtension
})
}
onEstablish(response)
}

@@ -205,42 +210,2 @@ })

/**
* @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

@@ -254,6 +219,2 @@ */

function onParserDrain () {
this.ws[kResponse].socket.resume()
}
/**

@@ -260,0 +221,0 @@ * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol

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

if (payloadLength === 126) {
new DataView(buffer.buffer).setUint16(2, bodyLength)
buffer.writeUInt16BE(bodyLength, 2)
} else if (payloadLength === 127) {

@@ -49,0 +49,0 @@ // Clear extended payload length

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

kResponse: Symbol('response'),
kExtensions: Symbol('extensions'),
kProtocol: Symbol('protocol'),
kBinaryType: Symbol('binary type'),
kClosingFrame: Symbol('closing frame'),
kSentClose: Symbol('sent close'),

@@ -14,0 +11,0 @@ kReceivedClose: Symbol('received close'),

@@ -11,12 +11,13 @@ 'use strict'

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 { getGlobalDispatcher } = require('../global')
const { types } = require('util')

@@ -36,2 +37,4 @@

#bufferedAmount = 0
#protocol = ''
#extensions = ''

@@ -54,4 +57,6 @@ /**

const options = webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'](protocols)
url = webidl.converters.USVString(url)
protocols = webidl.converters['DOMString or sequence<DOMString>'](protocols)
protocols = options.protocols

@@ -110,3 +115,9 @@ // 1. Let urlRecord be the result of applying the URL parser to url.

// and client.
this[kController] = establishWebSocketConnection(urlRecord, protocols, this)
this[kController] = establishWebSocketConnection(
urlRecord,
protocols,
this,
(response) => this.#onConnectionEstablished(response),
options
)

@@ -119,6 +130,4 @@ // Each WebSocket object has an associated ready state, which is a

// The extensions attribute must initially return the empty string.
this[kExtensions] = ''
// The protocol attribute must initially return the empty string.
this[kProtocol] = ''

@@ -376,3 +385,3 @@ // Each WebSocket object has an associated binary type, which is a

return this[kExtensions]
return this.#extensions
}

@@ -383,3 +392,3 @@

return this[kProtocol]
return this.#protocol
}

@@ -486,2 +495,43 @@

}
/**
* @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)
}
}

@@ -542,2 +592,32 @@

// This implements the propsal made in https://github.com/whatwg/websockets/issues/42
webidl.converters.WebSocketInit = webidl.dictionaryConverter([
{
key: 'protocols',
converter: webidl.converters['DOMString or sequence<DOMString>'],
get defaultValue () {
return []
}
},
{
key: 'dispatcher',
converter: (V) => V,
get defaultValue () {
return getGlobalDispatcher()
}
},
{
key: 'headers',
converter: webidl.nullableConverter(webidl.converters.HeadersInit)
}
])
webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'] = function (V) {
if (webidl.util.Type(V) === 'Object' && !(Symbol.iterator in V)) {
return webidl.converters.WebSocketInit(V)
}
return { protocols: webidl.converters['DOMString or sequence<DOMString>'](V) }
}
webidl.converters.WebSocketSendData = function (V) {

@@ -544,0 +624,0 @@ if (webidl.util.Type(V) === 'Object') {

{
"name": "undici",
"version": "5.20.0",
"version": "5.22.1",
"description": "An HTTP/1.1 client, written from scratch for Node.js",

@@ -45,14 +45,14 @@ "homepage": "https://undici.nodejs.org",

"build:node": "npx esbuild@0.14.38 index-fetch.js --bundle --platform=node --outfile=undici-fetch.js",
"prebuild:wasm": "docker build -t llhttp_wasm_builder -f build/Dockerfile .",
"prebuild:wasm": "node build/wasm.js --prebuild",
"build:wasm": "node build/wasm.js --docker",
"lint": "standard | snazzy",
"lint:fix": "standard --fix | snazzy",
"test": "npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:wpt && npm run test:websocket && npm run test:jest && tsd",
"test": "npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:wpt && npm run test:websocket && npm run test:jest && npm run test:typescript",
"test:cookies": "node scripts/verifyVersion 16 || tap test/cookie/*.js",
"test:node-fetch": "node scripts/verifyVersion.js 16 || mocha test/node-fetch",
"test:fetch": "node scripts/verifyVersion.js 16 || (npm run build:node && tap test/fetch/*.js && tap test/webidl/*.js)",
"test:node-fetch": "node scripts/verifyVersion.js 16 || mocha --exit test/node-fetch",
"test:fetch": "node scripts/verifyVersion.js 16 || (npm run build:node && tap --expose-gc test/fetch/*.js && tap test/webidl/*.js)",
"test:jest": "node scripts/verifyVersion.js 14 || jest",
"test:tap": "tap test/*.js test/diagnostics-channel/*.js",
"test:tdd": "tap test/*.js test/diagnostics-channel/*.js -w",
"test:typescript": "tsd && tsc test/imports/undici-import.ts",
"test:typescript": "node scripts/verifyVersion.js 14 || tsd && tsc --skipLibCheck test/imports/undici-import.ts",
"test:websocket": "node scripts/verifyVersion.js 18 || tap test/websocket/*.js",

@@ -65,3 +65,3 @@ "test:wpt": "node scripts/verifyVersion 18 || (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 --no-warnings test/wpt/start-websockets.mjs)",

"prebench:run": "node benchmarks/wait.js",
"bench:run": "CONNECTIONS=1 node --experimental-wasm-simd benchmarks/benchmark.js; CONNECTIONS=50 node --experimental-wasm-simd benchmarks/benchmark.js",
"bench:run": "CONNECTIONS=1 node benchmarks/benchmark.js; CONNECTIONS=50 node benchmarks/benchmark.js",
"serve:website": "docsify serve .",

@@ -80,3 +80,3 @@ "prepare": "husky install",

"chai-string": "^1.5.0",
"concurrently": "^7.1.0",
"concurrently": "^8.0.1",
"cronometro": "^1.0.5",

@@ -92,2 +92,3 @@ "delay": "^5.0.0",

"jest": "^29.0.2",
"jsdom": "^21.1.0",
"jsfuzz": "^1.0.15",

@@ -104,9 +105,9 @@ "mocha": "^10.0.0",

"tap": "^16.1.0",
"tsd": "^0.25.0",
"typescript": "^4.9.5",
"wait-on": "^6.0.0",
"tsd": "^0.28.1",
"typescript": "^5.0.2",
"wait-on": "^7.0.1",
"ws": "^8.11.0"
},
"engines": {
"node": ">=12.18"
"node": ">=14.0"
},

@@ -120,4 +121,3 @@ "standard": {

"lib/llhttp/utils.js",
"test/wpt/tests",
"test/wpt/runner/resources"
"test/wpt/tests"
]

@@ -124,0 +124,0 @@ },

@@ -410,3 +410,3 @@ # undici

### Network address family autoselection.
### Network address family autoselection.

@@ -413,0 +413,0 @@ If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)

@@ -8,6 +8,6 @@ import Pool from './pool'

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>;

@@ -14,0 +14,0 @@

@@ -7,6 +7,6 @@ import { URL } from 'url'

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);

@@ -21,36 +21,58 @@ /** Property to get and set the pipelining factor. */

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 {

@@ -66,4 +88,4 @@ localAddress?: string

}
}
}
export default Client;

@@ -30,11 +30,5 @@ import { TLSSocket, ConnectionOptions } from 'tls'

export type connector = connectorAsync | connectorSync
interface connectorSync {
(options: buildConnector.Options): Socket | TLSSocket
}
interface connectorAsync {
export interface connector {
(options: buildConnector.Options, callback: buildConnector.Callback): void
}
}

@@ -145,2 +145,4 @@ import { URL } from 'url'

responseHeader?: 'raw' | null;
/** Default: `64 KiB` */
highWaterMark?: number;
}

@@ -147,0 +149,0 @@ export interface PipelineOptions extends RequestOptions {

@@ -7,3 +7,6 @@ import { IncomingHttpHeaders } from "./header";

declare namespace Errors {
export class UndiciError extends Error { }
export class UndiciError extends Error {
name: string;
code: string;
}

@@ -35,2 +38,8 @@ /** Connect timeout error. */

export class ResponseStatusCodeError extends UndiciError {
constructor (
message?: string,
statusCode?: number,
headers?: IncomingHttpHeaders | string[] | null,
body?: null | Record<string, any> | string
);
name: 'ResponseStatusCodeError';

@@ -37,0 +46,0 @@ code: 'UND_ERR_RESPONSE_STATUS_CODE';

import Agent from './agent'
import buildConnector from './connector';
import Client from './client'
import Dispatcher from './dispatcher'
import { IncomingHttpHeaders } from './header'
import Pool from './pool'

@@ -26,3 +28,4 @@ export default ProxyAgent

proxyTls?: buildConnector.BuildOptions;
clientFactory?(origin: URL, opts: object): Dispatcher;
}
}

@@ -173,2 +173,4 @@ // These types are not exported, and are only used internally

illegalConstructor (): never
/**

@@ -175,0 +177,0 @@ * @see https://webidl.spec.whatwg.org/#es-to-record

/// <reference types="node" />
import type { Blob } from 'buffer'
import type { MessagePort } from 'worker_threads'
import {

@@ -11,2 +13,4 @@ EventTarget,

} from './patch'
import Dispatcher from './dispatcher'
import { HeadersInit } from './fetch'

@@ -69,3 +73,3 @@ export type BinaryType = 'blob' | 'arraybuffer'

prototype: WebSocket
new (url: string | URL, protocols?: string | string[]): WebSocket
new (url: string | URL, protocols?: string | string[] | WebSocketInit): WebSocket
readonly CLOSED: number

@@ -124,1 +128,7 @@ readonly CLOSING: number

}
interface WebSocketInit {
protocols?: string | string[],
dispatcher?: Dispatcher,
headers?: HeadersInit
}

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc