@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.
Files changed (88) hide show
  1. package/.github/dependabot.yml +13 -0
  2. package/.github/stale.yml +21 -0
  3. package/.github/workflows/ci.yml +60 -0
  4. package/.taprc +5 -0
  5. package/LICENSE +21 -0
  6. package/README.md +335 -0
  7. package/example.js +36 -0
  8. package/index.d.ts +99 -0
  9. package/index.js +262 -0
  10. package/lib/request.js +288 -0
  11. package/lib/utils.js +81 -0
  12. package/package.json +70 -0
  13. package/test/async-route-handler.js +50 -0
  14. package/test/base-get.js +48 -0
  15. package/test/base-path.js +38 -0
  16. package/test/base-querystring.js +48 -0
  17. package/test/build-url.js +69 -0
  18. package/test/core-with-path-in-base.js +50 -0
  19. package/test/custom-undici-instance.js +77 -0
  20. package/test/fastify-multipart-incompatibility.js +90 -0
  21. package/test/fixtures/fastify.cert +19 -0
  22. package/test/fixtures/fastify.key +27 -0
  23. package/test/fixtures/file.txt +1 -0
  24. package/test/full-get.js +45 -0
  25. package/test/full-https-get.js +55 -0
  26. package/test/full-post-extended-content-type.js +59 -0
  27. package/test/full-post-http2.js +49 -0
  28. package/test/full-post-stream-core.js +66 -0
  29. package/test/full-post-stream.js +64 -0
  30. package/test/full-post.js +56 -0
  31. package/test/full-querystring-rewrite-option-complex.js +48 -0
  32. package/test/full-querystring-rewrite-option-function.js +51 -0
  33. package/test/full-querystring-rewrite-option.js +48 -0
  34. package/test/full-querystring-rewrite-string.js +46 -0
  35. package/test/full-querystring-rewrite.js +46 -0
  36. package/test/full-querystring.js +46 -0
  37. package/test/full-rewrite-body-content-type.js +59 -0
  38. package/test/full-rewrite-body-http.js +63 -0
  39. package/test/full-rewrite-body-to-empty-string.js +61 -0
  40. package/test/full-rewrite-body-to-null.js +61 -0
  41. package/test/full-rewrite-body.js +61 -0
  42. package/test/get-upstream-http.js +70 -0
  43. package/test/get-upstream-undici.js +45 -0
  44. package/test/get-with-body.js +55 -0
  45. package/test/head-with-body.js +57 -0
  46. package/test/host-header.js +67 -0
  47. package/test/http-agents.js +55 -0
  48. package/test/http-http2.js +50 -0
  49. package/test/http-invalid-target.js +35 -0
  50. package/test/http-retry.js +109 -0
  51. package/test/http-timeout.js +56 -0
  52. package/test/http2-http2.js +57 -0
  53. package/test/http2-https.js +80 -0
  54. package/test/http2-invalid-target.js +36 -0
  55. package/test/http2-target-crash.js +53 -0
  56. package/test/http2-target-multi-crash.js +60 -0
  57. package/test/http2-timeout.js +92 -0
  58. package/test/https-agents.js +67 -0
  59. package/test/index.test-d.ts +136 -0
  60. package/test/modifyCoreObjects-false.js +48 -0
  61. package/test/no-body-opts-with-get.js +49 -0
  62. package/test/no-body-opts-with-head.js +51 -0
  63. package/test/no-stream-body-option.js +58 -0
  64. package/test/on-error.js +62 -0
  65. package/test/onResponse.js +48 -0
  66. package/test/padded-body.js +64 -0
  67. package/test/post-formbody.js +59 -0
  68. package/test/post-plain-text.js +56 -0
  69. package/test/post-with-custom-encoded-contenttype.js +65 -0
  70. package/test/retry-on-503.js +101 -0
  71. package/test/rewrite-headers.js +52 -0
  72. package/test/rewrite-request-headers.js +47 -0
  73. package/test/transform-body.js +61 -0
  74. package/test/undici-agent.js +77 -0
  75. package/test/undici-body.js +70 -0
  76. package/test/undici-options.js +73 -0
  77. package/test/undici-retry.js +109 -0
  78. package/test/undici-timeout-body-partial.js +60 -0
  79. package/test/undici-timeout-body.js +63 -0
  80. package/test/undici-timeout.js +61 -0
  81. package/test/undici-with-path-in-base.js +50 -0
  82. package/test/undici.js +50 -0
  83. package/test/unexpected-error.js +46 -0
  84. package/test/unix-http-undici-from.js +51 -0
  85. package/test/unix-http-undici.js +62 -0
  86. package/test/unix-http.js +65 -0
  87. package/test/unix-https-undici.js +71 -0
  88. package/test/unix-https.js +71 -0
