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 4.4.1 to 4.4.2

3

index.js

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

const nodeMajor = Number(process.versions.node.split('.')[0])
const nodeMinor = Number(process.versions.node.split('.')[1])

@@ -90,3 +91,3 @@ Object.assign(Dispatcher.prototype, api)

if (nodeMajor >= 16) {
if (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 5)) {
const fetchImpl = require('./lib/fetch')

@@ -93,0 +94,0 @@ module.exports.fetch = async function fetch (resource, init) {

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

const util = require('../core/util')
const { toWebReadable } = require('../fetch/util')

@@ -134,3 +135,3 @@ let Blob

if (!this[kBody]) {
this[kBody] = util.toWeb(this)
this[kBody] = toWebReadable(this)
if (this[kConsume]) {

@@ -137,0 +138,0 @@ // TODO: Is this the best way to force a lock?

@@ -266,10 +266,4 @@ 'use strict'

function isBodyReadable (body) {
// This is a hack!
return body && /state: 'readable'/.test(nodeUtil.inspect(body.stream))
}
module.exports = {
extractBody,
isBodyReadable,
safelyExtractBody,

@@ -276,0 +270,0 @@ cloneBody,

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

const { Request, makeRequest } = require('./request')
const zlib = require('zlib')
const {
requestBadPort,
responseLocationURL,
requestCurrentURL
requestCurrentURL,
setRequestReferrerPolicyOnRedirect,
makeTimingInfo
} = require('./util')

@@ -22,3 +25,3 @@ const { kState, kHeaders, kGuard } = require('./symbols')

const assert = require('assert')
const { safelyExtractBody, isBodyReadable } = require('./body')
const { safelyExtractBody } = require('./body')
const {

@@ -34,3 +37,9 @@ redirectStatus,

const EE = require('events')
const { PassThrough, pipeline } = require('stream')
// https://fetch.spec.whatwg.org/#garbage-collection
const registry = new FinalizationRegistry((abort) => {
abort()
})
// https://fetch.spec.whatwg.org/#fetch-method

@@ -40,6 +49,6 @@ async function fetch (resource, init) {

dispatcher: this,
controller: null,
terminated: false,
abort: null, // "connection" abort
terminate ({ aborted } = {}) {
connection: null,
dump: false,
terminate ({ reason, aborted } = {}) {
if (this.terminated) {

@@ -49,4 +58,5 @@ return

if (context.abort) {
context.abort()
if (this.connection) {
this.connection.destroy()
this.connection = null
}

@@ -56,3 +66,3 @@

this.emit('terminated')
this.emit('terminated', reason)
}

@@ -165,2 +175,57 @@ })

function finalizeAndReportTiming (response, initiatorType = 'other') {
// 1. If response’s URL list is null or empty, then return.
if (response.urlList.length === 0) {
return
}
// 2. Let originalURL be response’s URL list[0].
const originalURL = response.urlList[0]
// 3. Let timingInfo be response’s timing info.
let timingInfo = response.timingInfo
// 4. Let cacheState be response’s cache state.
let cacheState = response.cacheState
// 5. If timingInfo is null, then return.
if (timingInfo === null) {
return
}
// 6. If response’s timing allow passed flag is not set, then:
if (!timingInfo.timingAllowPassed) {
// 1. Set timingInfo to a new fetch timing info whose start time and
// post-redirect start time are timingInfo’s start time.
timingInfo = makeTimingInfo({
startTime: timingInfo.startTime,
postRedirectStartTime: timingInfo.postRedirectStartTime
})
// 2. Set cacheState to the empty string.
cacheState = ''
}
// 7. Set timingInfo’s end time to the coarsened shared current time
// given global’s relevant settings object’s cross-origin isolated
// capability.
// TODO: given global’s relevant settings object’s cross-origin isolated
// capability
response.timingInfo.endTime = performance.now()
// 8. Set response’s timing info to timingInfo.
response.timingInfo = timingInfo
// 9. Mark resource timing for timingInfo, originalURL, initiatorType,
// global, and cacheState.
markResourceTiming(
timingInfo,
originalURL,
initiatorType,
global,
cacheState
)
}
// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
function markResourceTiming () {
// TODO

@@ -181,4 +246,4 @@ }

// body with error.
if (request.body !== null && isBodyReadable(request.body)) {
request.body.stream.cancel(error)
if (request.body !== null) {
cancelIfReadable(request.body.stream, error)
}

@@ -196,4 +261,4 @@

// body with error.
if (response.body != null && isBodyReadable(response.body)) {
context.controller.error(error)
if (response.body != null) {
context.connection.destroy(error)
}

@@ -222,15 +287,6 @@ }

const currenTime = performance.now()
const timingInfo = {
const timingInfo = makeTimingInfo({
startTime: currenTime,
redirectStartTime: 0,
redirectEndTime: 0,
postRedirectStartTime: currenTime,
finalServiceWorkerStartTime: 0,
finalNetworkResponseStartTime: 0,
finalNetworkRequestStartTime: 0,
endTime: 0,
encodedBodySize: 0,
decodedBodySize: 0,
finalConnectionTimingInfo: null
}
postRedirectStartTime: currenTime
})

@@ -317,2 +373,4 @@ // 6. Let fetchParams be a new fetch params whose request is request, timing

async function mainFetch (fetchParams, recursive = false) {
const context = this
// 1. Let request be fetchParams’s request.

@@ -528,5 +586,3 @@ const request = fetchParams.request

internalResponse.body = null
if (context.controller) {
context.controller.error(new AbortError())
}
context.connection.dump = true
}

@@ -583,3 +639,3 @@

// fetchParams’s task destination.
if (fetchParams.processResponseDone) {
if (fetchParams.processResponseDone !== null) {
fetchParams.processResponseDone(response)

@@ -591,2 +647,4 @@ }

function fetchFinale (fetchParams, response) {
const context = this
// 1. If fetchParams’s process response is non-null,

@@ -613,2 +671,8 @@ // then queue a fetch task to run fetchParams’s process response

// TODO
// TODO (spec): The spec doesn't specify this but we need to
// terminate fetch if we have an error response.
if (response.status === 0) {
context.terminate({ reason: response.error })
}
}

@@ -618,2 +682,4 @@

async function httpFetch (fetchParams) {
const context = this
// 1. Let request be fetchParams’s request.

@@ -673,5 +739,3 @@ const request = fetchParams.request

// See, https://github.com/whatwg/fetch/issues/1288
if (context.abort) {
context.abort()
}
context.connection.destroy()

@@ -815,3 +879,4 @@ // 2. Switch on request’s redirect mode:

// 16. Set timingInfo’s redirect end time and post-redirect start time to the
// coarsened shared current time given fetchParams’s cross-origin isolated capability.
// coarsened shared current time given fetchParams’s cross-origin isolated
// capability.
// TODO: given fetchParams’s cross-origin isolated capability?

@@ -821,4 +886,4 @@ timingInfo.redirectEndTime = timingInfo.postRedirectStartTime =

// 17. If timingInfo’s redirect start time is 0, then set timingInfo’s redirect start
// time to timingInfo’s start time.
// 17. If timingInfo’s redirect start time is 0, then set timingInfo’s
// redirect start time to timingInfo’s start time.
if (timingInfo.redirectStartTime === 0) {

@@ -831,4 +896,5 @@ timingInfo.redirectStartTime = timingInfo.startTime

// 19. Invoke set request’s referrer policy on redirect on request and actualResponse.
// TODO
// 19. Invoke set request’s referrer policy on redirect on request and
// actualResponse.
setRequestReferrerPolicyOnRedirect(request, actualResponse)

@@ -845,2 +911,4 @@ // 20. Return the result of running main fetch given fetchParams and true.

) {
const context = this
// 1. Let request be fetchParams’s request.

@@ -1155,2 +1223,4 @@ const request = fetchParams.request

) {
// then:
// 1. If the ongoing fetch is terminated, then:

@@ -1168,2 +1238,10 @@ if (context.terminated) {

// 2. Set response to the result of running HTTP-network-or-cache
// fetch given fetchParams, isAuthenticationFetch, and true.
// TODO (spec): The spec doesn't specify this but we need to cancel
// the active response before we can start a new one.
// https://github.com/whatwg/fetch/issues/1293
context.connection.destroy()
response = await httpNetworkOrCacheFetch.call(

@@ -1193,5 +1271,43 @@ this,

) {
const context = this
return new Promise((resolve) => {
const context = this
assert(!context.connection || context.connection.destroyed)
const connection = (context.connection = {
abort: null,
controller: null,
destroyed: false,
errored: false,
dump: false,
destroy (err) {
if (this.destroyed) {
return
}
this.destroyed = true
if (this.abort) {
this.abort()
this.abort = null
}
if (err) {
this.errored = err
}
if (this.controller) {
try {
this.controller.error(err ?? new AbortError())
this.controller = null
} catch (err) {
// Will throw TypeError if body is not readable.
if (err.name !== 'TypeError') {
throw err
}
}
}
}
})
// 1. Let request be fetchParams’s request.

@@ -1204,3 +1320,3 @@ const request = fetchParams.request

// 3. Let timingInfo be fetchParams’s timing info.
// TODO
const timingInfo = fetchParams.timingInfo

@@ -1312,12 +1428,11 @@ // 4. Let httpCache be the result of determining the HTTP cache partition,

// 2. If e is an "AbortError" DOMException, then terminate the ongoing fetch with the aborted flag set.
if (e.name === 'AbortError') {
context.terminate({ aborted: true })
return
}
// 3. Otherwise, terminate the ongoing fetch.
context.terminate()
context.terminate({
aborted: e.name === 'AbortError',
reason: e
})
}
})()
// 9. If aborted, then:
function onRequestAborted () {

@@ -1328,26 +1443,99 @@ // 1. Let aborted be the termination’s aborted flag.

// 2. If connection uses HTTP/2, then transmit an RST_STREAM frame.
if (context.abort) {
context.abort()
}
connection.destroy()
// 3. If aborted is set, then return an aborted network error.
if (aborted) {
resolve(makeNetworkError(new AbortError()))
}
const reason = aborted ? new AbortError() : new Error('terminated')
// 4. Return a network error.
resolve(makeNetworkError())
resolve(makeNetworkError(reason))
}
// TODO...
// 10. Let pullAlgorithm be an action that resumes the ongoing fetch
// if it is suspended.
let pullAlgorithm
// TODO: forceNewConnection
// 11. Let cancelAlgorithm be an action that terminates the ongoing
// fetch with the aborted flag set.
const cancelAlgorithm = () => {
context.terminate({ aborted: true })
}
// NOTE: This is just a hack.
if (forceNewConnection && context.controller) {
context.abort()
// 12. Let highWaterMark be a non-negative, non-NaN number, chosen by
// the user agent.
const highWaterMark = 65536
// 13. Let sizeAlgorithm be an algorithm that accepts a chunk object
// and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent.
// TODO
// 14. Let stream be a new ReadableStream.
// 15. Set up stream with pullAlgorithm set to pullAlgorithm,
// cancelAlgorithm set to cancelAlgorithm, highWaterMark set to
// highWaterMark, and sizeAlgorithm set to sizeAlgorithm.
const stream = new ReadableStream(
{
async start (controller) {
connection.controller = controller
},
async pull () {
if (pullAlgorithm) {
pullAlgorithm()
} else {
pullAlgorithm = null
}
},
async cancel (reason) {
cancelAlgorithm()
}
},
{ highWaterMark }
)
// 16. Run these steps, but abort when the ongoing fetch is terminated:
// TODO
// 17. If aborted, then:
// TODO: How can this happen? The steps above are not async?
// 18. Run these steps in parallel:
// 1. Run these steps, but abort when the ongoing fetch is terminated:
// 1. While true:
// 1. If one or more bytes have been transmitted from response’s
// message body, then:
// NOTE: See onHeaders
// 2. Otherwise, if the bytes transmission for response’s message
// body is done normally and stream is readable, then close stream,
// finalize response for fetchParams and response, and abort these
// in-parallel steps.
// NOTE: See onHeaders
// 2. If aborted, then:
function onResponseAborted () {
// 1. Finalize response for fetchParams and response.
finalizeResponse(fetchParams, response)
// 2. Let aborted be the termination’s aborted flag.
const aborted = context.terminated.aborted
// 3. If aborted is set, then:
if (aborted) {
// 1. Set response’s aborted flag.
response.aborted = true
// 2. If stream is readable, error stream with an "AbortError" DOMException.
connection.destroy(new AbortError())
} else {
// 4. Otherwise, if stream is readable, error stream with a TypeError.
connection.destroy(new TypeError('terminated'))
}
// 5. If connection uses HTTP/2, then transmit an RST_STREAM frame.
// 6. Otherwise, the user agent should close connection unless it would be bad for performance to do so.
connection.destroy()
}
assert(!context.controller)
// 19. Return response.
// NOTE: See onHeaders
// Implementation
const url = requestCurrentURL(request)

@@ -1364,7 +1552,9 @@ context.dispatcher.dispatch(

{
decoder: null,
onConnect (abort) {
if (context.terminated) {
if (connection.destroyed) {
abort(new AbortError())
} else {
context.abort = (err) => abort(err ?? new AbortError())
connection.abort = abort
}

@@ -1386,28 +1576,6 @@ },

const stream =
status === 204
? null
: new ReadableStream(
{
async start (controller) {
context.controller = controller
},
async pull () {
resume()
},
async cancel (reason) {
let err
if (reason instanceof Error) {
err = reason
} else if (typeof reason === 'string') {
err = new Error(reason)
} else {
err = new AbortError()
}
const hasPulled = pullAlgorithm !== undefined
context.abort(err)
}
},
{ highWaterMark: 16384 }
)
const body = { stream }
registry.register(body, connection.abort)

@@ -1418,40 +1586,156 @@ response = makeResponse({

headersList: headers[kHeadersList],
body: stream ? { stream } : null
body
})
context.on('terminated', onResponseAborted)
const codings =
headers
.get('content-encoding')
?.toLowerCase()
.split(',')
.map((x) => x.trim()) ?? []
const decoders = []
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
for (const coding of codings) {
if (/(x-)?gzip/.test(coding)) {
decoders.push(zlib.createGunzip())
} else if (/(x-)?deflate/.test(coding)) {
decoders.push(zlib.createInflate())
} else if (coding === 'br') {
decoders.push(zlib.createBrotliDecompress())
} else {
// TODO: What to do when coding is invalid or unsupported?
}
}
let iterator
if (decoders.length > 1) {
this.decoder = new PassThrough()
iterator = pipeline(this.decoder, ...decoders, () => {})[
Symbol.asyncIterator
]()
} else if (decoders.length === 1) {
this.decoder = decoders[0]
iterator = this.decoder[Symbol.asyncIterator]()
} else {
this.decoder = new PassThrough()
iterator = this.decoder[Symbol.asyncIterator]()
}
if (this.decoder) {
this.decoder.on('drain', resume)
}
pullAlgorithm = async () => {
// 4. Set bytes to the result of handling content codings given
// codings and bytes.
let bytes
try {
const { done, value } = await iterator.next()
bytes = done ? undefined : value
} catch (err) {
if (this.decoder.writableEnded && !timingInfo.encodedBodySize) {
// zlib doesn't like empty streams.
bytes = undefined
} else {
bytes = err
}
}
if (!connection.controller) {
return
}
if (bytes === undefined) {
// 2. Otherwise, if the bytes transmission for response’s message
// body is done normally and stream is readable, then close
// stream, finalize response for fetchParams and response, and
// abort these in-parallel steps.
finalizeResponse(fetchParams, response)
context.off('terminated', onResponseAborted)
context.off('terminated', onRequestAborted)
connection.controller.close()
connection.controller = null
connection.destroy()
return
}
// 5. Increase timingInfo’s decoded body size by bytes’s length.
timingInfo.decodedBodySize += bytes ? bytes.byteLength : 0
// 6. If bytes is failure, then terminate the ongoing fetch.
if (bytes instanceof Error) {
context.terminate({ reason: bytes })
return
}
// 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes
// into stream.
connection.controller.enqueue(new Uint8Array(bytes))
// 8. If stream is errored, then terminate the ongoing fetch.
if (connection.errored) {
context.terminate({ reason: connection.errored })
return
}
// 9. If stream doesn’t need more data ask the user agent to suspend
// the ongoing fetch.
return connection.controller.desiredSize > 0
}
if (hasPulled) {
pullAlgorithm()
}
resolve(response)
return false
return true
},
onData (chunk) {
assert(context.controller)
if (connection.dump) {
return
}
// Copy the Buffer to detach it from Buffer pool.
// TODO: Is this required?
chunk = new Uint8Array(chunk)
// 1. If one or more bytes have been transmitted from response’s
// message body, then:
context.controller.enqueue(chunk)
// 1. Let bytes be the transmitted bytes.
const bytes = chunk
return context.controller.desiredSize > 0
},
// 2. Let codings be the result of extracting header list values
// given `Content-Encoding` and response’s header list.
// See pullAlgorithm.
onComplete () {
assert(context.controller)
// 3. Increase timingInfo’s encoded body size by bytes’s length.
timingInfo.encodedBodySize += bytes.byteLength
context.controller.close()
context.controller = null
// 4. See pullAlgorithm...
finalizeResponse(fetchParams, response)
return this.decoder.write(bytes)
},
onError (err) {
context.terminate({ aborted: err.name === 'AbortError' })
async onComplete () {
this.decoder.end()
},
if (context.controller) {
context.controller.error(err)
context.controller = null
} else {
// TODO: What if 204?
resolve(makeNetworkError(err))
onError (error) {
context.off('terminated', onResponseAborted)
context.off('terminated', onRequestAborted)
connection.destroy(error)
context.terminate({ reason: error })
if (!response) {
resolve(makeNetworkError(error))
}

@@ -1475,2 +1759,13 @@ }

function cancelIfReadable (stream, reason) {
try {
stream.cancel(reason)
} catch (err) {
// Will throw TypeError if body is not readable.
if (err.name !== 'TypeError') {
throw err
}
}
}
module.exports = fetch

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

timingInfo: null,
cacheState: '',
statusText: '',

@@ -280,0 +281,0 @@ ...init,

@@ -205,4 +205,40 @@ 'use strict'

// https://w3c.github.io/webappsec-referrer-policy/#set-requests-referrer-policy-on-redirect
function setRequestReferrerPolicyOnRedirect (request, actualResponse) {
// Given a request request and a response actualResponse, this algorithm
// updates request’s referrer policy according to the Referrer-Policy
// header (if any) in actualResponse.
// 1. Let policy be the result of executing § 8.1 Parse a referrer policy
// from a Referrer-Policy header on actualResponse.
// TODO: https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header
const policy = ''
// 2. If policy is not the empty string, then set request’s referrer policy to policy.
if (policy !== '') {
request.referrerPolicy = policy
}
}
function makeTimingInfo (init) {
return {
startTime: 0,
redirectStartTime: 0,
redirectEndTime: 0,
postRedirectStartTime: 0,
finalServiceWorkerStartTime: 0,
finalNetworkResponseStartTime: 0,
finalNetworkRequestStartTime: 0,
endTime: 0,
encodedBodySize: 0,
decodedBodySize: 0,
finalConnectionTimingInfo: null,
...init
}
}
module.exports = {
toWebReadable,
makeTimingInfo,
setRequestReferrerPolicyOnRedirect,
isValidHTTPToken,

@@ -209,0 +245,0 @@ requestBadPort,

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

@@ -5,0 +5,0 @@ "homepage": "https://undici.nodejs.org",

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