@fastify/reply-from 9.2.0 → 9.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
@@ -388,6 +388,10 @@ through `JSON.stringify()`.
388
388
  Setting this option for GET, HEAD requests will throw an error "Rewriting the body when doing a {GET|HEAD} is not allowed".
389
389
  Setting this option to `null` will strip the body (and `content-type` header) entirely from the proxied request.
390
390
 
391
+ #### `method`
392
+
393
+ Replaces the original request method with what is specified.
394
+
391
395
  #### `retriesCount`
392
396
 
393
397
  How many times it will try to pick another connection on socket hangup (`ECONNRESET` error).
package/index.js CHANGED
@@ -52,6 +52,7 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
52
52
  fastify.decorateReply('from', function (source, opts) {
53
53
  opts = opts || {}
54
54
  const req = this.request.raw
55
+ const method = opts.method || req.method
55
56
  const onResponse = opts.onResponse
56
57
  const rewriteHeaders = opts.rewriteHeaders || headersNoOp
57
58
  const rewriteRequestHeaders = opts.rewriteRequestHeaders || requestHeadersNoOp
@@ -127,12 +128,12 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
127
128
  // fastify ignore message body when it's a GET or HEAD request
128
129
  // when proxy this request, we should reset the content-length to make it a valid http request
129
130
  // discussion: https://github.com/fastify/fastify/issues/953
130
- if (req.method === 'GET' || req.method === 'HEAD') {
131
+ if (method === 'GET' || method === 'HEAD') {
131
132
  // body will be populated here only if opts.body is passed.
132
133
  // if we are doing that with a GET or HEAD request is a programmer error
133
134
  // and as such we can throw immediately.
134
135
  if (body) {
135
- throw new Error(`Rewriting the body when doing a ${req.method} is not allowed`)
136
+ throw new Error(`Rewriting the body when doing a ${method} is not allowed`)
136
137
  }
137
138
  }
138
139
 
@@ -141,13 +142,13 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
141
142
  const requestHeaders = rewriteRequestHeaders(this.request, headers)
142
143
  const contentLength = requestHeaders['content-length']
143
144
  let requestImpl
144
- if (retryMethods.has(req.method) && !contentLength) {
145
+ if (retryMethods.has(method) && !contentLength) {
145
146
  requestImpl = createRequestRetry(request, this, retriesCount, retryOnError, maxRetriesOn503)
146
147
  } else {
147
148
  requestImpl = request
148
149
  }
149
150
 
150
- requestImpl({ method: req.method, url, qs, headers: requestHeaders, body }, (err, res) => {
151
+ requestImpl({ method, url, qs, headers: requestHeaders, body }, (err, res) => {
151
152
  if (err) {
152
153
  this.request.log.warn(err, 'response errored')
153
154
  if (!this.sent) {
package/lib/request.js CHANGED
@@ -22,10 +22,17 @@ function shouldUseUndici (opts) {
22
22
  return true
23
23
  }
24
24
 
25
+ function isRequestable (obj) {
26
+ return obj !== null &&
27
+ typeof obj === 'object' &&
28
+ typeof obj.request === 'function'
29
+ }
30
+
25
31
  function isUndiciInstance (obj) {
26
32
  return obj instanceof undici.Pool ||
27
33
  obj instanceof undici.Client ||
28
- obj instanceof undici.Dispatcher
34
+ obj instanceof undici.Dispatcher ||
35
+ isRequestable(obj)
29
36
  }
30
37
 
31
38
  function buildRequest (opts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastify/reply-from",
3
- "version": "9.2.0",
3
+ "version": "9.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",
@@ -31,7 +31,7 @@
31
31
  "@fastify/formbody": "^7.4.0",
32
32
  "@fastify/multipart": "^7.4.0",
33
33
  "@fastify/pre-commit": "^2.0.2",
34
- "@sinonjs/fake-timers": "^10.0.0",
34
+ "@sinonjs/fake-timers": "^11.0.0",
35
35
  "@types/node": "^20.1.4",
36
36
  "@types/tap": "^15.0.7",
37
37
  "fastify": "^4.0.2",
@@ -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.patch('/', (request, reply) => {
37
+ reply.from(`http://localhost:${target.address().port}`, { method: 'POST' })
38
+ })
39
+
40
+ t.teardown(target.close.bind(target))
41
+
42
+ target.listen({ port: 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({ port: 0 }, (err) => {
55
+ t.error(err)
56
+
57
+ get({
58
+ url: `http://localhost:${instance.server.address().port}`,
59
+ method: 'PATCH',
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 { test } = require('tap')
4
+ const undici = require('undici')
5
+ const Fastify = require('fastify')
6
+ const From = require('..')
7
+
8
+ class CustomDispatcher {
9
+ constructor (...args) {
10
+ this._dispatcher = new undici.Pool(...args)
11
+ }
12
+
13
+ request (...args) {
14
+ return this._dispatcher.request(...args)
15
+ }
16
+
17
+ close (...args) {
18
+ return this._dispatcher.close(...args)
19
+ }
20
+
21
+ destroy (...args) {
22
+ return this._dispatcher.destroy(...args)
23
+ }
24
+ }
25
+
26
+ test('use a custom instance of \'undici\'', async t => {
27
+ const target = Fastify({
28
+ keepAliveTimeout: 1
29
+ })
30
+
31
+ target.get('/', (req, reply) => {
32
+ t.pass('request proxied')
33
+
34
+ reply.headers({
35
+ 'Content-Type': 'text/plain',
36
+ 'x-my-header': 'hello!'
37
+ })
38
+
39
+ reply.statusCode = 205
40
+ reply.send('hello world')
41
+ })
42
+
43
+ await target.listen({ port: 3001 })
44
+ t.teardown(async () => {
45
+ await target.close()
46
+ })
47
+
48
+ const instance = Fastify({
49
+ keepAliveTimeout: 1
50
+ })
51
+
52
+ instance.register(From, {
53
+ undici: new CustomDispatcher('http://localhost:3001')
54
+ })
55
+
56
+ instance.get('/', (request, reply) => {
57
+ reply.from('http://myserver.local')
58
+ })
59
+
60
+ await instance.listen({ port: 0 })
61
+ t.teardown(async () => {
62
+ await instance.close()
63
+ })
64
+
65
+ const res = await undici.request(`http://localhost:${instance.server.address().port}`)
66
+
67
+ t.equal(res.headers['content-type'], 'text/plain')
68
+ t.equal(res.headers['x-my-header'], 'hello!')
69
+ t.equal(res.statusCode, 205)
70
+
71
+ const data = await res.body.text()
72
+ t.equal(data, 'hello world')
73
+ })
package/types/index.d.ts CHANGED
@@ -67,6 +67,7 @@ declare namespace fastifyReplyFrom {
67
67
  request: FastifyRequest<RequestGenericInterface, RawServerBase>,
68
68
  base: string
69
69
  ) => string;
70
+ method?: HTTPMethods;
70
71
  }
71
72
 
72
73
  interface Http2Options {
@@ -90,6 +90,7 @@ async function main() {
90
90
 
91
91
  instance.get("/http2", (request, reply) => {
92
92
  reply.from("/", {
93
+ method: "POST",
93
94
  rewriteHeaders(headers, req) {
94
95
  return headers;
95
96
  },