@nxtedition/nxt-undici 7.3.14 → 7.3.16

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.
@@ -45,14 +45,19 @@ export default () => (dispatch) => {
45
45
  resolve([err, null])
46
46
  } else {
47
47
  const now = getFastNow()
48
- const val = records.map(({ address }) => ({
49
- address,
50
- expires: now + (ttl ?? 1e3),
51
- pending: 0,
52
- errored: 0,
53
- counter: 0,
54
- timeout: 0,
55
- }))
48
+ const prev = cache.get(hostname)
49
+ const prevByAddr = prev ? new Map(prev.map((r) => [r.address, r])) : null
50
+ const val = records.map(({ address }) => {
51
+ const old = prevByAddr?.get(address)
52
+ return {
53
+ address,
54
+ expires: now + (ttl ?? 1e3),
55
+ pending: 0,
56
+ errored: old ? old.errored : 0,
57
+ counter: 0,
58
+ timeout: old ? old.timeout : 0,
59
+ }
60
+ })
56
61
 
57
62
  cache.set(hostname, val)
58
63
 
@@ -1,5 +1,8 @@
1
1
  import { DecoratorHandler } from '../utils.js'
2
2
 
3
+ const kGlobalIndex = Symbol('globalIndex')
4
+ const kGlobalArray = Symbol('globalArray')
5
+
3
6
  class Handler extends DecoratorHandler {
4
7
  #opts
5
8
  #logger
@@ -19,9 +22,6 @@ class Handler extends DecoratorHandler {
19
22
  #statusCode
20
23
  #headers
21
24
 
22
- #globalIndex = -1
23
- #globalArray = (globalThis.__undici_requests ??= [])
24
-
25
25
  constructor(logOpts, opts, { handler }) {
26
26
  super(handler)
27
27
 
@@ -35,7 +35,8 @@ class Handler extends DecoratorHandler {
35
35
  this.#logger.debug('upstream request started')
36
36
  this.#timing.created = this.#created + performance.timeOrigin
37
37
 
38
- this.#globalIndex = this.#globalArray.push(this) - 1
38
+ this[kGlobalArray] = globalThis.__undici_requests ??= []
39
+ this[kGlobalIndex] = this[kGlobalArray].push(this) - 1
39
40
  }
40
41
 
41
42
  onConnect(abort) {
@@ -151,13 +152,13 @@ class Handler extends DecoratorHandler {
151
152
  }
152
153
 
153
154
  onDone() {
154
- if (this.#globalIndex !== -1) {
155
- const tmp = this.#globalArray.pop()
155
+ if (this[kGlobalIndex] !== -1) {
156
+ const tmp = this[kGlobalArray].pop()
156
157
  if (tmp !== this) {
157
- this.#globalArray[this.#globalIndex] = tmp
158
- tmp.#globalIndex = this.#globalIndex
158
+ this[kGlobalArray][this[kGlobalIndex]] = tmp
159
+ tmp[kGlobalIndex] = this[kGlobalIndex]
159
160
  }
160
- this.#globalIndex = -1
161
+ this[kGlobalIndex] = -1
161
162
  }
162
163
  }
163
164
  }
@@ -173,8 +173,7 @@ export default () => (dispatch) => (opts, handler) => {
173
173
  // the request message does not contain a payload body and the method
174
174
  // semantics do not anticipate such a body.
175
175
  // undici will error if provided an unexpected content-length: 0 header.
176
- }
177
- if (key[0] === ':') {
176
+ } else if (key[0] === ':') {
178
177
  // strip pseudo headers
179
178
  } else if (key === 'expect') {
180
179
  // undici doesn't support expect header.
@@ -176,11 +176,10 @@ function shouldRemoveHeader(header, removeContent, unknownOrigin) {
176
176
 
177
177
  // https://tools.ietf.org/html/rfc7231#section-6.4
178
178
  function cleanRequestHeaders(headers, removeContent, unknownOrigin) {
179
- let ret
179
+ const ret = {}
180
180
  if (headers && typeof headers === 'object') {
181
181
  for (const key of Object.keys(headers)) {
182
182
  if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) {
183
- ret ??= {}
184
183
  ret[key] = headers[key]
185
184
  }
186
185
  }
@@ -1,11 +1,14 @@
1
1
  import { DecoratorHandler, decorateError } from '../utils.js'
2
2
 
3
+ const MAX_ERROR_BODY_SIZE = 256 * 1024
4
+
3
5
  class Handler extends DecoratorHandler {
4
6
  #statusCode = 0
5
7
  #decoder
6
8
  #headers
7
9
  #trailers
8
10
  #body = ''
11
+ #bodySize = 0
9
12
  #opts
10
13
 
11
14
  constructor(opts, { handler }) {
@@ -19,6 +22,7 @@ class Handler extends DecoratorHandler {
19
22
  this.#decoder = null
20
23
  this.#headers = null
21
24
  this.#body = ''
25
+ this.#bodySize = 0
22
26
 
23
27
  super.onConnect(abort)
24
28
  }
@@ -45,7 +49,12 @@ class Handler extends DecoratorHandler {
45
49
  return super.onData(chunk)
46
50
  }
47
51
 
48
- this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? ''
52
+ if (this.#decoder) {
53
+ this.#bodySize += chunk.byteLength
54
+ if (this.#bodySize <= MAX_ERROR_BODY_SIZE) {
55
+ this.#body += this.#decoder.decode(chunk, { stream: true })
56
+ }
57
+ }
49
58
  }
50
59
 
51
60
  onComplete(trailers) {
@@ -85,6 +85,10 @@ class Handler extends DecoratorHandler {
85
85
  this.#statusCode = statusCode
86
86
  this.#headers = headers
87
87
 
88
+ if (statusCode < 200) {
89
+ return super.onHeaders(statusCode, headers, resume)
90
+ }
91
+
88
92
  if (!this.#headersSent) {
89
93
  assert(this.#etag == null)
90
94
  assert(this.#pos == null)
@@ -173,6 +177,11 @@ class Handler extends DecoratorHandler {
173
177
  onData(chunk) {
174
178
  if (this.#pos != null) {
175
179
  this.#pos += chunk.byteLength
180
+
181
+ if (this.#end != null && this.#pos > this.#end) {
182
+ this.#maybeError(new Error('Response body exceeded Content-Range'))
183
+ return false
184
+ }
176
185
  }
177
186
 
178
187
  if (this.#statusCode < 400) {
@@ -193,6 +202,10 @@ class Handler extends DecoratorHandler {
193
202
  this.#trailers = trailers
194
203
 
195
204
  if (this.#statusCode < 400) {
205
+ if (this.#end != null && this.#pos !== this.#end) {
206
+ this.#maybeError(new Error('Response body length mismatch with Content-Range'))
207
+ return
208
+ }
196
209
  return super.onComplete(trailers)
197
210
  }
198
211
 
@@ -232,6 +245,14 @@ class Handler extends DecoratorHandler {
232
245
  }
233
246
 
234
247
  super.onComplete(this.#trailers)
248
+ } else {
249
+ // Headers already sent but retry failed (e.g. etag mismatch, missing
250
+ // content-range). The user is waiting for data/complete — send an error
251
+ // so the response stream doesn't hang.
252
+ if (!this.#errorSent) {
253
+ this.#errorSent = true
254
+ super.onError(new Error('Response retry failed'))
255
+ }
235
256
  }
236
257
 
237
258
  this.#maybeAbort(err)
@@ -1,5 +1,4 @@
1
1
  import crypto from 'node:crypto'
2
- import assert from 'node:assert'
3
2
  import { DecoratorHandler } from '../utils.js'
4
3
 
5
4
  class Handler extends DecoratorHandler {
@@ -16,8 +15,6 @@ class Handler extends DecoratorHandler {
16
15
  }
17
16
 
18
17
  onConnect(abort) {
19
- assert(!this.#pos)
20
-
21
18
  this.#contentMD5 = null
22
19
  this.#contentLength = null
23
20
  this.#hasher = null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/nxt-undici",
3
- "version": "7.3.14",
3
+ "version": "7.3.16",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "main": "lib/index.js",