@fastify/reply-from 9.7.0 → 10.0.0-pre.fv5.1
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/.github/workflows/ci.yml +1 -1
- package/.taprc +1 -9
- package/README.md +23 -8
- package/index.js +8 -9
- package/lib/request.js +18 -6
- package/package.json +22 -22
- package/test/full-delete-http2.test.js +52 -0
- package/test/full-post-http2.test.js +2 -0
- package/test/full-querystring-rewrite-option-function-request.test.js +58 -0
- package/test/http-http2.test.js +2 -0
- package/test/http-timeout.test.js +0 -2
- package/test/http2-http2.test.js +2 -0
- package/test/http2-invalid-base.test.js +1 -1
- package/test/http2-timeout-disabled.test.js +85 -0
- package/test/http2-timeout.test.js +2 -0
- package/test/http2-unix-socket.test.js +1 -1
- package/test/on-error.test.js +11 -15
- package/test/onResponse.test.js +3 -2
- package/test/transform-body.test.js +1 -1
- package/test/undici-agent.test.js +1 -1
- package/test/undici-connect-timeout.test.js +8 -12
- package/test/undici-proxy-agent.test.js +81 -0
- package/test/undici-timeout-body-partial.test.js +14 -18
- package/test/undici-timeout-body.test.js +12 -16
- package/test/undici-timeout.test.js +11 -15
- package/types/index.d.ts +8 -2
- package/types/index.test-d.ts +21 -5
package/.github/workflows/ci.yml
CHANGED
package/.taprc
CHANGED
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ proxy.register(require('@fastify/reply-from'), {
|
|
|
82
82
|
|
|
83
83
|
#### `undici`
|
|
84
84
|
|
|
85
|
-
By default, [undici](https://github.com/
|
|
85
|
+
By default, [undici](https://github.com/nodejs/undici) will be used to perform the HTTP/1.1
|
|
86
86
|
requests. Enabling this flag should guarantee
|
|
87
87
|
20-50% more throughput.
|
|
88
88
|
|
|
@@ -102,6 +102,18 @@ proxy.register(require('@fastify/reply-from'), {
|
|
|
102
102
|
}
|
|
103
103
|
})
|
|
104
104
|
```
|
|
105
|
+
|
|
106
|
+
You can also include a proxy for the undici client:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
proxy.register(require('@fastify/reply-from'), {
|
|
110
|
+
base: 'http://localhost:3001/',
|
|
111
|
+
undici: {
|
|
112
|
+
proxy: 'http://my.proxy.server:8080',
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
105
117
|
See undici own options for more configurations.
|
|
106
118
|
|
|
107
119
|
You can also pass the plugin a custom instance:
|
|
@@ -325,17 +337,18 @@ const customRetryLogic = ({req, res, err, getDefaultRetry}: RetryDetails) => {
|
|
|
325
337
|
### `reply.from(source, [opts])`
|
|
326
338
|
|
|
327
339
|
The plugin decorates the
|
|
328
|
-
[`Reply`](https://
|
|
340
|
+
[`Reply`](https://fastify.dev/docs/latest/Reference/Reply)
|
|
329
341
|
instance with a `from` method, which will reply to the original request
|
|
330
342
|
__from the desired source__. The options allows to override any part of
|
|
331
343
|
the request or response being sent or received to/from the source.
|
|
332
344
|
|
|
333
345
|
**Note: If `base` is specified in plugin options, the `source` here should not override the host/origin.**
|
|
334
346
|
|
|
335
|
-
#### `onResponse(request, reply,
|
|
347
|
+
#### `onResponse(request, reply, response)`
|
|
348
|
+
|
|
349
|
+
Called when a HTTP response is received from the source. Passed the original source `request`, the in-progress reply to the source as `reply`, and the ongoing `response` from the upstream server.
|
|
336
350
|
|
|
337
|
-
|
|
338
|
-
The default behavior is `reply.send(res)`, which will be disabled if the
|
|
351
|
+
The default behavior is `reply.send(response.stream)`, which will be disabled if the
|
|
339
352
|
option is specified.
|
|
340
353
|
|
|
341
354
|
When replying with a body of a different length it is necessary to remove
|
|
@@ -350,6 +363,8 @@ the `content-length` header.
|
|
|
350
363
|
}
|
|
351
364
|
```
|
|
352
365
|
|
|
366
|
+
**Note**: `onResponse` is called after headers have already been sent. If you want to modify response headers, use the `rewriteHeaders` hook.
|
|
367
|
+
|
|
353
368
|
#### `onError(reply, error)`
|
|
354
369
|
|
|
355
370
|
Called when a HTTP response is received with error from the source.
|
|
@@ -428,7 +443,7 @@ server.register(fastifyHttpProxy, {
|
|
|
428
443
|
});
|
|
429
444
|
```
|
|
430
445
|
|
|
431
|
-
#### `queryString` or `queryString(search, reqUrl)`
|
|
446
|
+
#### `queryString` or `queryString(search, reqUrl, request)`
|
|
432
447
|
|
|
433
448
|
Replaces the original querystring of the request with what is specified.
|
|
434
449
|
This will be passed to
|
|
@@ -473,8 +488,8 @@ This library has:
|
|
|
473
488
|
|
|
474
489
|
- `timeout` for `http` set by default. The default value is 10 seconds (`10000`).
|
|
475
490
|
- `requestTimeout` & `sessionTimeout` for `http2` set by default.
|
|
476
|
-
- The default value for `requestTimeout` is 10 seconds (`10000`).
|
|
477
|
-
- The default value for `sessionTimeout` is 60 seconds (`60000`).
|
|
491
|
+
- The default value for `requestTimeout` is 10 seconds (`10000`), a value of 0 disables the timeout.
|
|
492
|
+
- The default value for `sessionTimeout` is 60 seconds (`60000`), a value of 0 disables the timeout.
|
|
478
493
|
|
|
479
494
|
When a timeout happens, [`504 Gateway Timeout`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504)
|
|
480
495
|
will be returned to the client.
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fp = require('fastify-plugin')
|
|
4
|
-
const {
|
|
4
|
+
const { LruMap } = require('toad-cache')
|
|
5
5
|
const querystring = require('fast-querystring')
|
|
6
6
|
const fastContentTypeParse = require('fast-content-type-parse')
|
|
7
7
|
const Stream = require('node:stream')
|
|
@@ -32,12 +32,13 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
|
|
|
32
32
|
const retryMethods = new Set(opts.retryMethods || [
|
|
33
33
|
'GET', 'HEAD', 'OPTIONS', 'TRACE'])
|
|
34
34
|
|
|
35
|
-
const cache = opts.disableCache ? undefined :
|
|
35
|
+
const cache = opts.disableCache ? undefined : new LruMap(opts.cacheURLs || 100)
|
|
36
36
|
const base = opts.base
|
|
37
37
|
const requestBuilt = buildRequest({
|
|
38
38
|
http: opts.http,
|
|
39
39
|
http2: opts.http2,
|
|
40
40
|
base,
|
|
41
|
+
sessionTimeout: opts.sessionTimeout,
|
|
41
42
|
undici: opts.undici,
|
|
42
43
|
globalAgent: opts.globalAgent,
|
|
43
44
|
destroyAgent: opts.destroyAgent
|
|
@@ -80,7 +81,7 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
|
|
|
80
81
|
const sourceHttp2 = req.httpVersionMajor === 2
|
|
81
82
|
const headers = sourceHttp2 ? filterPseudoHeaders(req.headers) : { ...req.headers }
|
|
82
83
|
headers.host = url.host
|
|
83
|
-
const qs = getQueryString(url.search, req.url, opts)
|
|
84
|
+
const qs = getQueryString(url.search, req.url, opts, this.request)
|
|
84
85
|
let body = ''
|
|
85
86
|
|
|
86
87
|
if (opts.body !== undefined) {
|
|
@@ -200,7 +201,7 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
|
|
|
200
201
|
}
|
|
201
202
|
this.code(res.statusCode)
|
|
202
203
|
if (onResponse) {
|
|
203
|
-
onResponse(this.request, this, res
|
|
204
|
+
onResponse(this.request, this, res)
|
|
204
205
|
} else {
|
|
205
206
|
this.send(res.stream)
|
|
206
207
|
}
|
|
@@ -227,9 +228,9 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
|
|
|
227
228
|
name: '@fastify/reply-from'
|
|
228
229
|
})
|
|
229
230
|
|
|
230
|
-
function getQueryString (search, reqUrl, opts) {
|
|
231
|
+
function getQueryString (search, reqUrl, opts, request) {
|
|
231
232
|
if (typeof opts.queryString === 'function') {
|
|
232
|
-
return '?' + opts.queryString(search, reqUrl)
|
|
233
|
+
return '?' + opts.queryString(search, reqUrl, request)
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
if (opts.queryString) {
|
|
@@ -266,9 +267,7 @@ function onErrorDefault (reply, { error }) {
|
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
function isFastifyMultipartRegistered (fastify) {
|
|
269
|
-
|
|
270
|
-
// It is used to be compatible with @fastify/multipart@<=7.3.0
|
|
271
|
-
return (fastify.hasContentTypeParser('multipart') || fastify.hasContentTypeParser('multipart/form-data')) && fastify.hasRequestDecorator('multipart')
|
|
270
|
+
return fastify.hasContentTypeParser('multipart/form-data')
|
|
272
271
|
}
|
|
273
272
|
|
|
274
273
|
function createRequestRetry (requestImpl, reply, retryHandler) {
|
package/lib/request.js
CHANGED
|
@@ -3,7 +3,7 @@ const http = require('node:http')
|
|
|
3
3
|
const https = require('node:https')
|
|
4
4
|
const querystring = require('node:querystring')
|
|
5
5
|
const eos = require('end-of-stream')
|
|
6
|
-
const
|
|
6
|
+
const { pipeline } = require('node:stream')
|
|
7
7
|
const undici = require('undici')
|
|
8
8
|
const { stripHttp1ConnectionHeaders } = require('./utils')
|
|
9
9
|
const http2 = require('node:http2')
|
|
@@ -83,7 +83,11 @@ function buildRequest (opts) {
|
|
|
83
83
|
} else if (isUndiciInstance(opts.undici)) {
|
|
84
84
|
undiciInstance = opts.undici
|
|
85
85
|
} else if (!globalAgent) {
|
|
86
|
-
|
|
86
|
+
if (undiciOpts.proxy) {
|
|
87
|
+
undiciAgent = new undici.ProxyAgent(getUndiciProxyOptions(opts.undici))
|
|
88
|
+
} else {
|
|
89
|
+
undiciAgent = new undici.Agent(getUndiciOptions(opts.undici))
|
|
90
|
+
}
|
|
87
91
|
} else {
|
|
88
92
|
undiciAgent = undici.getGlobalDispatcher()
|
|
89
93
|
}
|
|
@@ -204,7 +208,8 @@ function buildRequest (opts) {
|
|
|
204
208
|
...stripHttp1ConnectionHeaders(opts.headers)
|
|
205
209
|
}, http2Opts.requestOptions)
|
|
206
210
|
const isGet = opts.method === 'GET' || opts.method === 'get'
|
|
207
|
-
|
|
211
|
+
const isDelete = opts.method === 'DELETE' || opts.method === 'delete'
|
|
212
|
+
if (!isGet && !isDelete) {
|
|
208
213
|
end(req, opts.body, done)
|
|
209
214
|
}
|
|
210
215
|
req.setTimeout(http2Opts.requestTimeout, () => {
|
|
@@ -248,7 +253,7 @@ function end (req, body, cb) {
|
|
|
248
253
|
if (!body || typeof body === 'string' || body instanceof Uint8Array) {
|
|
249
254
|
req.end(body)
|
|
250
255
|
} else if (body.pipe) {
|
|
251
|
-
|
|
256
|
+
pipeline(body, req, err => {
|
|
252
257
|
if (err) cb(err)
|
|
253
258
|
})
|
|
254
259
|
} else {
|
|
@@ -267,10 +272,10 @@ function getHttp2Opts (opts) {
|
|
|
267
272
|
}
|
|
268
273
|
http2Opts.sessionOptions = http2Opts.sessionOptions || {}
|
|
269
274
|
|
|
270
|
-
if (
|
|
275
|
+
if (http2Opts.sessionTimeout === undefined) {
|
|
271
276
|
http2Opts.sessionTimeout = opts.sessionTimeout || 60000
|
|
272
277
|
}
|
|
273
|
-
if (
|
|
278
|
+
if (http2Opts.requestTimeout === undefined) {
|
|
274
279
|
http2Opts.requestTimeout = 10000
|
|
275
280
|
}
|
|
276
281
|
http2Opts.sessionOptions.rejectUnauthorized = http2Opts.sessionOptions.rejectUnauthorized || false
|
|
@@ -303,6 +308,13 @@ function getAgentOptions (opts) {
|
|
|
303
308
|
}
|
|
304
309
|
}
|
|
305
310
|
|
|
311
|
+
function getUndiciProxyOptions ({ proxy, ...opts }) {
|
|
312
|
+
if (typeof proxy === 'string' || proxy instanceof URL) {
|
|
313
|
+
return getUndiciOptions({ uri: proxy, ...opts })
|
|
314
|
+
}
|
|
315
|
+
return getUndiciOptions({ ...proxy, ...opts })
|
|
316
|
+
}
|
|
317
|
+
|
|
306
318
|
function getUndiciOptions (opts = {}) {
|
|
307
319
|
const res = {
|
|
308
320
|
pipelining: 1,
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/reply-from",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0-pre.fv5.1",
|
|
4
4
|
"description": "forward your HTTP request to another server, for fastify",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"lint": "standard | snazzy",
|
|
10
|
-
"lint:fix": "standard --fix",
|
|
10
|
+
"lint:fix": "standard --fix | snazzy",
|
|
11
11
|
"test": "npm run test:unit && npm run test:typescript",
|
|
12
12
|
"test:unit": "tap",
|
|
13
13
|
"test:typescript": "tsd"
|
|
@@ -30,35 +30,35 @@
|
|
|
30
30
|
"homepage": "https://github.com/fastify/fastify-reply-from#readme",
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@fastify/formbody": "^7.4.0",
|
|
33
|
-
"@fastify/multipart": "^
|
|
34
|
-
"@fastify/pre-commit": "^2.0
|
|
35
|
-
"@sinonjs/fake-timers": "^11.
|
|
36
|
-
"@types/node": "^20.
|
|
37
|
-
"@types/tap": "^15.0.
|
|
38
|
-
"fastify": "^4.
|
|
33
|
+
"@fastify/multipart": "^8.2.0",
|
|
34
|
+
"@fastify/pre-commit": "^2.1.0",
|
|
35
|
+
"@sinonjs/fake-timers": "^11.2.2",
|
|
36
|
+
"@types/node": "^20.11.30",
|
|
37
|
+
"@types/tap": "^15.0.11",
|
|
38
|
+
"fastify": "^4.26.2",
|
|
39
39
|
"form-data": "^4.0.0",
|
|
40
|
-
"got": "^11.8.
|
|
40
|
+
"got": "^11.8.6",
|
|
41
41
|
"h2url": "^0.2.0",
|
|
42
|
-
"msgpack5": "^6.0.
|
|
43
|
-
"nock": "^13.
|
|
42
|
+
"msgpack5": "^6.0.2",
|
|
43
|
+
"nock": "^13.5.4",
|
|
44
|
+
"proxy": "^2.1.1",
|
|
44
45
|
"proxyquire": "^2.1.3",
|
|
45
|
-
"semver": "^7.
|
|
46
|
+
"semver": "^7.6.0",
|
|
46
47
|
"simple-get": "^4.0.1",
|
|
47
48
|
"snazzy": "^9.0.0",
|
|
48
|
-
"split2": "^4.
|
|
49
|
-
"standard": "^17.
|
|
50
|
-
"tap": "^
|
|
51
|
-
"tsd": "^0.
|
|
49
|
+
"split2": "^4.2.0",
|
|
50
|
+
"standard": "^17.1.0",
|
|
51
|
+
"tap": "^18.7.2",
|
|
52
|
+
"tsd": "^0.31.0"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
|
-
"@fastify/error": "^3.
|
|
55
|
+
"@fastify/error": "^3.4.1",
|
|
55
56
|
"end-of-stream": "^1.4.4",
|
|
56
57
|
"fast-content-type-parse": "^1.1.0",
|
|
57
|
-
"fast-querystring": "^1.
|
|
58
|
-
"fastify-plugin": "^4.
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"undici": "^5.19.1"
|
|
58
|
+
"fast-querystring": "^1.1.2",
|
|
59
|
+
"fastify-plugin": "^4.5.1",
|
|
60
|
+
"toad-cache": "^3.7.0",
|
|
61
|
+
"undici": "^6.11.1"
|
|
62
62
|
},
|
|
63
63
|
"pre-commit": [
|
|
64
64
|
"lint",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const got = require('got')
|
|
7
|
+
|
|
8
|
+
test('http -> http2', async function (t) {
|
|
9
|
+
const instance = Fastify()
|
|
10
|
+
|
|
11
|
+
t.teardown(instance.close.bind(instance))
|
|
12
|
+
|
|
13
|
+
const target = Fastify({
|
|
14
|
+
http2: true
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
target.delete('/', (request, reply) => {
|
|
18
|
+
t.pass('request proxied')
|
|
19
|
+
reply.code(200).header('x-my-header', 'hello!').send({
|
|
20
|
+
hello: 'world'
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
instance.delete('/', (request, reply) => {
|
|
25
|
+
reply.from()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
t.teardown(target.close.bind(target))
|
|
29
|
+
|
|
30
|
+
await target.listen({ port: 0 })
|
|
31
|
+
|
|
32
|
+
instance.register(From, {
|
|
33
|
+
base: `http://localhost:${target.server.address().port}`,
|
|
34
|
+
http2: true
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
await instance.listen({ port: 0 })
|
|
38
|
+
|
|
39
|
+
const { headers, body, statusCode } = await got(
|
|
40
|
+
`http://localhost:${instance.server.address().port}`,
|
|
41
|
+
{
|
|
42
|
+
method: 'DELETE',
|
|
43
|
+
responseType: 'json'
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
t.equal(statusCode, 200)
|
|
47
|
+
t.equal(headers['x-my-header'], 'hello!')
|
|
48
|
+
t.match(headers['content-type'], /application\/json/)
|
|
49
|
+
t.same(body, { hello: 'world' })
|
|
50
|
+
instance.close()
|
|
51
|
+
target.close()
|
|
52
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const http = require('node:http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
const querystring = require('node:querystring')
|
|
9
|
+
|
|
10
|
+
const instance = Fastify()
|
|
11
|
+
|
|
12
|
+
instance.addHook('preHandler', (request, reply, done) => {
|
|
13
|
+
request.addedVal = 'test'
|
|
14
|
+
done()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
t.plan(10)
|
|
18
|
+
t.teardown(instance.close.bind(instance))
|
|
19
|
+
|
|
20
|
+
const target = http.createServer((req, res) => {
|
|
21
|
+
t.pass('request proxied')
|
|
22
|
+
t.equal(req.method, 'GET')
|
|
23
|
+
t.equal(req.url, '/world?q=test')
|
|
24
|
+
res.statusCode = 205
|
|
25
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
26
|
+
res.setHeader('x-my-header', 'hello!')
|
|
27
|
+
res.end('hello world')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
instance.get('/hello', (request, reply) => {
|
|
31
|
+
reply.from(`http://localhost:${target.address().port}/world?a=b`, {
|
|
32
|
+
queryString (search, reqUrl, request) {
|
|
33
|
+
return querystring.stringify({ q: request.addedVal })
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
t.teardown(target.close.bind(target))
|
|
39
|
+
|
|
40
|
+
target.listen({ port: 0 }, (err) => {
|
|
41
|
+
t.error(err)
|
|
42
|
+
|
|
43
|
+
instance.register(From)
|
|
44
|
+
|
|
45
|
+
instance.listen({ port: 0 }, (err) => {
|
|
46
|
+
t.error(err)
|
|
47
|
+
|
|
48
|
+
get(`http://localhost:${instance.server.address().port}/hello?a=b`, (err, res, data) => {
|
|
49
|
+
t.error(err)
|
|
50
|
+
t.equal(res.headers['content-type'], 'text/plain')
|
|
51
|
+
t.equal(res.headers['x-my-header'], 'hello!')
|
|
52
|
+
t.equal(res.statusCode, 205)
|
|
53
|
+
t.equal(data.toString(), 'hello world')
|
|
54
|
+
instance.close()
|
|
55
|
+
target.close()
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
package/test/http-http2.test.js
CHANGED
|
@@ -43,6 +43,8 @@ test('http -> http2', async (t) => {
|
|
|
43
43
|
t.equal(err.response.headers['x-my-header'], 'hello!')
|
|
44
44
|
t.match(err.response.headers['content-type'], /application\/json/)
|
|
45
45
|
t.same(JSON.parse(err.response.body), { hello: 'world' })
|
|
46
|
+
instance.close()
|
|
47
|
+
target.close()
|
|
46
48
|
return
|
|
47
49
|
}
|
|
48
50
|
|
package/test/http2-http2.test.js
CHANGED
|
@@ -7,7 +7,7 @@ const From = require('../index')
|
|
|
7
7
|
test('http2 invalid base', async (t) => {
|
|
8
8
|
const instance = Fastify()
|
|
9
9
|
|
|
10
|
-
await t.rejects(instance.register(From, {
|
|
10
|
+
await t.rejects(async () => instance.register(From, {
|
|
11
11
|
http2: { requestTimeout: 100 }
|
|
12
12
|
}), new Error('Option base is required when http2 is true'))
|
|
13
13
|
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const got = require('got')
|
|
7
|
+
|
|
8
|
+
test('http2 request timeout disabled', async (t) => {
|
|
9
|
+
const target = Fastify({ http2: true })
|
|
10
|
+
t.teardown(target.close.bind(target))
|
|
11
|
+
|
|
12
|
+
target.get('/', () => {
|
|
13
|
+
t.pass('request arrives')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
await target.listen({ port: 0 })
|
|
17
|
+
|
|
18
|
+
const instance = Fastify()
|
|
19
|
+
t.teardown(instance.close.bind(instance))
|
|
20
|
+
|
|
21
|
+
instance.register(From, {
|
|
22
|
+
base: `http://localhost:${target.server.address().port}`,
|
|
23
|
+
http2: { requestTimeout: 0, sessionTimeout: 16000 }
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
instance.get('/', (request, reply) => {
|
|
27
|
+
reply.from(`http://localhost:${target.server.address().port}/`)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
await instance.listen({ port: 0 })
|
|
31
|
+
|
|
32
|
+
const result = await Promise.race([
|
|
33
|
+
got.get(`http://localhost:${instance.server.address().port}/`, {
|
|
34
|
+
retry: 0
|
|
35
|
+
}),
|
|
36
|
+
new Promise(resolve => setTimeout(resolve, 11000, 'passed'))
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
// if we wait 11000 ms without a timeout error, we assume disabling the timeout worked
|
|
40
|
+
// 10000 ms is the default timeout
|
|
41
|
+
t.equal(result, 'passed')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('http2 session timeout disabled', async (t) => {
|
|
45
|
+
const target = Fastify({ http2: true })
|
|
46
|
+
|
|
47
|
+
target.get('/', () => {
|
|
48
|
+
t.pass('request arrives')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
await target.listen({ port: 0 })
|
|
52
|
+
|
|
53
|
+
const instance = Fastify()
|
|
54
|
+
|
|
55
|
+
instance.register(From, {
|
|
56
|
+
sessionTimeout: 3000,
|
|
57
|
+
destroyAgent: true,
|
|
58
|
+
base: `http://localhost:${target.server.address().port}`,
|
|
59
|
+
http2: { requestTimeout: 0, sessionTimeout: 0 }
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
instance.get('/', (request, reply) => {
|
|
63
|
+
reply.from(`http://localhost:${target.server.address().port}/`)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
await instance.listen({ port: 0 })
|
|
67
|
+
|
|
68
|
+
const request = got.get(`http://localhost:${instance.server.address().port}/`, {
|
|
69
|
+
retry: 0
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const result = await Promise.race([
|
|
73
|
+
request,
|
|
74
|
+
new Promise(resolve => setTimeout(resolve, 4000, 'passed'))
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
// clean up right after the timeout, otherwise test will hang
|
|
78
|
+
request.cancel()
|
|
79
|
+
target.close()
|
|
80
|
+
instance.close()
|
|
81
|
+
|
|
82
|
+
// if we wait 4000 ms without a timeout error, we assume disabling the session timeout for reply-from worked
|
|
83
|
+
// because we pass 3000 ms as session timeout to the Fastify options itself
|
|
84
|
+
t.equal(result, 'passed')
|
|
85
|
+
})
|
|
@@ -122,4 +122,6 @@ test('http2 sse removes request and session timeout test', async (t) => {
|
|
|
122
122
|
|
|
123
123
|
const { statusCode } = await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 })
|
|
124
124
|
t.equal(statusCode, 200)
|
|
125
|
+
instance.close()
|
|
126
|
+
target.close()
|
|
125
127
|
})
|
|
@@ -9,7 +9,7 @@ test('throw an error if http2 is used with a Unix socket destination', async t =
|
|
|
9
9
|
|
|
10
10
|
const instance = Fastify()
|
|
11
11
|
|
|
12
|
-
await t.rejects(instance.register(From, {
|
|
12
|
+
await t.rejects(async () => instance.register(From, {
|
|
13
13
|
base: 'unix+http://localhost:1337',
|
|
14
14
|
http2: { requestTimeout: 100 }
|
|
15
15
|
}), new Error('Unix socket destination is not supported when http2 is true'))
|
package/test/on-error.test.js
CHANGED
|
@@ -8,21 +8,19 @@ const FakeTimers = require('@sinonjs/fake-timers')
|
|
|
8
8
|
|
|
9
9
|
const clock = FakeTimers.createClock()
|
|
10
10
|
|
|
11
|
-
t.
|
|
11
|
+
t.test('on-error', async (t) => {
|
|
12
|
+
const target = Fastify()
|
|
13
|
+
t.teardown(target.close.bind(target))
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
t.
|
|
15
|
+
target.get('/', (request, reply) => {
|
|
16
|
+
t.pass('request arrives')
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
t.end()
|
|
22
|
-
}, 1000)
|
|
23
|
-
})
|
|
18
|
+
clock.setTimeout(() => {
|
|
19
|
+
reply.status(200).send('hello world')
|
|
20
|
+
t.end()
|
|
21
|
+
}, 1000)
|
|
22
|
+
})
|
|
24
23
|
|
|
25
|
-
async function main () {
|
|
26
24
|
await target.listen({ port: 0 })
|
|
27
25
|
|
|
28
26
|
const instance = Fastify()
|
|
@@ -63,6 +61,4 @@ async function main () {
|
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
t.fail()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
main()
|
|
64
|
+
})
|
package/test/onResponse.test.js
CHANGED
|
@@ -9,7 +9,7 @@ const get = require('simple-get').concat
|
|
|
9
9
|
const instance = Fastify()
|
|
10
10
|
instance.register(From)
|
|
11
11
|
|
|
12
|
-
t.plan(
|
|
12
|
+
t.plan(9)
|
|
13
13
|
t.teardown(instance.close.bind(instance))
|
|
14
14
|
|
|
15
15
|
const target = http.createServer((req, res) => {
|
|
@@ -22,8 +22,9 @@ const target = http.createServer((req, res) => {
|
|
|
22
22
|
instance.get('/', (request1, reply) => {
|
|
23
23
|
reply.from(`http://localhost:${target.address().port}`, {
|
|
24
24
|
onResponse: (request2, reply, res) => {
|
|
25
|
+
t.equal(res.statusCode, 200)
|
|
25
26
|
t.equal(request1.raw, request2.raw)
|
|
26
|
-
reply.send(res)
|
|
27
|
+
reply.send(res.stream)
|
|
27
28
|
}
|
|
28
29
|
})
|
|
29
30
|
})
|
|
@@ -26,7 +26,7 @@ instance.get('/', (request, reply) => {
|
|
|
26
26
|
reply.from(`http://localhost:${target.address().port}`, {
|
|
27
27
|
onResponse: (request, reply, res) => {
|
|
28
28
|
reply.send(
|
|
29
|
-
res.pipe(
|
|
29
|
+
res.stream.pipe(
|
|
30
30
|
new Transform({
|
|
31
31
|
transform: function (chunk, enc, cb) {
|
|
32
32
|
this.push(chunk.toString().toUpperCase())
|
|
@@ -31,7 +31,7 @@ target.listen({ port: 0 }, err => {
|
|
|
31
31
|
const From = proxyquire('..', {
|
|
32
32
|
'./lib/request.js': proxyquire('../lib/request.js', {
|
|
33
33
|
undici: proxyquire('undici', {
|
|
34
|
-
'./lib/agent': proxyquire('undici/lib/agent.js', {
|
|
34
|
+
'./lib/dispatcher/agent': proxyquire('undici/lib/dispatcher/agent.js', {
|
|
35
35
|
'./pool': class Pool extends undici.Pool {
|
|
36
36
|
constructor (url, options) {
|
|
37
37
|
super(url, options)
|
|
@@ -7,18 +7,16 @@ const Fastify = require('fastify')
|
|
|
7
7
|
const From = require('..')
|
|
8
8
|
const got = require('got')
|
|
9
9
|
|
|
10
|
-
t.
|
|
11
|
-
|
|
10
|
+
t.test('undici connect timeout', async (t) => {
|
|
12
11
|
// never connect
|
|
13
|
-
net.connect = function (options) {
|
|
14
|
-
|
|
15
|
-
}
|
|
12
|
+
net.connect = function (options) {
|
|
13
|
+
return new net.Socket(options)
|
|
14
|
+
}
|
|
16
15
|
|
|
17
|
-
const target = http.createServer((req, res) => {
|
|
18
|
-
|
|
19
|
-
})
|
|
16
|
+
const target = http.createServer((req, res) => {
|
|
17
|
+
t.fail('target never called')
|
|
18
|
+
})
|
|
20
19
|
|
|
21
|
-
async function main () {
|
|
22
20
|
t.plan(2)
|
|
23
21
|
await target.listen({ port: 0 })
|
|
24
22
|
|
|
@@ -53,6 +51,4 @@ async function main () {
|
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
t.fail()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
main()
|
|
54
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const { createServer } = require('node:http')
|
|
5
|
+
const Fastify = require('fastify')
|
|
6
|
+
const get = require('simple-get').concat
|
|
7
|
+
const { createProxy } = require('proxy')
|
|
8
|
+
const From = require('..')
|
|
9
|
+
|
|
10
|
+
const configFormat = {
|
|
11
|
+
string: (value) => value,
|
|
12
|
+
'url instance': (value) => new URL(value),
|
|
13
|
+
object: (value) => ({ uri: value })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
for (const [description, format] of Object.entries(configFormat)) {
|
|
17
|
+
test(`use undici ProxyAgent to connect through proxy - configured via ${description}`, async (t) => {
|
|
18
|
+
t.plan(5)
|
|
19
|
+
const target = await buildServer()
|
|
20
|
+
const proxy = await buildProxy()
|
|
21
|
+
t.teardown(target.close.bind(target))
|
|
22
|
+
t.teardown(proxy.close.bind(proxy))
|
|
23
|
+
|
|
24
|
+
const targetUrl = `http://localhost:${target.address().port}`
|
|
25
|
+
const proxyUrl = `http://localhost:${proxy.address().port}`
|
|
26
|
+
|
|
27
|
+
proxy.on('connect', () => {
|
|
28
|
+
t.ok(true, 'should connect to proxy')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
target.on('request', (req, res) => {
|
|
32
|
+
res.setHeader('content-type', 'application/json')
|
|
33
|
+
res.end(JSON.stringify({ hello: 'world' }))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const instance = Fastify()
|
|
37
|
+
t.teardown(instance.close.bind(instance))
|
|
38
|
+
|
|
39
|
+
instance.register(From, {
|
|
40
|
+
base: targetUrl,
|
|
41
|
+
undici: {
|
|
42
|
+
proxy: format(proxyUrl)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
instance.get('/', (request, reply) => {
|
|
47
|
+
reply.from()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const executionFlow = () => new Promise((resolve) => {
|
|
51
|
+
instance.listen({ port: 0 }, err => {
|
|
52
|
+
t.error(err)
|
|
53
|
+
|
|
54
|
+
get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
|
|
55
|
+
t.error(err)
|
|
56
|
+
t.same(res.statusCode, 200)
|
|
57
|
+
t.match(JSON.parse(data.toString()), { hello: 'world' })
|
|
58
|
+
resolve()
|
|
59
|
+
instance.close()
|
|
60
|
+
target.close()
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
await executionFlow()
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildServer () {
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
const server = createServer()
|
|
72
|
+
server.listen(0, () => resolve(server))
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildProxy () {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
const server = createProxy(createServer())
|
|
79
|
+
server.listen(0, () => resolve(server))
|
|
80
|
+
})
|
|
81
|
+
}
|
|
@@ -9,23 +9,21 @@ const FakeTimers = require('@sinonjs/fake-timers')
|
|
|
9
9
|
|
|
10
10
|
const clock = FakeTimers.createClock()
|
|
11
11
|
|
|
12
|
-
t.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
12
|
+
t.test('undici body timeout', async (t) => {
|
|
13
|
+
const target = http.createServer((req, res) => {
|
|
14
|
+
t.pass('request proxied')
|
|
15
|
+
req.on('data', () => undefined)
|
|
16
|
+
req.on('end', () => {
|
|
17
|
+
res.writeHead(200)
|
|
18
|
+
res.flushHeaders()
|
|
19
|
+
res.write('test')
|
|
20
|
+
clock.setTimeout(() => {
|
|
21
|
+
res.end()
|
|
22
|
+
t.end()
|
|
23
|
+
}, 1000)
|
|
24
|
+
})
|
|
25
25
|
})
|
|
26
|
-
})
|
|
27
26
|
|
|
28
|
-
async function main () {
|
|
29
27
|
await target.listen({ port: 0 })
|
|
30
28
|
|
|
31
29
|
const instance = Fastify()
|
|
@@ -55,6 +53,4 @@ async function main () {
|
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
t.fail()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
main()
|
|
56
|
+
})
|
|
@@ -9,21 +9,19 @@ const FakeTimers = require('@sinonjs/fake-timers')
|
|
|
9
9
|
|
|
10
10
|
const clock = FakeTimers.createClock()
|
|
11
11
|
|
|
12
|
-
t.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
12
|
+
t.test('undici body timeout', async (t) => {
|
|
13
|
+
const target = http.createServer((req, res) => {
|
|
14
|
+
t.pass('request proxied')
|
|
15
|
+
req.on('data', () => undefined)
|
|
16
|
+
req.on('end', () => {
|
|
17
|
+
res.flushHeaders()
|
|
18
|
+
clock.setTimeout(() => {
|
|
19
|
+
res.end()
|
|
20
|
+
t.end()
|
|
21
|
+
}, 1000)
|
|
22
|
+
})
|
|
23
23
|
})
|
|
24
|
-
})
|
|
25
24
|
|
|
26
|
-
async function main () {
|
|
27
25
|
await target.listen({ port: 0 })
|
|
28
26
|
|
|
29
27
|
const instance = Fastify()
|
|
@@ -58,6 +56,4 @@ async function main () {
|
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
t.fail()
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
main()
|
|
59
|
+
})
|
|
@@ -8,21 +8,19 @@ const FakeTimers = require('@sinonjs/fake-timers')
|
|
|
8
8
|
|
|
9
9
|
const clock = FakeTimers.createClock()
|
|
10
10
|
|
|
11
|
-
t.
|
|
11
|
+
t.test('undici request timeout', async (t) => {
|
|
12
|
+
const target = Fastify()
|
|
13
|
+
t.teardown(target.close.bind(target))
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
t.
|
|
15
|
+
target.get('/', (request, reply) => {
|
|
16
|
+
t.pass('request arrives')
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
t.end()
|
|
22
|
-
}, 1000)
|
|
23
|
-
})
|
|
18
|
+
clock.setTimeout(() => {
|
|
19
|
+
reply.status(200).send('hello world')
|
|
20
|
+
t.end()
|
|
21
|
+
}, 1000)
|
|
22
|
+
})
|
|
24
23
|
|
|
25
|
-
async function main () {
|
|
26
24
|
await target.listen({ port: 0 })
|
|
27
25
|
|
|
28
26
|
const instance = Fastify()
|
|
@@ -57,6 +55,4 @@ async function main () {
|
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
t.fail()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
main()
|
|
58
|
+
})
|
package/types/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
RequestOptions as SecureRequestOptions
|
|
29
29
|
} from "https";
|
|
30
30
|
import { Pool } from 'undici';
|
|
31
|
+
import { ProxyAgent } from 'undici';
|
|
31
32
|
|
|
32
33
|
declare module "fastify" {
|
|
33
34
|
interface FastifyReply {
|
|
@@ -40,13 +41,18 @@ declare module "fastify" {
|
|
|
40
41
|
|
|
41
42
|
type FastifyReplyFrom = FastifyPluginCallback<fastifyReplyFrom.FastifyReplyFromOptions>
|
|
42
43
|
declare namespace fastifyReplyFrom {
|
|
43
|
-
type QueryStringFunction = (
|
|
44
|
+
type QueryStringFunction = (
|
|
45
|
+
search: string | undefined,
|
|
46
|
+
reqUrl: string,
|
|
47
|
+
request: FastifyRequest<RequestGenericInterface, RawServerBase>
|
|
48
|
+
) => string;
|
|
44
49
|
|
|
45
50
|
export type RetryDetails = {
|
|
46
51
|
err: Error;
|
|
47
52
|
req: FastifyRequest<RequestGenericInterface, RawServerBase>;
|
|
48
53
|
res: FastifyReply<RawServerBase>;
|
|
49
54
|
attempt: number;
|
|
55
|
+
retriesCount: number;
|
|
50
56
|
getDefaultDelay: () => number | null;
|
|
51
57
|
}
|
|
52
58
|
export interface FastifyReplyFromHooks {
|
|
@@ -98,7 +104,7 @@ declare namespace fastifyReplyFrom {
|
|
|
98
104
|
disableCache?: boolean;
|
|
99
105
|
http?: HttpOptions;
|
|
100
106
|
http2?: Http2Options | boolean;
|
|
101
|
-
undici?: Pool.Options;
|
|
107
|
+
undici?: Pool.Options & { proxy?: string | URL | ProxyAgent.Options };
|
|
102
108
|
contentTypesToEncode?: string[];
|
|
103
109
|
retryMethods?: (HTTPMethods | 'TRACE')[];
|
|
104
110
|
maxRetriesOn503?: number;
|
package/types/index.test-d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fastify, { FastifyReply, FastifyRequest, RawServerBase, RequestGenericInterface } from "fastify";
|
|
1
|
+
import fastify, { FastifyReply, FastifyRequest, RawReplyDefaultExpression, RawServerBase, RequestGenericInterface } from "fastify";
|
|
2
2
|
import * as http from 'http';
|
|
3
3
|
import { IncomingHttpHeaders } from "http2";
|
|
4
4
|
import * as https from 'https';
|
|
@@ -38,7 +38,8 @@ const fullOptions: FastifyReplyFromOptions = {
|
|
|
38
38
|
disableCache: false,
|
|
39
39
|
undici: {
|
|
40
40
|
connections: 100,
|
|
41
|
-
pipelining: 10
|
|
41
|
+
pipelining: 10,
|
|
42
|
+
proxy: 'http://example2.com:8080'
|
|
42
43
|
},
|
|
43
44
|
contentTypesToEncode: ['application/x-www-form-urlencoded'],
|
|
44
45
|
retryMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE'],
|
|
@@ -47,7 +48,6 @@ const fullOptions: FastifyReplyFromOptions = {
|
|
|
47
48
|
globalAgent: false,
|
|
48
49
|
destroyAgent: true
|
|
49
50
|
};
|
|
50
|
-
tap.autoend(false);
|
|
51
51
|
|
|
52
52
|
async function main() {
|
|
53
53
|
const server = fastify();
|
|
@@ -60,6 +60,10 @@ async function main() {
|
|
|
60
60
|
|
|
61
61
|
server.register(replyFrom, fullOptions);
|
|
62
62
|
|
|
63
|
+
server.register(replyFrom, { undici: { proxy: new URL('http://example2.com:8080') } });
|
|
64
|
+
|
|
65
|
+
server.register(replyFrom, { undici: { proxy: { uri: 'http://example2.com:8080' } } });
|
|
66
|
+
|
|
63
67
|
server.get("/v1", (request, reply) => {
|
|
64
68
|
expectType<FastifyReply>(reply.from());
|
|
65
69
|
});
|
|
@@ -73,6 +77,12 @@ async function main() {
|
|
|
73
77
|
getUpstream(req, base) {
|
|
74
78
|
expectType<FastifyRequest<RequestGenericInterface, RawServerBase>>(req);
|
|
75
79
|
return base;
|
|
80
|
+
},
|
|
81
|
+
onResponse(request, reply, res) {
|
|
82
|
+
expectType<FastifyRequest<RequestGenericInterface, RawServerBase>>(request);
|
|
83
|
+
expectType<FastifyReply<RawServerBase>>(reply);
|
|
84
|
+
expectType<RawReplyDefaultExpression<RawServerBase>>(res);
|
|
85
|
+
expectType<number>(res.statusCode);
|
|
76
86
|
}
|
|
77
87
|
});
|
|
78
88
|
});
|
|
@@ -91,7 +101,7 @@ async function main() {
|
|
|
91
101
|
instance.get("/http2", (request, reply) => {
|
|
92
102
|
reply.from("/", {
|
|
93
103
|
method: "POST",
|
|
94
|
-
retryDelay: ({err, req, res, attempt, getDefaultDelay}) => {
|
|
104
|
+
retryDelay: ({err, req, res, attempt, retriesCount, getDefaultDelay }) => {
|
|
95
105
|
const defaultDelay = getDefaultDelay();
|
|
96
106
|
if (defaultDelay) return defaultDelay;
|
|
97
107
|
|
|
@@ -111,7 +121,13 @@ async function main() {
|
|
|
111
121
|
},
|
|
112
122
|
onError(reply: FastifyReply<RawServerBase>, error) {
|
|
113
123
|
return reply.send(error.error);
|
|
114
|
-
}
|
|
124
|
+
},
|
|
125
|
+
queryString(search, reqUrl, request) {
|
|
126
|
+
expectType<string | undefined>(search);
|
|
127
|
+
expectType<string>(reqUrl);
|
|
128
|
+
expectType<FastifyRequest<RequestGenericInterface, RawServerBase>>(request);
|
|
129
|
+
return '';
|
|
130
|
+
},
|
|
115
131
|
});
|
|
116
132
|
});
|
|
117
133
|
|