@nxtedition/nxt-undici 6.2.17 → 6.2.19

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/index.js CHANGED
@@ -15,7 +15,6 @@ export const interceptors = {
15
15
  proxy: (await import('./interceptor/proxy.js')).default,
16
16
  cache: (await import('./interceptor/cache.js')).default,
17
17
  requestId: (await import('./interceptor/request-id.js')).default,
18
- dns: (await import('./interceptor/dns.js')).default,
19
18
  lookup: (await import('./interceptor/lookup.js')).default,
20
19
  }
21
20
 
@@ -61,7 +60,6 @@ function wrapDispatch(dispatcher) {
61
60
  wrappedDispatcher = compose(
62
61
  dispatcher,
63
62
  interceptors.log({ bindings: { intercept: 'upstream' } }),
64
- interceptors.dns(),
65
63
  interceptors.requestBodyFactory(),
66
64
  interceptors.lookup(),
67
65
  interceptors.responseRetry(),
@@ -101,7 +99,6 @@ function wrapDispatch(dispatcher) {
101
99
  error: opts.error ?? opts.throwOnError ?? true,
102
100
  verify: opts.verify ?? false,
103
101
  logger: opts.logger ?? null,
104
- dns: opts.dns ?? true,
105
102
  connect: opts.connect,
106
103
  lookup: opts.lookup ?? defaultLookup,
107
104
  },
@@ -10,12 +10,14 @@ class CacheHandler extends DecoratorHandler {
10
10
  #key
11
11
  #value
12
12
  #store
13
+ #logger
13
14
  #maxEntrySize
14
15
 
15
- constructor(key, { store, handler, maxEntrySize }) {
16
+ constructor(key, { store, logger, handler, maxEntrySize }) {
16
17
  super(handler)
17
18
 
18
19
  this.#key = key
20
+ this.#logger = logger
19
21
  this.#value = null
20
22
  this.#store = store
21
23
  this.#maxEntrySize = maxEntrySize ?? store.maxEntrySize ?? DEFAULT_MAX_ENTRY_SIZE
@@ -156,7 +158,11 @@ class CacheHandler extends DecoratorHandler {
156
158
  onComplete(trailers) {
157
159
  if (this.#value && (!trailers || Object.keys(trailers).length === 0)) {
158
160
  this.#value.end ??= this.#value.start + this.#value.size
159
- this.#store.set(this.#key, this.#value)
161
+ try {
162
+ this.#store.set(this.#key, this.#value)
163
+ } catch (err) {
164
+ this.#logger?.error({ err }, 'failed to set cache entry')
165
+ }
160
166
  this.#value = null
161
167
  }
162
168
 
@@ -194,7 +200,12 @@ export default () => (dispatch) => (opts, handler) => {
194
200
 
195
201
  // TODO (fix): enable range requests
196
202
 
197
- const entry = store.get(opts)
203
+ let entry
204
+ try {
205
+ entry = store.get(opts)
206
+ } catch (err) {
207
+ opts.logger?.error({ err }, 'failed to get cache entry')
208
+ }
198
209
 
199
210
  if (!entry && !cacheControlDirectives['only-if-cached']) {
200
211
  return dispatch(
@@ -204,6 +215,7 @@ export default () => (dispatch) => (opts, handler) => {
204
215
  : new CacheHandler(undici.util.cache.makeCacheKey(opts), {
205
216
  maxEntrySize: opts.cache.maxEntrySize,
206
217
  store,
218
+ logger: opts.logger,
207
219
  handler,
208
220
  }),
209
221
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/nxt-undici",
3
- "version": "6.2.17",
3
+ "version": "6.2.19",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "main": "lib/index.js",
@@ -1,137 +0,0 @@
1
- import net from 'node:net'
2
- import assert from 'node:assert'
3
- import * as dns from 'node:dns'
4
- import { DecoratorHandler, getFastNow } from '../utils.js'
5
-
6
- class Handler extends DecoratorHandler {
7
- #callback
8
- #statusCode
9
-
10
- constructor(handler, callback) {
11
- super(handler)
12
- this.#callback = callback
13
- }
14
-
15
- onHeaders(statusCode, headers, resume) {
16
- this.#statusCode = statusCode
17
- return super.onHeaders(statusCode, headers, resume)
18
- }
19
-
20
- onComplete(trailers) {
21
- this.#callback(null, this.#statusCode)
22
- super.onComplete(trailers)
23
- }
24
-
25
- onError(err) {
26
- this.#callback(err, this.#statusCode)
27
- super.onError(err)
28
- }
29
- }
30
-
31
- export default () => (dispatch) => {
32
- const cache = new Map()
33
- const promises = new Map()
34
-
35
- function resolve(hostname, { logger }) {
36
- let promise = promises.get(hostname)
37
- if (!promise) {
38
- promise = new Promise((resolve) => {
39
- logger?.debug({ dns: { hostname } }, 'lookup started')
40
- dns.resolve4(hostname, { ttl: true }, (err, records) => {
41
- promises.delete(hostname)
42
-
43
- if (err) {
44
- logger?.error({ err, dns: { hostname } }, 'lookup failed')
45
-
46
- resolve([err, null])
47
- } else {
48
- logger?.debug({ dns: { hostname, records } }, 'lookup completed')
49
-
50
- const now = getFastNow()
51
- const val = records.map(({ address, ttl }) => ({
52
- address,
53
- expires: now + 1e3 * ttl,
54
- pending: 0,
55
- errored: 0,
56
- counter: 0,
57
- }))
58
-
59
- cache.set(hostname, val)
60
-
61
- resolve([null, val])
62
- }
63
- })
64
- })
65
- promises.set(hostname, promise)
66
- }
67
- return promise
68
- }
69
-
70
- return async (opts, handler) => {
71
- if (!opts || !opts.dns || !opts.origin) {
72
- return dispatch(opts, handler)
73
- }
74
-
75
- const origin = new URL(opts.origin)
76
-
77
- if (net.isIP(origin.hostname)) {
78
- return dispatch(opts, handler)
79
- }
80
-
81
- try {
82
- const { host, hostname } = origin
83
-
84
- const now = getFastNow()
85
-
86
- const logger = opts.dns.logger ?? opts.logger
87
-
88
- let records = cache.get(hostname)
89
-
90
- if (records == null || records.every((x) => x.expires < now)) {
91
- const [err, val] = await resolve(hostname, { logger })
92
-
93
- if (err) {
94
- throw err
95
- }
96
-
97
- assert(val.every((x) => x.expires > 0))
98
-
99
- records = val
100
- } else if (records.some((x) => x.expires < now + 1e3)) {
101
- resolve(hostname, { logger })
102
- }
103
-
104
- records.sort(
105
- (a, b) => a.errored - b.errored || a.pending - b.pending || a.counter - b.counter,
106
- )
107
-
108
- const record = records.find((x) => x.expires >= now)
109
-
110
- if (!record) {
111
- throw Object.assign(new Error(`No available DNS records found for ${hostname}`), {
112
- code: 'ENOTFOUND',
113
- })
114
- }
115
-
116
- origin.hostname = record.address
117
-
118
- dispatch(
119
- { ...opts, origin, headers: { ...opts.headers, host } },
120
- new Handler(handler, (err, statusCode) => {
121
- record.pending--
122
-
123
- if (err != null && err.name !== 'AbortError') {
124
- record.expires = 0
125
- } else if (statusCode != null && statusCode >= 500) {
126
- record.errored++
127
- }
128
- }),
129
- )
130
-
131
- record.counter++
132
- record.pending++
133
- } catch (err) {
134
- handler.onError(err)
135
- }
136
- }
137
- }
@@ -1,82 +0,0 @@
1
- import { once } from 'node:events'
2
- import http from 'node:http'
3
- import { test } from 'tap'
4
- import { request } from '../index.js'
5
-
6
- test('retry destroy pre response', async (t) => {
7
- const { body, statusCode } = await request(`http://google.com`)
8
- await body.dump()
9
- t.equal(statusCode, 200)
10
- t.end()
11
- })
12
-
13
- test('expire & retry on error', async (t) => {
14
- t.plan(3)
15
-
16
- const server = http
17
- .createServer((req, res) => {
18
- res.end()
19
- })
20
- .listen(0)
21
- t.teardown(server.close.bind(server))
22
-
23
- await once(server, 'listening')
24
-
25
- let counter = 0
26
- const { body } = await request(`http://asd.com:${server.address().port}`, {
27
- dns: {
28
- resolve4(hostname, opts, callback) {
29
- t.pass()
30
- if (counter++ === 0) {
31
- process.nextTick(callback, null, [{ address: '11.9.9.9', ttl: 600 }])
32
- } else {
33
- process.nextTick(callback, null, [{ address: '127.0.0.1', ttl: 600 }])
34
- }
35
- },
36
- },
37
- retry: 2,
38
- })
39
- await body.dump()
40
-
41
- t.pass()
42
- })
43
-
44
- test('expire on error', async (t) => {
45
- t.plan(2)
46
-
47
- const server = http
48
- .createServer((req, res) => {
49
- res.end()
50
- })
51
- .listen(0)
52
- t.teardown(server.close.bind(server))
53
-
54
- await once(server, 'listening')
55
-
56
- try {
57
- const { body } = await request(`http://123.com:${server.address().port}`, {
58
- dns: {
59
- resolve4(hostname, opts, callback) {
60
- process.nextTick(callback, null, [{ address: '10.9.9.9', ttl: 600 }])
61
- },
62
- },
63
- retry: false,
64
- })
65
- await body.dump()
66
- } catch (err) {
67
- t.equal(err.code, 'UND_ERR_CONNECT_TIMEOUT')
68
- }
69
-
70
- const { body } = await request(`http://123.com:${server.address().port}`, {
71
- dns: {
72
- resolve4(hostname, opts, callback) {
73
- process.nextTick(callback, null, [{ address: '127.0.0.1', ttl: 600 }])
74
- },
75
- },
76
- retry: false,
77
- })
78
- await body.dump()
79
-
80
- console.error('### 4')
81
- t.pass()
82
- })