diff --git a/index.js b/index.js index b2752f2..6c85d86 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ const { ServerResponse } = require('node:http') const WebSocket = require('ws') const { convertUrlToWebSocket } = require('./utils') const fp = require('fastify-plugin') +const qs = require('fast-querystring') const httpMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS'] const urlPattern = /^https?:\/\// @@ -305,6 +306,20 @@ async function fastifyHttpProxy (fastify, opts) { wsProxy = setupWebSocketProxy(fastify, opts, rewritePrefix) } + function extractUrlComponents (urlString) { + const [path, queryString] = urlString.split('?', 2) + const components = { + path, + queryParams: null + } + + if (queryString) { + components.queryParams = qs.parse(queryString) + } + + return components + } + function handler (request, reply) { if (request.raw[kWs]) { reply.hijack() @@ -316,11 +331,11 @@ async function fastifyHttpProxy (fastify, opts) { } return } - const queryParamIndex = request.raw.url.indexOf('?') - let dest = request.raw.url.slice(0, queryParamIndex !== -1 ? queryParamIndex : undefined) + const { path, queryParams } = extractUrlComponents(request.url) + let dest = path if (this.prefix.includes(':')) { - const requestedPathElements = request.url.split('/') + const requestedPathElements = path.split('/') const prefixPathWithVariables = this.prefix.split('/').map((_, index) => requestedPathElements[index]).join('/') let rewritePrefixWithVariables = rewritePrefix @@ -329,6 +344,9 @@ async function fastifyHttpProxy (fastify, opts) { } dest = dest.replace(prefixPathWithVariables, rewritePrefixWithVariables) + if (queryParams) { + dest += `?${qs.stringify(queryParams)}` + } } else { dest = dest.replace(this.prefix, rewritePrefix) } diff --git a/package.json b/package.json index 90b082f..926c5fa 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ }, "dependencies": { "@fastify/reply-from": "^9.0.0", + "fast-querystring": "^1.1.2", "fastify-plugin": "^4.5.0", "ws": "^8.4.2" }, diff --git a/test/test.js b/test/test.js index 1c33afd..98bc258 100644 --- a/test/test.js +++ b/test/test.js @@ -6,6 +6,7 @@ const proxy = require('../') const got = require('got') const { Unauthorized } = require('http-errors') const Transform = require('node:stream').Transform +const qs = require('fast-querystring') async function run () { const origin = Fastify() @@ -42,6 +43,10 @@ async function run () { return `this is "variable-api" endpoint with id ${request.params.id}` }) + origin.get('/variable-api/:id/endpoint-with-query', async (request, reply) => { + return `this is "variable-api" endpoint with id ${request.params.id} and query params ${JSON.stringify(request.query)}` + }) + origin.get('/timeout', async (request, reply) => { await new Promise((resolve) => setTimeout(resolve, 600)) return 'this is never received' @@ -890,6 +895,28 @@ async function run () { ) t.equal(resultFooRoute.body, 'Hello World (foo) - lang = en') }) + + test('keep the query params on proxy', { only: true }, async t => { + const proxyServer = Fastify() + + proxyServer.register(proxy, { + upstream: `http://localhost:${origin.server.address().port}`, + prefix: '/api/:id/endpoint', + rewritePrefix: '/variable-api/:id/endpoint-with-query' + }) + + await proxyServer.listen({ port: 0 }) + + t.teardown(() => { + proxyServer.close() + }) + + const firstProxyPrefix = await got( + `http://localhost:${proxyServer.server.address().port}/api/123/endpoint?foo=bar&foo=baz&abc=qux` + ) + const queryParams = JSON.stringify(qs.parse('foo=bar&foo=baz&abc=qux')) + t.equal(firstProxyPrefix.body, `this is "variable-api" endpoint with id 123 and query params ${queryParams}`) + }) } run()