@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 +0 -3
- package/lib/interceptor/cache.js +15 -3
- package/package.json +1 -1
- package/lib/interceptor/dns.js +0 -137
- package/lib/interceptor/dns.test.js +0 -82
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
|
},
|
package/lib/interceptor/cache.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
package/lib/interceptor/dns.js
DELETED
|
@@ -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
|
-
})
|