@fastify/reply-from 7.0.0
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/dependabot.yml +13 -0
- package/.github/stale.yml +21 -0
- package/.github/workflows/ci.yml +60 -0
- package/.taprc +5 -0
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/example.js +36 -0
- package/index.d.ts +99 -0
- package/index.js +262 -0
- package/lib/request.js +288 -0
- package/lib/utils.js +81 -0
- package/package.json +70 -0
- package/test/async-route-handler.js +50 -0
- package/test/base-get.js +48 -0
- package/test/base-path.js +38 -0
- package/test/base-querystring.js +48 -0
- package/test/build-url.js +69 -0
- package/test/core-with-path-in-base.js +50 -0
- package/test/custom-undici-instance.js +77 -0
- package/test/fastify-multipart-incompatibility.js +90 -0
- package/test/fixtures/fastify.cert +19 -0
- package/test/fixtures/fastify.key +27 -0
- package/test/fixtures/file.txt +1 -0
- package/test/full-get.js +45 -0
- package/test/full-https-get.js +55 -0
- package/test/full-post-extended-content-type.js +59 -0
- package/test/full-post-http2.js +49 -0
- package/test/full-post-stream-core.js +66 -0
- package/test/full-post-stream.js +64 -0
- package/test/full-post.js +56 -0
- package/test/full-querystring-rewrite-option-complex.js +48 -0
- package/test/full-querystring-rewrite-option-function.js +51 -0
- package/test/full-querystring-rewrite-option.js +48 -0
- package/test/full-querystring-rewrite-string.js +46 -0
- package/test/full-querystring-rewrite.js +46 -0
- package/test/full-querystring.js +46 -0
- package/test/full-rewrite-body-content-type.js +59 -0
- package/test/full-rewrite-body-http.js +63 -0
- package/test/full-rewrite-body-to-empty-string.js +61 -0
- package/test/full-rewrite-body-to-null.js +61 -0
- package/test/full-rewrite-body.js +61 -0
- package/test/get-upstream-http.js +70 -0
- package/test/get-upstream-undici.js +45 -0
- package/test/get-with-body.js +55 -0
- package/test/head-with-body.js +57 -0
- package/test/host-header.js +67 -0
- package/test/http-agents.js +55 -0
- package/test/http-http2.js +50 -0
- package/test/http-invalid-target.js +35 -0
- package/test/http-retry.js +109 -0
- package/test/http-timeout.js +56 -0
- package/test/http2-http2.js +57 -0
- package/test/http2-https.js +80 -0
- package/test/http2-invalid-target.js +36 -0
- package/test/http2-target-crash.js +53 -0
- package/test/http2-target-multi-crash.js +60 -0
- package/test/http2-timeout.js +92 -0
- package/test/https-agents.js +67 -0
- package/test/index.test-d.ts +136 -0
- package/test/modifyCoreObjects-false.js +48 -0
- package/test/no-body-opts-with-get.js +49 -0
- package/test/no-body-opts-with-head.js +51 -0
- package/test/no-stream-body-option.js +58 -0
- package/test/on-error.js +62 -0
- package/test/onResponse.js +48 -0
- package/test/padded-body.js +64 -0
- package/test/post-formbody.js +59 -0
- package/test/post-plain-text.js +56 -0
- package/test/post-with-custom-encoded-contenttype.js +65 -0
- package/test/retry-on-503.js +101 -0
- package/test/rewrite-headers.js +52 -0
- package/test/rewrite-request-headers.js +47 -0
- package/test/transform-body.js +61 -0
- package/test/undici-agent.js +77 -0
- package/test/undici-body.js +70 -0
- package/test/undici-options.js +73 -0
- package/test/undici-retry.js +109 -0
- package/test/undici-timeout-body-partial.js +60 -0
- package/test/undici-timeout-body.js +63 -0
- package/test/undici-timeout.js +61 -0
- package/test/undici-with-path-in-base.js +50 -0
- package/test/undici.js +50 -0
- package/test/unexpected-error.js +46 -0
- package/test/unix-http-undici-from.js +51 -0
- package/test/unix-http-undici.js +62 -0
- package/test/unix-http.js +65 -0
- package/test/unix-https-undici.js +71 -0
- package/test/unix-https.js +71 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const got = require('got')
|
|
8
|
+
|
|
9
|
+
function createTargetServer (withRetryAfterHeader, stopAfter = 1) {
|
|
10
|
+
let requestCount = 0
|
|
11
|
+
return http.createServer((req, res) => {
|
|
12
|
+
if (requestCount++ < stopAfter) {
|
|
13
|
+
res.statusCode = 503
|
|
14
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
15
|
+
if (withRetryAfterHeader) {
|
|
16
|
+
res.setHeader('Retry-After', 100)
|
|
17
|
+
}
|
|
18
|
+
return res.end('This Service is Unavailable')
|
|
19
|
+
}
|
|
20
|
+
res.statusCode = 205
|
|
21
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
22
|
+
return res.end(`Hello World ${requestCount}!`)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test('Should retry on 503 HTTP error', async function (t) {
|
|
27
|
+
t.plan(3)
|
|
28
|
+
const target = createTargetServer()
|
|
29
|
+
await target.listen(0)
|
|
30
|
+
t.teardown(target.close.bind(target))
|
|
31
|
+
|
|
32
|
+
const instance = Fastify()
|
|
33
|
+
|
|
34
|
+
instance.register(From, {
|
|
35
|
+
base: `http://localhost:${target.address().port}`
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
instance.get('/', (request, reply) => {
|
|
39
|
+
reply.from()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
t.teardown(instance.close.bind(instance))
|
|
43
|
+
await instance.listen(0)
|
|
44
|
+
|
|
45
|
+
const res = await got.get(`http://localhost:${instance.server.address().port}`, { retry: 0 })
|
|
46
|
+
t.equal(res.headers['content-type'], 'text/plain')
|
|
47
|
+
t.equal(res.statusCode, 205)
|
|
48
|
+
t.equal(res.body.toString(), 'Hello World 2!')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('Should retry on 503 HTTP error with Retry-After response header', async function (t) {
|
|
52
|
+
t.plan(3)
|
|
53
|
+
const target = createTargetServer(true)
|
|
54
|
+
await target.listen(0)
|
|
55
|
+
t.teardown(target.close.bind(target))
|
|
56
|
+
|
|
57
|
+
const instance = Fastify()
|
|
58
|
+
|
|
59
|
+
instance.register(From, {
|
|
60
|
+
base: `http://localhost:${target.address().port}`
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
instance.get('/', (request, reply) => {
|
|
64
|
+
reply.from()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
t.teardown(instance.close.bind(instance))
|
|
68
|
+
await instance.listen(0)
|
|
69
|
+
|
|
70
|
+
const res = await got.get(`http://localhost:${instance.server.address().port}`, { retry: 0 })
|
|
71
|
+
t.equal(res.headers['content-type'], 'text/plain')
|
|
72
|
+
t.equal(res.statusCode, 205)
|
|
73
|
+
t.equal(res.body.toString(), 'Hello World 2!')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('Should abort if server is always returning 503', async function (t) {
|
|
77
|
+
t.plan(2)
|
|
78
|
+
const target = createTargetServer(true, Number.MAX_SAFE_INTEGER)
|
|
79
|
+
await target.listen(0)
|
|
80
|
+
t.teardown(target.close.bind(target))
|
|
81
|
+
|
|
82
|
+
const instance = Fastify()
|
|
83
|
+
|
|
84
|
+
instance.register(From, {
|
|
85
|
+
base: `http://localhost:${target.address().port}`
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
instance.get('/', (request, reply) => {
|
|
89
|
+
reply.from()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
t.teardown(instance.close.bind(instance))
|
|
93
|
+
await instance.listen(0)
|
|
94
|
+
try {
|
|
95
|
+
await got.get(`http://localhost:${instance.server.address().port}`, { retry: 0 })
|
|
96
|
+
t.fail()
|
|
97
|
+
} catch (err) {
|
|
98
|
+
t.equal(err.response.statusCode, 503)
|
|
99
|
+
t.equal(err.response.body.toString(), 'This Service is Unavailable')
|
|
100
|
+
}
|
|
101
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
|
|
9
|
+
const instance = Fastify()
|
|
10
|
+
instance.register(From)
|
|
11
|
+
|
|
12
|
+
t.plan(10)
|
|
13
|
+
t.teardown(instance.close.bind(instance))
|
|
14
|
+
|
|
15
|
+
const target = http.createServer((req, res) => {
|
|
16
|
+
t.pass('request proxied')
|
|
17
|
+
t.equal(req.method, 'GET')
|
|
18
|
+
res.statusCode = 205
|
|
19
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
20
|
+
res.setHeader('x-my-header', 'hello!')
|
|
21
|
+
res.end('hello world')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
instance.get('/', (request, reply) => {
|
|
25
|
+
reply.from(`http://localhost:${target.address().port}`, {
|
|
26
|
+
rewriteHeaders: (headers, req) => {
|
|
27
|
+
t.pass('rewriteHeaders called')
|
|
28
|
+
return {
|
|
29
|
+
'content-type': headers['content-type'],
|
|
30
|
+
'x-another-header': 'so headers!'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
t.teardown(target.close.bind(target))
|
|
37
|
+
|
|
38
|
+
instance.listen(0, (err) => {
|
|
39
|
+
t.error(err)
|
|
40
|
+
|
|
41
|
+
target.listen(0, (err) => {
|
|
42
|
+
t.error(err)
|
|
43
|
+
|
|
44
|
+
get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
|
|
45
|
+
t.error(err)
|
|
46
|
+
t.equal(res.headers['content-type'], 'text/plain')
|
|
47
|
+
t.equal(res.headers['x-another-header'], 'so headers!')
|
|
48
|
+
t.notOk(res.headers['x-my-header'])
|
|
49
|
+
t.equal(res.statusCode, 205)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
|
|
9
|
+
const instance = Fastify()
|
|
10
|
+
instance.register(From)
|
|
11
|
+
|
|
12
|
+
t.plan(9)
|
|
13
|
+
t.teardown(instance.close.bind(instance))
|
|
14
|
+
|
|
15
|
+
const target = http.createServer((req, res) => {
|
|
16
|
+
t.pass('request proxied')
|
|
17
|
+
t.equal(req.method, 'GET')
|
|
18
|
+
res.statusCode = 205
|
|
19
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
20
|
+
res.end(req.headers.host)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
instance.get('/', (request, reply) => {
|
|
24
|
+
reply.from(`http://localhost:${target.address().port}`, {
|
|
25
|
+
rewriteRequestHeaders: (originalReq, headers) => {
|
|
26
|
+
t.pass('rewriteRequestHeaders called')
|
|
27
|
+
return Object.assign(headers, { host: 'host-override' })
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
t.teardown(target.close.bind(target))
|
|
33
|
+
|
|
34
|
+
instance.listen(0, (err) => {
|
|
35
|
+
t.error(err)
|
|
36
|
+
|
|
37
|
+
target.listen(0, (err) => {
|
|
38
|
+
t.error(err)
|
|
39
|
+
|
|
40
|
+
get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
|
|
41
|
+
t.error(err)
|
|
42
|
+
t.equal(res.headers['content-type'], 'text/plain')
|
|
43
|
+
t.equal(res.statusCode, 205)
|
|
44
|
+
t.equal(data.toString(), 'host-override')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
const Transform = require('stream').Transform
|
|
9
|
+
|
|
10
|
+
const instance = Fastify()
|
|
11
|
+
instance.register(From)
|
|
12
|
+
|
|
13
|
+
t.plan(9)
|
|
14
|
+
t.teardown(instance.close.bind(instance))
|
|
15
|
+
|
|
16
|
+
const target = http.createServer((req, res) => {
|
|
17
|
+
t.pass('request proxied')
|
|
18
|
+
t.equal(req.method, 'GET')
|
|
19
|
+
res.statusCode = 205
|
|
20
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
21
|
+
res.setHeader('x-my-header', 'hello!')
|
|
22
|
+
res.end('hello world')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
instance.get('/', (request, reply) => {
|
|
26
|
+
reply.from(`http://localhost:${target.address().port}`, {
|
|
27
|
+
onResponse: (request, reply, res) => {
|
|
28
|
+
reply.send(
|
|
29
|
+
res.pipe(
|
|
30
|
+
new Transform({
|
|
31
|
+
transform: function (chunk, enc, cb) {
|
|
32
|
+
this.push(chunk.toString().toUpperCase())
|
|
33
|
+
cb()
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
t.teardown(target.close.bind(target))
|
|
43
|
+
|
|
44
|
+
instance.listen(0, err => {
|
|
45
|
+
t.error(err)
|
|
46
|
+
|
|
47
|
+
target.listen(0, err => {
|
|
48
|
+
t.error(err)
|
|
49
|
+
|
|
50
|
+
get(
|
|
51
|
+
`http://localhost:${instance.server.address().port}`,
|
|
52
|
+
(err, res, data) => {
|
|
53
|
+
t.error(err)
|
|
54
|
+
t.equal(res.headers['content-type'], 'text/plain')
|
|
55
|
+
t.equal(res.headers['x-my-header'], 'hello!')
|
|
56
|
+
t.equal(res.statusCode, 205)
|
|
57
|
+
t.equal(data.toString(), 'HELLO WORLD')
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const proxyquire = require('proxyquire')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
const undici = require('undici')
|
|
9
|
+
const { getUndiciOptions } = require('../lib/request')
|
|
10
|
+
|
|
11
|
+
t.plan(10)
|
|
12
|
+
|
|
13
|
+
const instance = Fastify()
|
|
14
|
+
t.teardown(instance.close.bind(instance))
|
|
15
|
+
|
|
16
|
+
const target = http.createServer((req, res) => {
|
|
17
|
+
res.statusCode = 200
|
|
18
|
+
res.end('hello world')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
instance.get('/', (request, reply) => {
|
|
22
|
+
reply.from()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
t.teardown(target.close.bind(target))
|
|
26
|
+
|
|
27
|
+
target.listen(0, err => {
|
|
28
|
+
t.error(err)
|
|
29
|
+
let poolCreation = 0
|
|
30
|
+
|
|
31
|
+
const From = proxyquire('..', {
|
|
32
|
+
'./lib/request.js': proxyquire('../lib/request.js', {
|
|
33
|
+
undici: proxyquire('undici', {
|
|
34
|
+
'./lib/agent': proxyquire('undici/lib/agent.js', {
|
|
35
|
+
'./pool': class Pool extends undici.Pool {
|
|
36
|
+
constructor (url, options) {
|
|
37
|
+
super(url, options)
|
|
38
|
+
poolCreation++
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
instance.register(From, {
|
|
47
|
+
base: `http://localhost:${target.address().port}`,
|
|
48
|
+
undici: buildUndiciOptions()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
instance.listen(0, err => {
|
|
52
|
+
t.error(err)
|
|
53
|
+
|
|
54
|
+
get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
|
|
55
|
+
t.error(err)
|
|
56
|
+
t.equal(res.statusCode, 200)
|
|
57
|
+
t.equal(data.toString(), 'hello world')
|
|
58
|
+
t.equal(poolCreation, 1)
|
|
59
|
+
|
|
60
|
+
get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
|
|
61
|
+
t.error(err)
|
|
62
|
+
t.equal(res.statusCode, 200)
|
|
63
|
+
t.equal(data.toString(), 'hello world')
|
|
64
|
+
t.equal(poolCreation, 1)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
function buildUndiciOptions () {
|
|
71
|
+
return getUndiciOptions({
|
|
72
|
+
connections: 42,
|
|
73
|
+
pipelining: 24,
|
|
74
|
+
keepAliveTimeout: 4242,
|
|
75
|
+
strictContentLength: false
|
|
76
|
+
})
|
|
77
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const From = require('..')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
|
|
9
|
+
const instance = Fastify()
|
|
10
|
+
|
|
11
|
+
t.plan(9)
|
|
12
|
+
t.teardown(instance.close.bind(instance))
|
|
13
|
+
|
|
14
|
+
const bodyString = JSON.stringify({ hello: 'world' })
|
|
15
|
+
|
|
16
|
+
const parsedLength = Buffer.byteLength(bodyString)
|
|
17
|
+
|
|
18
|
+
const target = http.createServer((req, res) => {
|
|
19
|
+
t.pass('request proxied')
|
|
20
|
+
t.equal(req.method, 'POST')
|
|
21
|
+
t.equal(req.headers['content-type'], 'application/json')
|
|
22
|
+
t.same(req.headers['content-length'], parsedLength)
|
|
23
|
+
let data = ''
|
|
24
|
+
req.setEncoding('utf8')
|
|
25
|
+
req.on('data', (d) => {
|
|
26
|
+
data += d
|
|
27
|
+
})
|
|
28
|
+
req.on('end', () => {
|
|
29
|
+
t.same(JSON.parse(data), { hello: 'world' })
|
|
30
|
+
res.statusCode = 200
|
|
31
|
+
res.setHeader('content-type', 'application/json')
|
|
32
|
+
res.end(JSON.stringify({ something: 'else' }))
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
instance.post('/', (request, reply) => {
|
|
37
|
+
reply.from(`http://localhost:${target.address().port}`)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
t.teardown(target.close.bind(target))
|
|
41
|
+
|
|
42
|
+
target.listen(0, (err) => {
|
|
43
|
+
t.error(err)
|
|
44
|
+
|
|
45
|
+
instance.addContentTypeParser('application/json', function (req, payload, done) {
|
|
46
|
+
done(null, payload)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
instance.register(From, {
|
|
50
|
+
base: `http://localhost:${target.address().port}`,
|
|
51
|
+
undici: true
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
instance.listen(0, (err) => {
|
|
55
|
+
t.error(err)
|
|
56
|
+
|
|
57
|
+
get({
|
|
58
|
+
url: `http://localhost:${instance.server.address().port}`,
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'content-type': 'application/json'
|
|
62
|
+
},
|
|
63
|
+
body: bodyString
|
|
64
|
+
}, (err, res, data) => {
|
|
65
|
+
t.error(err)
|
|
66
|
+
const parsed = JSON.parse(data)
|
|
67
|
+
t.same(parsed, { something: 'else' })
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const Fastify = require('fastify')
|
|
5
|
+
const proxyquire = require('proxyquire')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const get = require('simple-get').concat
|
|
8
|
+
const undici = require('undici')
|
|
9
|
+
const { getUndiciOptions } = require('../lib/request')
|
|
10
|
+
|
|
11
|
+
const instance = Fastify()
|
|
12
|
+
|
|
13
|
+
t.plan(6)
|
|
14
|
+
t.teardown(instance.close.bind(instance))
|
|
15
|
+
|
|
16
|
+
const target = http.createServer((req, res) => {
|
|
17
|
+
res.statusCode = 200
|
|
18
|
+
res.end('hello world')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
instance.get('/', (request, reply) => {
|
|
22
|
+
reply.from()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
t.teardown(target.close.bind(target))
|
|
26
|
+
|
|
27
|
+
target.listen(0, err => {
|
|
28
|
+
t.error(err)
|
|
29
|
+
|
|
30
|
+
const From = proxyquire('..', {
|
|
31
|
+
'./lib/request.js': proxyquire('../lib/request.js', {
|
|
32
|
+
undici: undiciProxy
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
instance.register(From, {
|
|
37
|
+
base: `http://localhost:${target.address().port}`,
|
|
38
|
+
undici: buildUndiciOptions()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
instance.listen(0, err => {
|
|
42
|
+
t.error(err)
|
|
43
|
+
|
|
44
|
+
get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
|
|
45
|
+
t.error(err)
|
|
46
|
+
t.equal(res.statusCode, 200)
|
|
47
|
+
t.equal(data.toString(), 'hello world')
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
function undiciProxy () {}
|
|
53
|
+
undiciProxy.Agent = class Agent extends undici.Agent {
|
|
54
|
+
constructor (opts) {
|
|
55
|
+
super(opts)
|
|
56
|
+
t.strictSame(opts, buildUndiciOptions())
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
undiciProxy.Pool = class Pool extends undici.Pool {
|
|
60
|
+
constructor (url, options) {
|
|
61
|
+
super(url, options)
|
|
62
|
+
t.hasStrict(options, buildUndiciOptions())
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildUndiciOptions () {
|
|
67
|
+
return getUndiciOptions({
|
|
68
|
+
connections: 42,
|
|
69
|
+
pipelining: 24,
|
|
70
|
+
keepAliveTimeout: 4242,
|
|
71
|
+
strictContentLength: false
|
|
72
|
+
})
|
|
73
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Fastify = require('fastify')
|
|
4
|
+
const From = require('..')
|
|
5
|
+
const got = require('got')
|
|
6
|
+
const { test } = require('tap')
|
|
7
|
+
|
|
8
|
+
let retryNum = 1
|
|
9
|
+
|
|
10
|
+
const target = require('http').createServer(function (req, res) {
|
|
11
|
+
if (retryNum % 2 !== 0) {
|
|
12
|
+
req.socket.destroy()
|
|
13
|
+
} else {
|
|
14
|
+
res.statusCode = 200
|
|
15
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
16
|
+
res.end('hello world')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
retryNum += 1
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('Will retry', async function (t) {
|
|
23
|
+
t.teardown(() => { retryNum = 1 })
|
|
24
|
+
|
|
25
|
+
await target.listen(0)
|
|
26
|
+
t.teardown(target.close.bind(target))
|
|
27
|
+
|
|
28
|
+
const instance = Fastify()
|
|
29
|
+
|
|
30
|
+
instance.register(From, { undici: true })
|
|
31
|
+
|
|
32
|
+
instance.get('/', (request, reply) => {
|
|
33
|
+
reply.from(`http://localhost:${target.address().port}/`, {
|
|
34
|
+
retriesCount: 1,
|
|
35
|
+
onError: (reply, { error }) => {
|
|
36
|
+
t.equal(error.code, 'UND_ERR_SOCKET')
|
|
37
|
+
reply.send(error)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
await instance.listen(0)
|
|
43
|
+
t.teardown(instance.close.bind(instance))
|
|
44
|
+
|
|
45
|
+
const { statusCode } = await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 })
|
|
46
|
+
t.equal(statusCode, 200)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('will not retry', async function (t) {
|
|
50
|
+
t.teardown(() => { retryNum = 1 })
|
|
51
|
+
|
|
52
|
+
await target.listen(0)
|
|
53
|
+
t.teardown(target.close.bind(target))
|
|
54
|
+
|
|
55
|
+
const instance = Fastify()
|
|
56
|
+
|
|
57
|
+
instance.register(From, { undici: true })
|
|
58
|
+
|
|
59
|
+
instance.get('/', (request, reply) => {
|
|
60
|
+
reply.from(`http://localhost:${target.address().port}/`, {
|
|
61
|
+
retriesCount: 0,
|
|
62
|
+
onError: (reply, { error }) => {
|
|
63
|
+
t.equal(error.code, 'UND_ERR_SOCKET')
|
|
64
|
+
reply.send(error)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
await instance.listen(0)
|
|
70
|
+
t.teardown(instance.close.bind(instance))
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 })
|
|
74
|
+
t.fail()
|
|
75
|
+
} catch (err) {
|
|
76
|
+
t.equal(err.response.statusCode, 500)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('will not retry unsupported method', async function (t) {
|
|
81
|
+
t.teardown(() => { retryNum = 1 })
|
|
82
|
+
|
|
83
|
+
await target.listen(0)
|
|
84
|
+
t.teardown(target.close.bind(target))
|
|
85
|
+
|
|
86
|
+
const instance = Fastify()
|
|
87
|
+
|
|
88
|
+
instance.register(From, { undici: true, retryMethods: ['DELETE'] })
|
|
89
|
+
|
|
90
|
+
instance.get('/', (request, reply) => {
|
|
91
|
+
reply.from(`http://localhost:${target.address().port}/`, {
|
|
92
|
+
retriesCount: 1,
|
|
93
|
+
onError: (reply, { error }) => {
|
|
94
|
+
t.equal(error.code, 'UND_ERR_SOCKET')
|
|
95
|
+
reply.send(error)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
await instance.listen(0)
|
|
101
|
+
t.teardown(instance.close.bind(instance))
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 })
|
|
105
|
+
t.fail()
|
|
106
|
+
} catch (err) {
|
|
107
|
+
t.equal(err.response.statusCode, 500)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const http = require('http')
|
|
5
|
+
const Fastify = require('fastify')
|
|
6
|
+
const From = require('..')
|
|
7
|
+
const got = require('got')
|
|
8
|
+
const FakeTimers = require('@sinonjs/fake-timers')
|
|
9
|
+
|
|
10
|
+
const clock = FakeTimers.createClock()
|
|
11
|
+
|
|
12
|
+
t.autoend(false)
|
|
13
|
+
|
|
14
|
+
const target = http.createServer((req, res) => {
|
|
15
|
+
t.pass('request proxied')
|
|
16
|
+
req.on('data', () => undefined)
|
|
17
|
+
req.on('end', () => {
|
|
18
|
+
res.writeHead(200)
|
|
19
|
+
res.flushHeaders()
|
|
20
|
+
res.write('test')
|
|
21
|
+
clock.setTimeout(() => {
|
|
22
|
+
res.end()
|
|
23
|
+
t.end()
|
|
24
|
+
}, 1000)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
async function main () {
|
|
29
|
+
await target.listen(0)
|
|
30
|
+
|
|
31
|
+
const instance = Fastify()
|
|
32
|
+
t.teardown(instance.close.bind(instance))
|
|
33
|
+
t.teardown(target.close.bind(target))
|
|
34
|
+
|
|
35
|
+
instance.register(From, {
|
|
36
|
+
base: `http://localhost:${target.address().port}`,
|
|
37
|
+
undici: {
|
|
38
|
+
bodyTimeout: 100
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
instance.get('/', (request, reply) => {
|
|
43
|
+
reply.from()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await instance.listen(0)
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 })
|
|
50
|
+
} catch (err) {
|
|
51
|
+
t.equal(err.code, 'ECONNRESET')
|
|
52
|
+
t.equal(err.response.statusCode, 200)
|
|
53
|
+
clock.tick(1000)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
t.fail()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
main()
|