@@ -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, { http: 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, 'ECONNRESET')
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, { http: 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, 'ECONNRESET')
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, { http: 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, 'ECONNRESET')
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,56 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const Fastify = require('fastify')
5
+ const From = require('..')
6
+ const got = require('got')
7
+ const FakeTimers = require('@sinonjs/fake-timers')
8
+
9
+ const clock = FakeTimers.createClock()
10
+
11
+ t.autoend(false)
12
+
13
+ const target = Fastify()
14
+ t.teardown(target.close.bind(target))
15
+
16
+ target.get('/', (request, reply) => {
17
+ t.pass('request arrives')
18
+
19
+ clock.setTimeout(() => {
20
+ reply.status(200).send('hello world')
21
+ t.end()
22
+ }, 200)
23
+ })
24
+
25
+ async function main () {
26
+ await target.listen(0)
27
+
28
+ const instance = Fastify()
29
+ t.teardown(instance.close.bind(instance))
30
+
31
+ instance.register(From, { http: { requestOptions: { timeout: 100 } } })
32
+
33
+ instance.get('/', (request, reply) => {
34
+ reply.from(`http://localhost:${target.server.address().port}/`)
35
+ })
36
+
37
+ await instance.listen(0)
38
+
39
+ try {
40
+ await got.get(`http://localhost:${instance.server.address().port}/`, { retry: 0 })
41
+ } catch (err) {
42
+ t.equal(err.response.statusCode, 504)
43
+ t.match(err.response.headers['content-type'], /application\/json/)
44
+ t.same(JSON.parse(err.response.body), {
45
+ statusCode: 504,
46
+ error: 'Gateway Timeout',
47
+ message: 'Gateway Timeout'
48
+ })
49
+ clock.tick(200)
50
+ return
51
+ }
52
+
53
+ t.fail()
54
+ }
55
+
56
+ main()
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const h2url = require('h2url')
4
+ const t = require('tap')
5
+ const Fastify = require('fastify')
6
+ const From = require('..')
7
+ const fs = require('fs')
8
+ const path = require('path')
9
+ const certs = {
10
+ key: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.key')),
11
+ cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.cert'))
12
+ }
13
+
14
+ t.test('http2 -> http2', async (t) => {
15
+ const instance = Fastify({
16
+ http2: true,
17
+ https: certs
18
+ })
19
+
20
+ t.teardown(instance.close.bind(instance))
21
+
22
+ const target = Fastify({
23
+ http2: true
24
+ })
25
+
26
+ target.get('/', (request, reply) => {
27
+ t.pass('request proxied')
28
+ reply.code(404).header('x-my-header', 'hello!').send({
29
+ hello: 'world'
30
+ })
31
+ })
32
+
33
+ instance.get('/', (request, reply) => {
34
+ reply.from()
35
+ })
36
+
37
+ t.teardown(target.close.bind(target))
38
+
39
+ await target.listen(0)
40
+
41
+ instance.register(From, {
42
+ base: `http://localhost:${target.server.address().port}`,
43
+ http2: true,
44
+ rejectUnauthorized: false
45
+ })
46
+
47
+ await instance.listen(0)
48
+
49
+ const { headers, body } = await h2url.concat({
50
+ url: `https://localhost:${instance.server.address().port}`
51
+ })
52
+
53
+ t.equal(headers[':status'], 404)
54
+ t.equal(headers['x-my-header'], 'hello!')
55
+ t.match(headers['content-type'], /application\/json/)
56
+ t.same(JSON.parse(body), { hello: 'world' })
57
+ })
@@ -0,0 +1,80 @@
1
+ 'use strict'
2
+
3
+ const h2url = require('h2url')
4
+ const t = require('tap')
5
+ const Fastify = require('fastify')
6
+ const From = require('..')
7
+ const got = require('got')
8
+ const fs = require('fs')
9
+ const path = require('path')
10
+ const certs = {
11
+ allowHTTP1: true, // fallback support for HTTP1
12
+ key: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.key')),
13
+ cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.cert'))
14
+ }
15
+
16
+ const instance = Fastify({
17
+ http2: true,
18
+ https: certs
19
+ })
20
+
21
+ t.plan(4)
22
+ t.teardown(instance.close.bind(instance))
23
+
24
+ const target = Fastify({
25
+ https: certs
26
+ })
27
+
28
+ target.get('/', (request, reply) => {
29
+ t.pass('request proxied')
30
+ reply.code(404).header('x-my-header', 'hello!').send({
31
+ hello: 'world'
32
+ })
33
+ })
34
+
35
+ instance.get('/', (request, reply) => {
36
+ reply.from()
37
+ })
38
+
39
+ t.teardown(target.close.bind(target))
40
+
41
+ async function run () {
42
+ await target.listen(0)
43
+
44
+ instance.register(From, {
45
+ base: `https://localhost:${target.server.address().port}`,
46
+ rejectUnauthorized: false
47
+ })
48
+
49
+ await instance.listen(0)
50
+
51
+ t.test('http2 -> https', async (t) => {
52
+ const { headers, body } = await h2url.concat({
53
+ url: `https://localhost:${instance.server.address().port}`
54
+ })
55
+
56
+ t.equal(headers[':status'], 404)
57
+ t.equal(headers['x-my-header'], 'hello!')
58
+ t.match(headers['content-type'], /application\/json/)
59
+ t.same(JSON.parse(body), { hello: 'world' })
60
+ })
61
+
62
+ t.test('https -> https', async (t) => {
63
+ try {
64
+ await got(`https://localhost:${instance.server.address().port}`, {
65
+ https: {
66
+ rejectUnauthorized: false
67
+ }
68
+ })
69
+ } catch (err) {
70
+ t.equal(err.response.statusCode, 404)
71
+ t.equal(err.response.headers['x-my-header'], 'hello!')
72
+ t.match(err.response.headers['content-type'], /application\/json/)
73
+ t.same(JSON.parse(err.response.body), { hello: 'world' })
74
+ return
75
+ }
76
+ t.fail()
77
+ })
78
+ }
79
+
80
+ run()
@@ -0,0 +1,36 @@
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 invalid target', async (t) => {
9
+ const instance = Fastify()
10
+
11
+ t.teardown(instance.close.bind(instance))
12
+
13
+ instance.get('/', (request, reply) => {
14
+ reply.from()
15
+ })
16
+ instance.register(From, {
17
+ base: 'http://abc.xyz1',
18
+ http2: true
19
+ })
20
+
21
+ await instance.listen(0)
22
+
23
+ try {
24
+ await got(`http://localhost:${instance.server.address().port}`)
25
+ } catch (err) {
26
+ t.equal(err.response.statusCode, 503)
27
+ t.match(err.response.headers['content-type'], /application\/json/)
28
+ t.same(JSON.parse(err.response.body), {
29
+ statusCode: 503,
30
+ error: 'Service Unavailable',
31
+ message: 'Service Unavailable'
32
+ })
33
+ return
34
+ }
35
+ t.fail()
36
+ })
@@ -0,0 +1,53 @@
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 crash', async (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.get('/', (request, reply) => {
18
+ t.pass('request proxied')
19
+ reply.code(200).send({
20
+ hello: 'world'
21
+ })
22
+ })
23
+
24
+ instance.get('/', (request, reply) => {
25
+ reply.from()
26
+ })
27
+
28
+ t.teardown(target.close.bind(target))
29
+
30
+ await target.listen(0)
31
+
32
+ instance.register(From, {
33
+ base: `http://localhost:${target.server.address().port}`,
34
+ http2: true
35
+ })
36
+
37
+ await instance.listen(0)
38
+
39
+ try {
40
+ await target.close()
41
+ await got(`http://localhost:${instance.server.address().port}`)
42
+ } catch (err) {
43
+ t.equal(err.response.statusCode, 503)
44
+ t.match(err.response.headers['content-type'], /application\/json/)
45
+ t.same(JSON.parse(err.response.body), {
46
+ statusCode: 503,
47
+ error: 'Service Unavailable',
48
+ message: 'Service Unavailable'
49
+ })
50
+ return
51
+ }
52
+ t.fail()
53
+ })
@@ -0,0 +1,60 @@
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 crash multiple times', async (t) => {
9
+ const instance = Fastify()
10
+
11
+ t.teardown(instance.close.bind(instance))
12
+
13
+ instance.get('/', (request, reply) => {
14
+ reply.from()
15
+ })
16
+
17
+ instance.register(From, {
18
+ base: 'http://localhost:3128',
19
+ http2: true,
20
+ sessionTimeout: 200
21
+ })
22
+
23
+ await instance.listen(0)
24
+
25
+ try {
26
+ let target = setupTarget()
27
+ await target.listen(3128)
28
+ await got(`http://localhost:${instance.server.address().port}`)
29
+ await target.close()
30
+ target = setupTarget()
31
+ await target.listen(3128)
32
+ await got(`http://localhost:${instance.server.address().port}`)
33
+ await target.close()
34
+ await got(`http://localhost:${instance.server.address().port}`)
35
+ } catch (err) {
36
+ t.equal(err.response.statusCode, 503)
37
+ t.match(err.response.headers['content-type'], /application\/json/)
38
+ t.same(JSON.parse(err.response.body), {
39
+ statusCode: 503,
40
+ error: 'Service Unavailable',
41
+ message: 'Service Unavailable'
42
+ })
43
+ return
44
+ }
45
+ t.fail()
46
+
47
+ function setupTarget () {
48
+ const target = Fastify({
49
+ http2: true
50
+ })
51
+
52
+ target.get('/', (request, reply) => {
53
+ t.pass('request proxied')
54
+ reply.code(200).send({
55
+ hello: 'world'
56
+ })
57
+ })
58
+ return target
59
+ }
60
+ })
@@ -0,0 +1,92 @@
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', async (t) => {
9
+ const target = Fastify({ http2: true, sessionTimeout: 0 })
10
+ t.teardown(target.close.bind(target))
11
+
12
+ target.get('/', () => {
13
+ t.pass('request arrives')
14
+ })
15
+
16
+ await target.listen(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: 100 }
24
+ })
25
+
26
+ instance.get('/', (request, reply) => {
27
+ reply.from(`http://localhost:${target.server.address().port}/`)
28
+ })
29
+
30
+ await instance.listen(0)
31
+
32
+ try {
33
+ await got.get(`http://localhost:${instance.server.address().port}/`, {
34
+ retry: 0
35
+ })
36
+ } catch (err) {
37
+ t.equal(err.response.statusCode, 504)
38
+ t.match(err.response.headers['content-type'], /application\/json/)
39
+ t.same(JSON.parse(err.response.body), {
40
+ statusCode: 504,
41
+ error: 'Gateway Timeout',
42
+ message: 'Gateway Timeout'
43
+ })
44
+
45
+ return
46
+ }
47
+
48
+ t.fail()
49
+ })
50
+
51
+ test('http2 session timeout', async (t) => {
52
+ const target = Fastify({ http2: true, sessionTimeout: 0 })
53
+ t.teardown(target.close.bind(target))
54
+
55
+ target.get('/', () => {
56
+ t.pass('request arrives')
57
+ })
58
+
59
+ await target.listen(0)
60
+
61
+ const instance = Fastify()
62
+ t.teardown(instance.close.bind(instance))
63
+
64
+ instance.register(From, {
65
+ base: `http://localhost:${target.server.address().port}`,
66
+ http2: { sessionTimeout: 100 }
67
+ })
68
+
69
+ instance.get('/', (request, reply) => {
70
+ reply.from(`http://localhost:${target.server.address().port}/`)
71
+ })
72
+
73
+ await instance.listen(0)
74
+
75
+ try {
76
+ await got.get(`http://localhost:${instance.server.address().port}/`, {
77
+ retry: 0
78
+ })
79
+ } catch (err) {
80
+ t.equal(err.response.statusCode, 504)
81
+ t.match(err.response.headers['content-type'], /application\/json/)
82
+ t.same(JSON.parse(err.response.body), {
83
+ statusCode: 504,
84
+ error: 'Gateway Timeout',
85
+ message: 'Gateway Timeout'
86
+ })
87
+
88
+ return
89
+ }
90
+
91
+ t.fail()
92
+ })
@@ -0,0 +1,67 @@
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 https = require('https')
8
+ const get = require('simple-get').concat
9
+
10
+ const fs = require('fs')
11
+ const path = require('path')
12
+ const certs = {
13
+ key: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.key')),
14
+ cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.cert'))
15
+ }
16
+
17
+ const instance = Fastify({
18
+ https: certs
19
+ })
20
+
21
+ t.plan(10)
22
+ t.teardown(instance.close.bind(instance))
23
+
24
+ const target = https.createServer(certs, (req, res) => {
25
+ t.pass('request proxied')
26
+ t.equal(req.method, 'GET')
27
+ t.equal(req.url, '/')
28
+ res.statusCode = 205
29
+ res.setHeader('Content-Type', 'text/plain')
30
+ res.setHeader('x-my-header', 'hello!')
31
+ res.end('hello world')
32
+ })
33
+
34
+ instance.get('/', (request, reply) => {
35
+ reply.from()
36
+ })
37
+
38
+ t.teardown(target.close.bind(target))
39
+
40
+ target.listen(0, (err) => {
41
+ t.error(err)
42
+
43
+ instance.register(From, {
44
+ base: `https://localhost:${target.address().port}`,
45
+ http: {
46
+ agents: {
47
+ 'http:': new http.Agent({}),
48
+ 'https:': new https.Agent({})
49
+ }
50
+ }
51
+ })
52
+
53
+ instance.listen(0, (err) => {
54
+ t.error(err)
55
+
56
+ get({
57
+ url: `https://localhost:${instance.server.address().port}`,
58
+ rejectUnauthorized: false
59
+ }, (err, res, data) => {
60
+ t.error(err)
61
+ t.equal(res.headers['content-type'], 'text/plain')
62
+ t.equal(res.headers['x-my-header'], 'hello!')
63
+ t.equal(res.statusCode, 205)
64
+ t.equal(data.toString(), 'hello world')
65
+ })
66
+ })
67
+ })