undici 4.11.1 → 4.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/fetch/body.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const util = require('../core/util')
4
4
  const { ReadableStreamFrom, toUSVString, isBlobLike } = require('./util')
5
5
  const { FormData } = require('./formdata')
6
- const { kState } = require('./symbols')
6
+ const { kState, kError } = require('./symbols')
7
7
  const { Blob } = require('buffer')
8
8
  const { kBodyUsed } = require('../core/symbols')
9
9
  const assert = require('assert')
@@ -268,6 +268,10 @@ const methods = {
268
268
  if (this[kState].body) {
269
269
  const stream = this[kState].body.stream
270
270
 
271
+ if (stream[kError]) {
272
+ throw stream[kError]
273
+ }
274
+
271
275
  if (util.isDisturbed(stream)) {
272
276
  throw new TypeError('disturbed')
273
277
  }
@@ -31,7 +31,7 @@ const {
31
31
  determineRequestsReferrer,
32
32
  coarsenedSharedCurrentTime
33
33
  } = require('./util')
34
- const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
34
+ const { kState, kHeaders, kGuard, kRealm, kError } = require('./symbols')
35
35
  const { AbortError } = require('../core/errors')
36
36
  const assert = require('assert')
37
37
  const { safelyExtractBody, cancelBody } = require('./body')
@@ -82,14 +82,13 @@ async function fetch (...args) {
82
82
  if (this.terminated) {
83
83
  return
84
84
  }
85
+ this.terminated = { aborted }
85
86
 
86
87
  if (this.connection) {
87
- this.connection.destroy()
88
+ this.connection.destroy(reason)
88
89
  this.connection = null
89
90
  }
90
91
 
91
- this.terminated = { aborted }
92
-
93
92
  this.emit('terminated', reason)
94
93
  }
95
94
  })
