@nxtedition/nxt-undici 7.3.13 → 7.3.15

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
 
@@ -66,6 +66,7 @@ class Handler extends DecoratorHandler {
66
66
 
67
67
  socket.on('close', () => {
68
68
  this.#logger.debug('upstream request socket closed')
69
+ this.onDone()
69
70
  })
70
71
 
71
72
  super.onUpgrade(statusCode, headers, socket)
@@ -105,7 +105,9 @@ function reduceHeaders({ headers, proxyName, httpVersion, socket }, fn, acc) {
105
105
  socket.remoteAddress && `for=${printIp(socket.remoteAddress, socket.remotePort)}`,
106
106
  `proto=${socket.encrypted ? 'https' : 'http'}`,
107
107
  forwardedHost && `host="${forwardedHost}"`,
108
- ].join(';'),
108
+ ]
109
+ .filter(Boolean)
110
+ .join(';'),
109
111
  )
110
112
  } else if (forwarded) {
111
113
  // The forwarded header should not be included in response.
@@ -171,8 +173,7 @@ export default () => (dispatch) => (opts, handler) => {
171
173
  // the request message does not contain a payload body and the method
172
174
  // semantics do not anticipate such a body.
173
175
  // undici will error if provided an unexpected content-length: 0 header.
174
- }
175
- if (key[0] === ':') {
176
+ } else if (key[0] === ':') {
176
177
  // strip pseudo headers
177
178
  } else if (key === 'expect') {
178
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)
@@ -176,7 +180,6 @@ class Handler extends DecoratorHandler {
176
180
  }
177
181
 
178
182
  if (this.#statusCode < 400) {
179
- this.#retryCount = 0
180
183
  return super.onData(chunk)
181
184
  }
182
185
 
@@ -194,7 +197,6 @@ class Handler extends DecoratorHandler {
194
197
  this.#trailers = trailers
195
198
 
196
199
  if (this.#statusCode < 400) {
197
- this.#retryCount = 0
198
200
  return super.onComplete(trailers)
199
201
  }
200
202
 
@@ -234,6 +236,14 @@ class Handler extends DecoratorHandler {
234
236
  }
235
237
 
236
238
  super.onComplete(this.#trailers)
239
+ } else {
240
+ // Headers already sent but retry failed (e.g. etag mismatch, missing
241
+ // content-range). The user is waiting for data/complete — send an error
242
+ // so the response stream doesn't hang.
243
+ if (!this.#errorSent) {
244
+ this.#errorSent = true
245
+ super.onError(new Error('Response retry failed'))
246
+ }
237
247
  }
238
248
 
239
249
  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/lib/utils.js CHANGED
@@ -59,8 +59,8 @@ export function parseContentRange(range) {
59
59
  return m
60
60
  ? {
61
61
  start: parseInt(m[1], 10),
62
- end: m[2] ? parseInt(m[2]) + 1 : null,
63
- size: m[3] === '*' ? null : parseInt(m[3]),
62
+ end: m[2] ? parseInt(m[2], 10) + 1 : null,
63
+ size: m[3] === '*' ? null : parseInt(m[3], 10),
64
64
  }
65
65
  : null
66
66
  }
@@ -83,8 +83,8 @@ export function parseRangeHeader(range) {
83
83
  const m = range.match(/^bytes=(\d+)-(\d+)?$/)
84
84
  return m
85
85
  ? {
86
- start: parseInt(m[1]),
87
- end: m[2] ? parseInt(m[2]) + 1 : null,
86
+ start: parseInt(m[1], 10),
87
+ end: m[2] ? parseInt(m[2], 10) + 1 : null,
88
88
  size: null,
89
89
  }
90
90
  : null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/nxt-undici",
3
- "version": "7.3.13",
3
+ "version": "7.3.15",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "main": "lib/index.js",