@fastify/reply-from 8.3.1 → 8.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @fastify/reply-from
2
2
 
3
- [![CI](https://github.com/fastify/reply-fro/workflows/CI/badge.svg)](https://github.com/fastify/reply-fro/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/fastify/fastify-reply-from/workflows/CI/badge.svg)](https://github.com/fastify/fastify-reply-from/actions/workflows/ci.yml)
4
4
  [![NPM version](https://img.shields.io/npm/v/@fastify/reply-from.svg?style=flat)](https://www.npmjs.com/package/@fastify/reply-from)
5
5
  [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)
6
6
 
@@ -166,7 +166,7 @@ proxy.register(require('@fastify/reply-from'), {
166
166
  sessionOptions: { // HTTP/2 session connect options, pass in any options from https://nodejs.org/api/http2.html#http2_http2_connect_authority_options_listener
167
167
  rejectUnauthorized: true
168
168
  },
169
- requestTimeout: { // HTTP/2 request options, pass in any options from https://nodejs.org/api/http2.html#http2_clienthttp2session_request_headers_options
169
+ requestOptions: { // HTTP/2 request options, pass in any options from https://nodejs.org/api/http2.html#clienthttp2sessionrequestheaders-options
170
170
  endStream: true
171
171
  }
172
172
  }
@@ -216,6 +216,34 @@ By default: `['GET', 'HEAD', 'OPTIONS', 'TRACE' ]`
216
216
 
217
217
  This plugin will always retry on 503 errors, _unless_ `retryMethods` does not contain `GET`.
218
218
 
219
+ #### `globalAgent`
220
+
221
+ Enables the possibility to explictly opt-in for global agents.
222
+
223
+ Usage for undici global agent:
224
+ ```js
225
+ import { setGlobalDispatcher, ProxyAgent } from 'undici'
226
+
227
+ const proxyAgent = new ProxyAgent('my.proxy.server')
228
+ setGlobalDispatcher(proxyAgent)
229
+
230
+ fastify.register(FastifyReplyFrom, {
231
+ base: 'http://localhost:3001/',
232
+ globalAgent: true
233
+ })
234
+ ```
235
+
236
+ Usage for http/https global agent:
237
+ ```js
238
+ fastify.register(FastifyReplyFrom, {
239
+ base: 'http://localhost:3001/',
240
+ // http and https is allowed to use http.globalAgent or https.globalAgent
241
+ globalAgent: true,
242
+ http: {
243
+ }
244
+ })
245
+ ```
246
+
219
247
  ---
220
248
 
221
249
  #### `maxRetriesOn503`
package/index.js CHANGED
@@ -37,7 +37,8 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
37
37
  http: opts.http,
38
38
  http2: opts.http2,
39
39
  base,
40
- undici: opts.undici
40
+ undici: opts.undici,
41
+ globalAgent: opts.globalAgent
41
42
  })
42
43
  if (requestBuilt instanceof Error) {
43
44
  next(requestBuilt)
@@ -240,7 +241,9 @@ function onErrorDefault (reply, { error }) {
240
241
  }
241
242
 
242
243
  function isFastifyMultipartRegistered (fastify) {
243
- return fastify.hasContentTypeParser('multipart') && fastify.hasRequestDecorator('multipart')
244
+ // TODO: remove fastify.hasContentTypeParser('multipart') in next major
245
+ // It is used to be compatible with @fastify/multipart@<=7.3.0
246
+ return (fastify.hasContentTypeParser('multipart') || fastify.hasContentTypeParser('multipart/form-data')) && fastify.hasRequestDecorator('multipart')
244
247
  }
245
248
 
246
249
  function createRequestRetry (requestImpl, reply, retriesCount, retryOnError, maxRetriesOn503) {
package/lib/request.js CHANGED
@@ -41,6 +41,7 @@ function buildRequest (opts) {
41
41
  const httpOpts = getHttpOpts(opts)
42
42
  const baseUrl = opts.base && new URL(opts.base).origin
43
43
  const undiciOpts = opts.undici || {}
44
+ const globalAgent = opts.globalAgent
44
45
  let http2Client
45
46
  let undiciAgent
46
47
  let undiciInstance
@@ -51,11 +52,16 @@ function buildRequest (opts) {
51
52
  if (opts.base.startsWith('unix+')) {
52
53
  return new Error('Unix socket destination is not supported when http2 is true')
53
54
  }
54
- } else {
55
+ } else if (!globalAgent) {
55
56
  agents = httpOpts.agents || {
56
57
  'http:': new http.Agent(httpOpts.agentOptions),
57
58
  'https:': new https.Agent(httpOpts.agentOptions)
58
59
  }
60
+ } else {
61
+ agents = {
62
+ 'http:': http.globalAgent,
63
+ 'https:': https.globalAgent
64
+ }
59
65
  }
60
66
 
61
67
  if (isHttp2) {
@@ -68,8 +74,10 @@ function buildRequest (opts) {
68
74
  undiciInstance = new undici.Pool(protocol + '://localhost', undiciOpts)
69
75
  } else if (isUndiciInstance(opts.undici)) {
70
76
  undiciInstance = opts.undici
71
- } else {
77
+ } else if (!globalAgent) {
72
78
  undiciAgent = new undici.Agent(getUndiciOptions(opts.undici))
79
+ } else {
80
+ undiciAgent = undici.getGlobalDispatcher()
73
81
  }
74
82
  return { request: handleUndici, close, retryOnError: 'UND_ERR_SOCKET' }
75
83
  } else {
@@ -77,6 +85,10 @@ function buildRequest (opts) {
77
85
  }
78
86
 
79
87
  function close () {
88
+ if (globalAgent) {
89
+ return
90
+ }
91
+
80
92
  if (hasUndiciOptions) {
81
93
  undiciAgent && undiciAgent.destroy()
82
94
  undiciInstance && undiciInstance.destroy()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastify/reply-from",
3
- "version": "8.3.1",
3
+ "version": "8.4.0",
4
4
  "description": "forward your HTTP request to another server, for fastify",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -28,8 +28,8 @@
28
28
  },
29
29
  "homepage": "https://github.com/fastify/fastify-reply-from#readme",
30
30
  "devDependencies": {
31
- "@fastify/formbody": "^7.0.1",
32
- "@fastify/multipart": "^7.1.0",
31
+ "@fastify/formbody": "^7.4.0",
32
+ "@fastify/multipart": "^7.4.0",
33
33
  "@fastify/pre-commit": "^2.0.2",
34
34
  "@sinonjs/fake-timers": "^10.0.0",
35
35
  "@types/node": "^18.0.0",
@@ -46,7 +46,7 @@
46
46
  "split2": "^4.1.0",
47
47
  "standard": "^17.0.0",
48
48
  "tap": "^16.2.0",
49
- "tsd": "^0.24.1"
49
+ "tsd": "^0.25.0"
50
50
  },
51
51
  "dependencies": {
52
52
  "@fastify/error": "^3.0.0",
@@ -0,0 +1,57 @@
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 get = require('simple-get').concat
8
+
9
+ test('http global agent is used, but not destroyed', async (t) => {
10
+ http.globalAgent.destroy = () => {
11
+ t.fail()
12
+ }
13
+ const instance = Fastify()
14
+ t.teardown(instance.close.bind(instance))
15
+ instance.get('/', (request, reply) => {
16
+ reply.from()
17
+ })
18
+
19
+ const target = http.createServer((req, res) => {
20
+ t.pass('request proxied')
21
+ t.equal(req.method, 'GET')
22
+ t.equal(req.url, '/')
23
+ res.statusCode = 200
24
+ res.end()
25
+ })
26
+ t.teardown(target.close.bind(target))
27
+
28
+ const executionFlow = () => new Promise((resolve) => {
29
+ target.listen({ port: 0 }, (err) => {
30
+ t.error(err)
31
+
32
+ instance.register(From, {
33
+ base: `http://localhost:${target.address().port}`,
34
+ globalAgent: true,
35
+ http: {
36
+ }
37
+ })
38
+
39
+ instance.listen({ port: 0 }, (err) => {
40
+ t.error(err)
41
+
42
+ get(
43
+ `http://localhost:${instance.server.address().port}`,
44
+ (err, res) => {
45
+ t.error(err)
46
+ t.equal(res.statusCode, 200)
47
+ resolve()
48
+ }
49
+ )
50
+ })
51
+ })
52
+ })
53
+
54
+ await executionFlow()
55
+
56
+ target.close()
57
+ })
@@ -0,0 +1,69 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const Fastify = require('fastify')
5
+ const From = require('..')
6
+ const https = require('https')
7
+ const get = require('simple-get').concat
8
+
9
+ const fs = require('fs')
10
+ const path = require('path')
11
+ const certs = {
12
+ key: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.key')),
13
+ cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'fastify.cert'))
14
+ }
15
+
16
+ test('https global agent is used, but not destroyed', async (t) => {
17
+ https.globalAgent.destroy = () => {
18
+ t.fail()
19
+ }
20
+ const instance = Fastify({
21
+ https: certs
22
+ })
23
+ t.teardown(instance.close.bind(instance))
24
+ instance.get('/', (request, reply) => {
25
+ reply.from()
26
+ })
27
+
28
+ const target = https.createServer(certs, (req, res) => {
29
+ t.pass('request proxied')
30
+ t.equal(req.method, 'GET')
31
+ t.equal(req.url, '/')
32
+ res.statusCode = 200
33
+ res.end()
34
+ })
35
+ t.teardown(target.close.bind(target))
36
+
37
+ const executionFlow = () => new Promise((resolve) => {
38
+ target.listen({ port: 0 }, (err) => {
39
+ t.error(err)
40
+
41
+ instance.register(From, {
42
+ base: `https://localhost:${target.address().port}`,
43
+ globalAgent: true,
44
+ http: {
45
+ }
46
+ })
47
+
48
+ instance.listen({ port: 0 }, (err) => {
49
+ t.error(err)
50
+
51
+ get(
52
+ {
53
+ url: `https://localhost:${instance.server.address().port}`,
54
+ rejectUnauthorized: false
55
+ },
56
+ (err, res) => {
57
+ t.error(err)
58
+ t.equal(res.statusCode, 200)
59
+ resolve()
60
+ }
61
+ )
62
+ })
63
+ })
64
+ })
65
+
66
+ await executionFlow()
67
+
68
+ target.close()
69
+ })
@@ -0,0 +1,66 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const Fastify = require('fastify')
5
+ const http = require('http')
6
+ const get = require('simple-get').concat
7
+ const undici = require('undici')
8
+ const From = require('..')
9
+
10
+ test('undici global agent is used, but not destroyed', async (t) => {
11
+ const mockAgent = new undici.Agent()
12
+ mockAgent.destroy = () => {
13
+ t.fail()
14
+ }
15
+ undici.setGlobalDispatcher(mockAgent)
16
+ const instance = Fastify()
17
+
18
+ t.teardown(instance.close.bind(instance))
19
+
20
+ const target = http.createServer((req, res) => {
21
+ res.statusCode = 200
22
+ res.end()
23
+ })
24
+
25
+ instance.get('/', (request, reply) => {
26
+ reply.from()
27
+ })
28
+
29
+ t.teardown(target.close.bind(target))
30
+
31
+ const executionFlow = () => new Promise((resolve) => {
32
+ target.listen({ port: 0 }, (err) => {
33
+ t.error(err)
34
+
35
+ instance.register(From, {
36
+ base: `http://localhost:${target.address().port}`,
37
+ globalAgent: true
38
+ })
39
+
40
+ instance.listen({ port: 0 }, (err) => {
41
+ t.error(err)
42
+
43
+ get(
44
+ `http://localhost:${instance.server.address().port}`,
45
+ (err, res) => {
46
+ t.error(err)
47
+ t.equal(res.statusCode, 200)
48
+
49
+ get(
50
+ `http://localhost:${instance.server.address().port}`,
51
+ (err, res) => {
52
+ t.error(err)
53
+ t.equal(res.statusCode, 200)
54
+ resolve()
55
+ }
56
+ )
57
+ }
58
+ )
59
+ })
60
+ })
61
+ })
62
+
63
+ await executionFlow()
64
+
65
+ target.close()
66
+ })
package/types/index.d.ts CHANGED
@@ -95,6 +95,7 @@ declare namespace fastifyReplyFrom {
95
95
  retryMethods?: (HTTPMethods | 'TRACE')[];
96
96
  maxRetriesOn503?: number;
97
97
  disableRequestLogging?: boolean;
98
+ globalAgent?: boolean;
98
99
  }
99
100
 
100
101
  export const fastifyReplyFrom: FastifyReplyFrom
@@ -45,6 +45,7 @@ const fullOptions: FastifyReplyFromOptions = {
45
45
  retryMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE'],
46
46
  maxRetriesOn503: 10,
47
47
  disableRequestLogging: false,
48
+ globalAgent: false,
48
49
  };
49
50
  tap.autoend(false);
50
51