@@ -266,8 +265,6 @@ function markResourceTiming () {
266
265
 
267
266
  // https://fetch.spec.whatwg.org/#abort-fetch
268
267
  function abortFetch (p, request, responseObject) {
269
- const context = this
270
-
271
268
  // 1. Let error be an "AbortError" DOMException.
272
269
  const error = new AbortError()
273
270
 
@@ -291,7 +288,7 @@ function abortFetch (p, request, responseObject) {
291
288
  // 6. If response’s body is not null and is readable, then error response’s
292
289
  // body with error.
293
290
  if (response.body != null) {
294
- context.connection.destroy(error)
291
+ cancelBody(response.body, error)
295
292
  }
296
293
  }
297
294
 
@@ -649,7 +646,7 @@ async function mainFetch (fetchParams, recursive = false) {
649
646
  nullBodyStatus.includes(internalResponse.status))
650
647
  ) {
651
648
  internalResponse.body = null
652
- context.connection.dump = true
649
+ context.dump = true
653
650
  }
654
651
 
655
652
  // 20. If request’s integrity metadata is not the empty string, then:
@@ -1335,32 +1332,28 @@ function httpNetworkFetch (
1335
1332
  return new Promise((resolve) => {
1336
1333
  assert(!context.connection || context.connection.destroyed)
1337
1334
 
1338
- const connection = (context.connection = {
1335
+ context.connection = {
1339
1336
  abort: null,
1340
1337
  controller: null,
1341
1338
  destroyed: false,
1342
- errored: false,
1343
- dump: false,
1344
1339
  destroy (err) {
1345
1340
  if (this.destroyed) {
1346
1341
  return
1347
1342
  }
1348
-
1349
1343
  this.destroyed = true
1350
1344
 
1345
+ err = err ?? new AbortError()
1346
+
1351
1347
  if (this.abort) {
1352
- this.abort()
1348
+ this.abort(err)
1353
1349
  this.abort = null
1354
1350
  }
1355
1351
 
1356
- if (err) {
1357
- this.errored = err
1358
- }
1359
-
1360
- if (this.controller) {
1352
+ // TODO (fix): Do we need controller here?
1353
+ if (context.controller) {
1361
1354
  try {
1362
- this.controller.error(err ?? new AbortError())
1363
- this.controller = null
1355
+ context.controller.error(err)
1356
+ context.controller = null
1364
1357
  } catch (err) {
1365
1358
  // Will throw TypeError if body is not readable.
1366
1359
  if (err.name !== 'TypeError') {
@@ -1369,7 +1362,7 @@ function httpNetworkFetch (
1369
1362
  }
1370
1363
  }
1371
1364
  }
1372
- })
1365
+ }
1373
1366
 
1374
1367
  // 1. Let request be fetchParams’s request.
1375
1368
  const request = fetchParams.request
@@ -1505,10 +1498,10 @@ function httpNetworkFetch (
1505
1498
  // 9. If aborted, then:
1506
1499
  function onRequestAborted () {
1507
1500
  // 1. Let aborted be the termination’s aborted flag.
1508
- const aborted = context.terminated.aborted
1501
+ const aborted = this.terminated.aborted
1509
1502
 
1510
1503
  // 2. If connection uses HTTP/2, then transmit an RST_STREAM frame.
1511
- connection.destroy()
1504
+ this.connection?.destroy()
1512
1505
 
1513
1506
  // 3. If aborted is set, then return an aborted network error.
1514
1507
  const reason = aborted ? new AbortError() : new Error('terminated')
@@ -1529,7 +1522,7 @@ function httpNetworkFetch (
1529
1522
 
1530
1523
  // 12. Let highWaterMark be a non-negative, non-NaN number, chosen by
1531
1524
  // the user agent.
1532
- const highWaterMark = 65536
1525
+ const highWaterMark = 64 * 1024 // Same as nodejs fs streams.
1533
1526
 
1534
1527
  // 13. Let sizeAlgorithm be an algorithm that accepts a chunk object
1535
1528
  // and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent.
@@ -1543,20 +1536,24 @@ function httpNetworkFetch (
1543
1536
  ReadableStream = require('stream/web').ReadableStream
1544
1537
  }
1545
1538
 
1539
+ let pullResolve
1540
+
1546
1541
  const stream = new ReadableStream(
1547
1542
  {
1548
1543
  async start (controller) {
1549
- connection.controller = controller
1544
+ context.controller = controller
1550
1545
  },
1551
- async pull () {
1552
- if (pullAlgorithm) {
1553
- pullAlgorithm()
1554
- } else {
1555
- pullAlgorithm = null
1546
+ async pull (controller) {
1547
+ if (!pullAlgorithm) {
1548
+ await new Promise((resolve) => {
1549
+ pullResolve = resolve
1550
+ })
1556
1551
  }
1552
+ await pullAlgorithm(controller)
1557
1553
  },
1558
1554
  async cancel (reason) {
1559
- cancelAlgorithm()
1555
+ stream[kError] = reason
1556
+ await cancelAlgorithm(reason)
1560
1557
  }
1561
1558
  },
1562
1559
  { highWaterMark }
@@ -1586,7 +1583,7 @@ function httpNetworkFetch (
1586
1583
  finalizeResponse(fetchParams, response)
1587
1584
 
1588
1585
  // 2. Let aborted be the termination’s aborted flag.
1589
- const aborted = context.terminated.aborted
1586
+ const aborted = this.terminated.aborted
1590
1587
 
1591
1588
  // 3. If aborted is set, then:
1592
1589
  if (aborted) {
@@ -1594,15 +1591,29 @@ function httpNetworkFetch (
1594
1591
  response.aborted = true
1595
1592
 
1596
1593
  // 2. If stream is readable, error stream with an "AbortError" DOMException.
1597
- connection.destroy(new AbortError())
1594
+ try {
1595
+ this.controller.error(new AbortError())
1596
+ } catch (err) {
1597
+ // Will throw TypeError if body is not readable.
1598
+ if (err.name !== 'TypeError') {
1599
+ throw err
1600
+ }
1601
+ }
1598
1602
  } else {
1599
1603
  // 4. Otherwise, if stream is readable, error stream with a TypeError.
1600
- connection.destroy(new TypeError('terminated'))
1604
+ try {
1605
+ this.controller.error(new TypeError('terminated'))
1606
+ } catch (err) {
1607
+ // Will throw TypeError if body is not readable.
1608
+ if (err.name !== 'TypeError') {
1609
+ throw err
1610
+ }
1611
+ }
1601
1612
  }
1602
1613
 
1603
1614
  // 5. If connection uses HTTP/2, then transmit an RST_STREAM frame.
1604
1615
  // 6. Otherwise, the user agent should close connection unless it would be bad for performance to do so.
1605
- connection.destroy()
1616
+ this.connection?.destroy()
1606
1617
  }
1607
1618
 
1608
1619
  // 19. Return response.
@@ -1621,8 +1632,12 @@ function httpNetworkFetch (
1621
1632
  },
1622
1633
  {
1623
1634
  decoder: null,
1635
+ context,
1624
1636
 
1625
1637
  onConnect (abort) {
1638
+ // TODO (fix): Do we need connection here?
1639
+ const { connection } = this.context
1640
+
1626
1641
  if (connection.destroyed) {
1627
1642
  abort(new AbortError())
1628
1643
  } else {
@@ -1643,19 +1658,16 @@ function httpNetworkFetch (
1643
1658
  )
1644
1659
  }
1645
1660
 
1646
- const hasPulled = pullAlgorithm !== undefined
1647
-
1648
- const body = { stream }
1649
- registry.register(body, connection.abort)
1661
+ registry.register(stream, this.abort, this)
1650
1662
 
1651
1663
  response = makeResponse({
1652
1664
  status,
1653
1665
  statusText,
1654
1666
  headersList: headers[kHeadersList],
1655
- body
1667
+ body: { stream }
1656
1668
  })
1657
1669
 
1658
- context.on('terminated', onResponseAborted)
1670
+ this.context.on('terminated', onResponseAborted)
1659
1671
 
1660
1672
  const codings =
1661
1673
  headers
@@ -1704,7 +1716,7 @@ function httpNetworkFetch (
1704
1716
  this.decoder.on('drain', resume)
1705
1717
  }
1706
1718
 
1707
- pullAlgorithm = async () => {
1719
+ pullAlgorithm = async (controller) => {
1708
1720
  // 4. Set bytes to the result of handling content codings given
1709
1721
  // codings and bytes.
1710
1722
  let bytes
@@ -1720,10 +1732,6 @@ function httpNetworkFetch (
1720
1732
  }
1721
1733
  }
1722
1734
 
1723
- if (!connection.controller) {
1724
- return
1725
- }
1726
-
1727
1735
  if (bytes === undefined) {
1728
1736
  // 2. Otherwise, if the bytes transmission for response’s message
1729
1737
  // body is done normally and stream is readable, then close
@@ -1731,13 +1739,7 @@ function httpNetworkFetch (
1731
1739
  // abort these in-parallel steps.
1732
1740
  finalizeResponse(fetchParams, response)
1733
1741
 
1734
- context.off('terminated', onResponseAborted)
1735
- context.off('terminated', onRequestAborted)
1736
-
1737
- connection.controller.close()
1738
- connection.controller = null
1739
-
1740
- connection.destroy()
1742
+ controller.close()
1741
1743
 
1742
1744
  return
1743
1745
  }
@@ -1747,27 +1749,28 @@ function httpNetworkFetch (
1747
1749
 
1748
1750
  // 6. If bytes is failure, then terminate the ongoing fetch.
1749
1751
  if (bytes instanceof Error) {
1750
- context.terminate({ reason: bytes })
1752
+ this.context.terminate({ reason: bytes })
1751
1753
  return
1752
1754
  }
1753
1755
 
1754
1756
  // 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes
1755
- // into stream.
1756
- connection.controller.enqueue(new Uint8Array(bytes))
1757
+ // into stream.
1758
+ controller.enqueue(new Uint8Array(bytes))
1757
1759
 
1758
1760
  // 8. If stream is errored, then terminate the ongoing fetch.
1759
- if (connection.errored) {
1760
- context.terminate({ reason: connection.errored })
1761
+ if (stream[kError]) {
1762
+ this.context.terminate({ reason: stream[kError] })
1761
1763
  return
1762
1764
  }
1763
1765
 
1764
1766
  // 9. If stream doesn’t need more data ask the user agent to suspend
1765
- // the ongoing fetch.
1766
- return connection.controller.desiredSize > 0
1767
+ // the ongoing fetch.
1768
+ return controller.desiredSize > 0
1767
1769
  }
1768
1770
 
1769
- if (hasPulled) {
1770
- pullAlgorithm()
1771
+ if (pullResolve) {
1772
+ pullResolve()
1773
+ pullResolve = null
1771
1774
  }
1772
1775
 
1773
1776
  resolve(response)
@@ -1776,7 +1779,7 @@ function httpNetworkFetch (
1776
1779
  },
1777
1780
 
1778
1781
  onData (chunk) {
1779
- if (connection.dump) {
1782
+ if (this.context.dump) {
1780
1783
  return
1781
1784
  }
1782
1785
 
@@ -1798,17 +1801,18 @@ function httpNetworkFetch (
1798
1801
  return this.decoder.write(bytes)
1799
1802
  },
1800
1803
 
1801
- async onComplete () {
1804
+ onComplete () {
1805
+ registry.unregister(this)
1806
+
1802
1807
  this.decoder.end()
1803
1808
  },
1804
1809
 
1805
1810
  onError (error) {
1806
- context.off('terminated', onResponseAborted)
1807
- context.off('terminated', onRequestAborted)
1811
+ registry.unregister(this)
1808
1812
 
1809
- connection.destroy(error)
1813
+ this.decoder?.destroy(error)
1810
1814
 
1811
- context.terminate({ reason: error })
1815
+ this.context.terminate({ reason: error })
1812
1816
 
1813
1817
  if (!response) {
1814
1818
  resolve(makeNetworkError(error))
@@ -6,5 +6,6 @@ module.exports = {
6
6
  kSignal: Symbol('signal'),
7
7
  kState: Symbol('state'),
8
8
  kGuard: Symbol('guard'),
9
- kRealm: Symbol('realm')
9
+ kRealm: Symbol('realm'),
10
+ kError: Symbol('error')
10
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "4.11.1",
3
+ "version": "4.11.2",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {