[d24f17c] | 1 | 'use strict'
|
---|
| 2 |
|
---|
| 3 | const assert = require('assert')
|
---|
| 4 | const { kDestroyed, kBodyUsed } = require('./symbols')
|
---|
| 5 | const { IncomingMessage } = require('http')
|
---|
| 6 | const stream = require('stream')
|
---|
| 7 | const net = require('net')
|
---|
| 8 | const { InvalidArgumentError } = require('./errors')
|
---|
| 9 | const { Blob } = require('buffer')
|
---|
| 10 | const nodeUtil = require('util')
|
---|
| 11 | const { stringify } = require('querystring')
|
---|
| 12 |
|
---|
| 13 | const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v))
|
---|
| 14 |
|
---|
| 15 | function nop () {}
|
---|
| 16 |
|
---|
| 17 | function isStream (obj) {
|
---|
| 18 | return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
|
---|
| 19 | }
|
---|
| 20 |
|
---|
| 21 | // based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
|
---|
| 22 | function isBlobLike (object) {
|
---|
| 23 | return (Blob && object instanceof Blob) || (
|
---|
| 24 | object &&
|
---|
| 25 | typeof object === 'object' &&
|
---|
| 26 | (typeof object.stream === 'function' ||
|
---|
| 27 | typeof object.arrayBuffer === 'function') &&
|
---|
| 28 | /^(Blob|File)$/.test(object[Symbol.toStringTag])
|
---|
| 29 | )
|
---|
| 30 | }
|
---|
| 31 |
|
---|
| 32 | function buildURL (url, queryParams) {
|
---|
| 33 | if (url.includes('?') || url.includes('#')) {
|
---|
| 34 | throw new Error('Query params cannot be passed when url already contains "?" or "#".')
|
---|
| 35 | }
|
---|
| 36 |
|
---|
| 37 | const stringified = stringify(queryParams)
|
---|
| 38 |
|
---|
| 39 | if (stringified) {
|
---|
| 40 | url += '?' + stringified
|
---|
| 41 | }
|
---|
| 42 |
|
---|
| 43 | return url
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 | function parseURL (url) {
|
---|
| 47 | if (typeof url === 'string') {
|
---|
| 48 | url = new URL(url)
|
---|
| 49 |
|
---|
| 50 | if (!/^https?:/.test(url.origin || url.protocol)) {
|
---|
| 51 | throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | return url
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | if (!url || typeof url !== 'object') {
|
---|
| 58 | throw new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.')
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 | if (!/^https?:/.test(url.origin || url.protocol)) {
|
---|
| 62 | throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | if (!(url instanceof URL)) {
|
---|
| 66 | if (url.port != null && url.port !== '' && !Number.isFinite(parseInt(url.port))) {
|
---|
| 67 | throw new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.')
|
---|
| 68 | }
|
---|
| 69 |
|
---|
| 70 | if (url.path != null && typeof url.path !== 'string') {
|
---|
| 71 | throw new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.')
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | if (url.pathname != null && typeof url.pathname !== 'string') {
|
---|
| 75 | throw new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.')
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | if (url.hostname != null && typeof url.hostname !== 'string') {
|
---|
| 79 | throw new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.')
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | if (url.origin != null && typeof url.origin !== 'string') {
|
---|
| 83 | throw new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.')
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | const port = url.port != null
|
---|
| 87 | ? url.port
|
---|
| 88 | : (url.protocol === 'https:' ? 443 : 80)
|
---|
| 89 | let origin = url.origin != null
|
---|
| 90 | ? url.origin
|
---|
| 91 | : `${url.protocol}//${url.hostname}:${port}`
|
---|
| 92 | let path = url.path != null
|
---|
| 93 | ? url.path
|
---|
| 94 | : `${url.pathname || ''}${url.search || ''}`
|
---|
| 95 |
|
---|
| 96 | if (origin.endsWith('/')) {
|
---|
| 97 | origin = origin.substring(0, origin.length - 1)
|
---|
| 98 | }
|
---|
| 99 |
|
---|
| 100 | if (path && !path.startsWith('/')) {
|
---|
| 101 | path = `/${path}`
|
---|
| 102 | }
|
---|
| 103 | // new URL(path, origin) is unsafe when `path` contains an absolute URL
|
---|
| 104 | // From https://developer.mozilla.org/en-US/docs/Web/API/URL/URL:
|
---|
| 105 | // If first parameter is a relative URL, second param is required, and will be used as the base URL.
|
---|
| 106 | // If first parameter is an absolute URL, a given second param will be ignored.
|
---|
| 107 | url = new URL(origin + path)
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | return url
|
---|
| 111 | }
|
---|
| 112 |
|
---|
| 113 | function parseOrigin (url) {
|
---|
| 114 | url = parseURL(url)
|
---|
| 115 |
|
---|
| 116 | if (url.pathname !== '/' || url.search || url.hash) {
|
---|
| 117 | throw new InvalidArgumentError('invalid url')
|
---|
| 118 | }
|
---|
| 119 |
|
---|
| 120 | return url
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | function getHostname (host) {
|
---|
| 124 | if (host[0] === '[') {
|
---|
| 125 | const idx = host.indexOf(']')
|
---|
| 126 |
|
---|
| 127 | assert(idx !== -1)
|
---|
| 128 | return host.substring(1, idx)
|
---|
| 129 | }
|
---|
| 130 |
|
---|
| 131 | const idx = host.indexOf(':')
|
---|
| 132 | if (idx === -1) return host
|
---|
| 133 |
|
---|
| 134 | return host.substring(0, idx)
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | // IP addresses are not valid server names per RFC6066
|
---|
| 138 | // > Currently, the only server names supported are DNS hostnames
|
---|
| 139 | function getServerName (host) {
|
---|
| 140 | if (!host) {
|
---|
| 141 | return null
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | assert.strictEqual(typeof host, 'string')
|
---|
| 145 |
|
---|
| 146 | const servername = getHostname(host)
|
---|
| 147 | if (net.isIP(servername)) {
|
---|
| 148 | return ''
|
---|
| 149 | }
|
---|
| 150 |
|
---|
| 151 | return servername
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | function deepClone (obj) {
|
---|
| 155 | return JSON.parse(JSON.stringify(obj))
|
---|
| 156 | }
|
---|
| 157 |
|
---|
| 158 | function isAsyncIterable (obj) {
|
---|
| 159 | return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function')
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | function isIterable (obj) {
|
---|
| 163 | return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function'))
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 | function bodyLength (body) {
|
---|
| 167 | if (body == null) {
|
---|
| 168 | return 0
|
---|
| 169 | } else if (isStream(body)) {
|
---|
| 170 | const state = body._readableState
|
---|
| 171 | return state && state.objectMode === false && state.ended === true && Number.isFinite(state.length)
|
---|
| 172 | ? state.length
|
---|
| 173 | : null
|
---|
| 174 | } else if (isBlobLike(body)) {
|
---|
| 175 | return body.size != null ? body.size : null
|
---|
| 176 | } else if (isBuffer(body)) {
|
---|
| 177 | return body.byteLength
|
---|
| 178 | }
|
---|
| 179 |
|
---|
| 180 | return null
|
---|
| 181 | }
|
---|
| 182 |
|
---|
| 183 | function isDestroyed (stream) {
|
---|
| 184 | return !stream || !!(stream.destroyed || stream[kDestroyed])
|
---|
| 185 | }
|
---|
| 186 |
|
---|
| 187 | function isReadableAborted (stream) {
|
---|
| 188 | const state = stream && stream._readableState
|
---|
| 189 | return isDestroyed(stream) && state && !state.endEmitted
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | function destroy (stream, err) {
|
---|
| 193 | if (stream == null || !isStream(stream) || isDestroyed(stream)) {
|
---|
| 194 | return
|
---|
| 195 | }
|
---|
| 196 |
|
---|
| 197 | if (typeof stream.destroy === 'function') {
|
---|
| 198 | if (Object.getPrototypeOf(stream).constructor === IncomingMessage) {
|
---|
| 199 | // See: https://github.com/nodejs/node/pull/38505/files
|
---|
| 200 | stream.socket = null
|
---|
| 201 | }
|
---|
| 202 |
|
---|
| 203 | stream.destroy(err)
|
---|
| 204 | } else if (err) {
|
---|
| 205 | process.nextTick((stream, err) => {
|
---|
| 206 | stream.emit('error', err)
|
---|
| 207 | }, stream, err)
|
---|
| 208 | }
|
---|
| 209 |
|
---|
| 210 | if (stream.destroyed !== true) {
|
---|
| 211 | stream[kDestroyed] = true
|
---|
| 212 | }
|
---|
| 213 | }
|
---|
| 214 |
|
---|
| 215 | const KEEPALIVE_TIMEOUT_EXPR = /timeout=(\d+)/
|
---|
| 216 | function parseKeepAliveTimeout (val) {
|
---|
| 217 | const m = val.toString().match(KEEPALIVE_TIMEOUT_EXPR)
|
---|
| 218 | return m ? parseInt(m[1], 10) * 1000 : null
|
---|
| 219 | }
|
---|
| 220 |
|
---|
| 221 | function parseHeaders (headers, obj = {}) {
|
---|
| 222 | // For H2 support
|
---|
| 223 | if (!Array.isArray(headers)) return headers
|
---|
| 224 |
|
---|
| 225 | for (let i = 0; i < headers.length; i += 2) {
|
---|
| 226 | const key = headers[i].toString().toLowerCase()
|
---|
| 227 | let val = obj[key]
|
---|
| 228 |
|
---|
| 229 | if (!val) {
|
---|
| 230 | if (Array.isArray(headers[i + 1])) {
|
---|
| 231 | obj[key] = headers[i + 1].map(x => x.toString('utf8'))
|
---|
| 232 | } else {
|
---|
| 233 | obj[key] = headers[i + 1].toString('utf8')
|
---|
| 234 | }
|
---|
| 235 | } else {
|
---|
| 236 | if (!Array.isArray(val)) {
|
---|
| 237 | val = [val]
|
---|
| 238 | obj[key] = val
|
---|
| 239 | }
|
---|
| 240 | val.push(headers[i + 1].toString('utf8'))
|
---|
| 241 | }
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 | // See https://github.com/nodejs/node/pull/46528
|
---|
| 245 | if ('content-length' in obj && 'content-disposition' in obj) {
|
---|
| 246 | obj['content-disposition'] = Buffer.from(obj['content-disposition']).toString('latin1')
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | return obj
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 | function parseRawHeaders (headers) {
|
---|
| 253 | const ret = []
|
---|
| 254 | let hasContentLength = false
|
---|
| 255 | let contentDispositionIdx = -1
|
---|
| 256 |
|
---|
| 257 | for (let n = 0; n < headers.length; n += 2) {
|
---|
| 258 | const key = headers[n + 0].toString()
|
---|
| 259 | const val = headers[n + 1].toString('utf8')
|
---|
| 260 |
|
---|
| 261 | if (key.length === 14 && (key === 'content-length' || key.toLowerCase() === 'content-length')) {
|
---|
| 262 | ret.push(key, val)
|
---|
| 263 | hasContentLength = true
|
---|
| 264 | } else if (key.length === 19 && (key === 'content-disposition' || key.toLowerCase() === 'content-disposition')) {
|
---|
| 265 | contentDispositionIdx = ret.push(key, val) - 1
|
---|
| 266 | } else {
|
---|
| 267 | ret.push(key, val)
|
---|
| 268 | }
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | // See https://github.com/nodejs/node/pull/46528
|
---|
| 272 | if (hasContentLength && contentDispositionIdx !== -1) {
|
---|
| 273 | ret[contentDispositionIdx] = Buffer.from(ret[contentDispositionIdx]).toString('latin1')
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | return ret
|
---|
| 277 | }
|
---|
| 278 |
|
---|
| 279 | function isBuffer (buffer) {
|
---|
| 280 | // See, https://github.com/mcollina/undici/pull/319
|
---|
| 281 | return buffer instanceof Uint8Array || Buffer.isBuffer(buffer)
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | function validateHandler (handler, method, upgrade) {
|
---|
| 285 | if (!handler || typeof handler !== 'object') {
|
---|
| 286 | throw new InvalidArgumentError('handler must be an object')
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | if (typeof handler.onConnect !== 'function') {
|
---|
| 290 | throw new InvalidArgumentError('invalid onConnect method')
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | if (typeof handler.onError !== 'function') {
|
---|
| 294 | throw new InvalidArgumentError('invalid onError method')
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) {
|
---|
| 298 | throw new InvalidArgumentError('invalid onBodySent method')
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 | if (upgrade || method === 'CONNECT') {
|
---|
| 302 | if (typeof handler.onUpgrade !== 'function') {
|
---|
| 303 | throw new InvalidArgumentError('invalid onUpgrade method')
|
---|
| 304 | }
|
---|
| 305 | } else {
|
---|
| 306 | if (typeof handler.onHeaders !== 'function') {
|
---|
| 307 | throw new InvalidArgumentError('invalid onHeaders method')
|
---|
| 308 | }
|
---|
| 309 |
|
---|
| 310 | if (typeof handler.onData !== 'function') {
|
---|
| 311 | throw new InvalidArgumentError('invalid onData method')
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | if (typeof handler.onComplete !== 'function') {
|
---|
| 315 | throw new InvalidArgumentError('invalid onComplete method')
|
---|
| 316 | }
|
---|
| 317 | }
|
---|
| 318 | }
|
---|
| 319 |
|
---|
| 320 | // A body is disturbed if it has been read from and it cannot
|
---|
| 321 | // be re-used without losing state or data.
|
---|
| 322 | function isDisturbed (body) {
|
---|
| 323 | return !!(body && (
|
---|
| 324 | stream.isDisturbed
|
---|
| 325 | ? stream.isDisturbed(body) || body[kBodyUsed] // TODO (fix): Why is body[kBodyUsed] needed?
|
---|
| 326 | : body[kBodyUsed] ||
|
---|
| 327 | body.readableDidRead ||
|
---|
| 328 | (body._readableState && body._readableState.dataEmitted) ||
|
---|
| 329 | isReadableAborted(body)
|
---|
| 330 | ))
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | function isErrored (body) {
|
---|
| 334 | return !!(body && (
|
---|
| 335 | stream.isErrored
|
---|
| 336 | ? stream.isErrored(body)
|
---|
| 337 | : /state: 'errored'/.test(nodeUtil.inspect(body)
|
---|
| 338 | )))
|
---|
| 339 | }
|
---|
| 340 |
|
---|
| 341 | function isReadable (body) {
|
---|
| 342 | return !!(body && (
|
---|
| 343 | stream.isReadable
|
---|
| 344 | ? stream.isReadable(body)
|
---|
| 345 | : /state: 'readable'/.test(nodeUtil.inspect(body)
|
---|
| 346 | )))
|
---|
| 347 | }
|
---|
| 348 |
|
---|
| 349 | function getSocketInfo (socket) {
|
---|
| 350 | return {
|
---|
| 351 | localAddress: socket.localAddress,
|
---|
| 352 | localPort: socket.localPort,
|
---|
| 353 | remoteAddress: socket.remoteAddress,
|
---|
| 354 | remotePort: socket.remotePort,
|
---|
| 355 | remoteFamily: socket.remoteFamily,
|
---|
| 356 | timeout: socket.timeout,
|
---|
| 357 | bytesWritten: socket.bytesWritten,
|
---|
| 358 | bytesRead: socket.bytesRead
|
---|
| 359 | }
|
---|
| 360 | }
|
---|
| 361 |
|
---|
| 362 | async function * convertIterableToBuffer (iterable) {
|
---|
| 363 | for await (const chunk of iterable) {
|
---|
| 364 | yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
---|
| 365 | }
|
---|
| 366 | }
|
---|
| 367 |
|
---|
| 368 | let ReadableStream
|
---|
| 369 | function ReadableStreamFrom (iterable) {
|
---|
| 370 | if (!ReadableStream) {
|
---|
| 371 | ReadableStream = require('stream/web').ReadableStream
|
---|
| 372 | }
|
---|
| 373 |
|
---|
| 374 | if (ReadableStream.from) {
|
---|
| 375 | return ReadableStream.from(convertIterableToBuffer(iterable))
|
---|
| 376 | }
|
---|
| 377 |
|
---|
| 378 | let iterator
|
---|
| 379 | return new ReadableStream(
|
---|
| 380 | {
|
---|
| 381 | async start () {
|
---|
| 382 | iterator = iterable[Symbol.asyncIterator]()
|
---|
| 383 | },
|
---|
| 384 | async pull (controller) {
|
---|
| 385 | const { done, value } = await iterator.next()
|
---|
| 386 | if (done) {
|
---|
| 387 | queueMicrotask(() => {
|
---|
| 388 | controller.close()
|
---|
| 389 | })
|
---|
| 390 | } else {
|
---|
| 391 | const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
|
---|
| 392 | controller.enqueue(new Uint8Array(buf))
|
---|
| 393 | }
|
---|
| 394 | return controller.desiredSize > 0
|
---|
| 395 | },
|
---|
| 396 | async cancel (reason) {
|
---|
| 397 | await iterator.return()
|
---|
| 398 | }
|
---|
| 399 | },
|
---|
| 400 | 0
|
---|
| 401 | )
|
---|
| 402 | }
|
---|
| 403 |
|
---|
| 404 | // The chunk should be a FormData instance and contains
|
---|
| 405 | // all the required methods.
|
---|
| 406 | function isFormDataLike (object) {
|
---|
| 407 | return (
|
---|
| 408 | object &&
|
---|
| 409 | typeof object === 'object' &&
|
---|
| 410 | typeof object.append === 'function' &&
|
---|
| 411 | typeof object.delete === 'function' &&
|
---|
| 412 | typeof object.get === 'function' &&
|
---|
| 413 | typeof object.getAll === 'function' &&
|
---|
| 414 | typeof object.has === 'function' &&
|
---|
| 415 | typeof object.set === 'function' &&
|
---|
| 416 | object[Symbol.toStringTag] === 'FormData'
|
---|
| 417 | )
|
---|
| 418 | }
|
---|
| 419 |
|
---|
| 420 | function throwIfAborted (signal) {
|
---|
| 421 | if (!signal) { return }
|
---|
| 422 | if (typeof signal.throwIfAborted === 'function') {
|
---|
| 423 | signal.throwIfAborted()
|
---|
| 424 | } else {
|
---|
| 425 | if (signal.aborted) {
|
---|
| 426 | // DOMException not available < v17.0.0
|
---|
| 427 | const err = new Error('The operation was aborted')
|
---|
| 428 | err.name = 'AbortError'
|
---|
| 429 | throw err
|
---|
| 430 | }
|
---|
| 431 | }
|
---|
| 432 | }
|
---|
| 433 |
|
---|
| 434 | function addAbortListener (signal, listener) {
|
---|
| 435 | if ('addEventListener' in signal) {
|
---|
| 436 | signal.addEventListener('abort', listener, { once: true })
|
---|
| 437 | return () => signal.removeEventListener('abort', listener)
|
---|
| 438 | }
|
---|
| 439 | signal.addListener('abort', listener)
|
---|
| 440 | return () => signal.removeListener('abort', listener)
|
---|
| 441 | }
|
---|
| 442 |
|
---|
| 443 | const hasToWellFormed = !!String.prototype.toWellFormed
|
---|
| 444 |
|
---|
| 445 | /**
|
---|
| 446 | * @param {string} val
|
---|
| 447 | */
|
---|
| 448 | function toUSVString (val) {
|
---|
| 449 | if (hasToWellFormed) {
|
---|
| 450 | return `${val}`.toWellFormed()
|
---|
| 451 | } else if (nodeUtil.toUSVString) {
|
---|
| 452 | return nodeUtil.toUSVString(val)
|
---|
| 453 | }
|
---|
| 454 |
|
---|
| 455 | return `${val}`
|
---|
| 456 | }
|
---|
| 457 |
|
---|
| 458 | // Parsed accordingly to RFC 9110
|
---|
| 459 | // https://www.rfc-editor.org/rfc/rfc9110#field.content-range
|
---|
| 460 | function parseRangeHeader (range) {
|
---|
| 461 | if (range == null || range === '') return { start: 0, end: null, size: null }
|
---|
| 462 |
|
---|
| 463 | const m = range ? range.match(/^bytes (\d+)-(\d+)\/(\d+)?$/) : null
|
---|
| 464 | return m
|
---|
| 465 | ? {
|
---|
| 466 | start: parseInt(m[1]),
|
---|
| 467 | end: m[2] ? parseInt(m[2]) : null,
|
---|
| 468 | size: m[3] ? parseInt(m[3]) : null
|
---|
| 469 | }
|
---|
| 470 | : null
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | const kEnumerableProperty = Object.create(null)
|
---|
| 474 | kEnumerableProperty.enumerable = true
|
---|
| 475 |
|
---|
| 476 | module.exports = {
|
---|
| 477 | kEnumerableProperty,
|
---|
| 478 | nop,
|
---|
| 479 | isDisturbed,
|
---|
| 480 | isErrored,
|
---|
| 481 | isReadable,
|
---|
| 482 | toUSVString,
|
---|
| 483 | isReadableAborted,
|
---|
| 484 | isBlobLike,
|
---|
| 485 | parseOrigin,
|
---|
| 486 | parseURL,
|
---|
| 487 | getServerName,
|
---|
| 488 | isStream,
|
---|
| 489 | isIterable,
|
---|
| 490 | isAsyncIterable,
|
---|
| 491 | isDestroyed,
|
---|
| 492 | parseRawHeaders,
|
---|
| 493 | parseHeaders,
|
---|
| 494 | parseKeepAliveTimeout,
|
---|
| 495 | destroy,
|
---|
| 496 | bodyLength,
|
---|
| 497 | deepClone,
|
---|
| 498 | ReadableStreamFrom,
|
---|
| 499 | isBuffer,
|
---|
| 500 | validateHandler,
|
---|
| 501 | getSocketInfo,
|
---|
| 502 | isFormDataLike,
|
---|
| 503 | buildURL,
|
---|
| 504 | throwIfAborted,
|
---|
| 505 | addAbortListener,
|
---|
| 506 | parseRangeHeader,
|
---|
| 507 | nodeMajor,
|
---|
| 508 | nodeMinor,
|
---|
| 509 | nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13),
|
---|
| 510 | safeHTTPMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE']
|
---|
| 511 | }
|
---|