Skip to content

Commit

Permalink
feat: add method opt to overwrite http method (#322)
Browse files Browse the repository at this point in the history
  • Loading branch information
psteinroe authored Jun 18, 2023
1 parent 658033f commit dcb8179
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ through `JSON.stringify()`.
Setting this option for GET, HEAD requests will throw an error "Rewriting the body when doing a {GET|HEAD} is not allowed".
Setting this option to `null` will strip the body (and `content-type` header) entirely from the proxied request.

#### `method`

Replaces the original request method with what is specified.

#### `retriesCount`

How many times it will try to pick another connection on socket hangup (`ECONNRESET` error).
Expand Down
9 changes: 5 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
fastify.decorateReply('from', function (source, opts) {
opts = opts || {}
const req = this.request.raw
const method = opts.method || req.method
const onResponse = opts.onResponse
const rewriteHeaders = opts.rewriteHeaders || headersNoOp
const rewriteRequestHeaders = opts.rewriteRequestHeaders || requestHeadersNoOp
Expand Down Expand Up @@ -127,12 +128,12 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
// fastify ignore message body when it's a GET or HEAD request
// when proxy this request, we should reset the content-length to make it a valid http request
// discussion: https://github.com/fastify/fastify/issues/953
if (req.method === 'GET' || req.method === 'HEAD') {
if (method === 'GET' || method === 'HEAD') {
// body will be populated here only if opts.body is passed.
// if we are doing that with a GET or HEAD request is a programmer error
// and as such we can throw immediately.
if (body) {
throw new Error(`Rewriting the body when doing a ${req.method} is not allowed`)
throw new Error(`Rewriting the body when doing a ${method} is not allowed`)
}
}

Expand All @@ -141,13 +142,13 @@ const fastifyReplyFrom = fp(function from (fastify, opts, next) {
const requestHeaders = rewriteRequestHeaders(this.request, headers)
const contentLength = requestHeaders['content-length']
let requestImpl
if (retryMethods.has(req.method) && !contentLength) {
if (retryMethods.has(method) && !contentLength) {
requestImpl = createRequestRetry(request, this, retriesCount, retryOnError, maxRetriesOn503)
} else {
requestImpl = request
}

requestImpl({ method: req.method, url, qs, headers: requestHeaders, body }, (err, res) => {
requestImpl({ method, url, qs, headers: requestHeaders, body }, (err, res) => {
if (err) {
this.request.log.warn(err, 'response errored')
if (!this.sent) {
Expand Down
70 changes: 70 additions & 0 deletions test/method.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict'

const t = require('tap')
const Fastify = require('fastify')
const From = require('..')
const http = require('http')
const get = require('simple-get').concat

const instance = Fastify()

t.plan(9)
t.teardown(instance.close.bind(instance))

const bodyString = JSON.stringify({ hello: 'world' })

const parsedLength = Buffer.byteLength(bodyString)

const target = http.createServer((req, res) => {
t.pass('request proxied')
t.equal(req.method, 'POST')
t.equal(req.headers['content-type'], 'application/json')
t.same(req.headers['content-length'], parsedLength)
let data = ''
req.setEncoding('utf8')
req.on('data', (d) => {
data += d
})
req.on('end', () => {
t.same(JSON.parse(data), { hello: 'world' })
res.statusCode = 200
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ something: 'else' }))
})
})

instance.patch('/', (request, reply) => {
reply.from(`http://localhost:${target.address().port}`, { method: 'POST' })
})

t.teardown(target.close.bind(target))

target.listen({ port: 0 }, (err) => {
t.error(err)

instance.addContentTypeParser('application/json', function (req, payload, done) {
done(null, payload)
})

instance.register(From, {
base: `http://localhost:${target.address().port}`,
undici: true
})

instance.listen({ port: 0 }, (err) => {
t.error(err)

get({
url: `http://localhost:${instance.server.address().port}`,
method: 'PATCH',
headers: {
'content-type': 'application/json'
},
body: bodyString
}, (err, res, data) => {
t.error(err)
const parsed = JSON.parse(data)
t.same(parsed, { something: 'else' })
})
})
})
1 change: 1 addition & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ declare namespace fastifyReplyFrom {
request: FastifyRequest<RequestGenericInterface, RawServerBase>,
base: string
) => string;
method?: HTTPMethods;
}

interface Http2Options {
Expand Down
1 change: 1 addition & 0 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ async function main() {

instance.get("/http2", (request, reply) => {
reply.from("/", {
method: "POST",
rewriteHeaders(headers, req) {
return headers;
},
Expand Down

0 comments on commit dcb8179

Please sign in to comment.