Skip to content

Commit 00bfe30

Browse files
ShogunPandaEomm
andauthored
feat: Added fromParameters decorator. (#421)
* feat: Added fromParameters decorator. Signed-off-by: Paolo Insogna <[email protected]> * fixup Signed-off-by: Paolo Insogna <[email protected]> * Update README.md Co-authored-by: Manuel Spigolon <[email protected]> Signed-off-by: Paolo Insogna <[email protected]> * Update README.md Co-authored-by: Manuel Spigolon <[email protected]> Signed-off-by: Paolo Insogna <[email protected]> --------- Signed-off-by: Paolo Insogna <[email protected]> Co-authored-by: Manuel Spigolon <[email protected]>
1 parent d5d6d8f commit 00bfe30

File tree

3 files changed

+81
-10
lines changed

3 files changed

+81
-10
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ It also supports an additional `rewriteRequestHeaders(headers, request)` functio
227227
opening the WebSocket connection. This function should return an object with the given headers.
228228
The default implementation forwards the `cookie` header.
229229

230-
## `wsReconnect`
230+
### `wsReconnect`
231231

232232
**Experimental.** (default: `disabled`)
233233

@@ -247,7 +247,7 @@ To enable the feature, set the `wsReconnect` option to an object with the follow
247247

248248
See the example in [examples/reconnection](examples/reconnection).
249249

250-
## wsHooks
250+
### `wsHooks`
251251

252252
On websocket events, the following hooks are available, note **the hooks are all synchronous**.
253253
The `context` object is passed to all hooks and contains the `log` property.
@@ -259,6 +259,26 @@ The `context` object is passed to all hooks and contains the `log` property.
259259
- `onReconnect`: A hook function that is called when the connection is reconnected `onReconnect(context, source, target)` (default: `undefined`). The function is called if reconnection feature is enabled.
260260
- `onPong`: A hook function that is called when the target responds to the ping `onPong(context, source, target)` (default: `undefined`). The function is called if reconnection feature is enabled.
261261

262+
## Decorators
263+
264+
### `reply.fromParameters(url[, params[, prefix]])`
265+
266+
It can be used to get the final URL and options that `@fastify/http-proxy` would have used to invoke `reply.from`.
267+
268+
A typical use is to override the request URL:
269+
270+
```javascript
271+
preHandler (request, reply, done) {
272+
if (request.url !== '/original') {
273+
done()
274+
return
275+
}
276+
277+
const { url, options } = reply.fromParameters('/updated', { ...request.params, serverId: 42 })
278+
reply.from(url, options)
279+
}
280+
```
281+
262282
## Benchmarks
263283

264284
The following benchmarks were generated on a dedicated server with an Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz and 64GB of RAM:

index.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -592,16 +592,16 @@ async function fastifyHttpProxy (fastify, opts) {
592592
return components
593593
}
594594

595-
function handler (request, reply) {
596-
const { path, queryParams } = extractUrlComponents(request.url)
595+
function fromParameters (url, params = {}, prefix = '/') {
596+
const { path, queryParams } = extractUrlComponents(url)
597597
let dest = path
598598

599-
if (this.prefix.includes(':')) {
599+
if (prefix.includes(':')) {
600600
const requestedPathElements = path.split('/')
601-
const prefixPathWithVariables = this.prefix.split('/').map((_, index) => requestedPathElements[index]).join('/')
601+
const prefixPathWithVariables = prefix.split('/').map((_, index) => requestedPathElements[index]).join('/')
602602

603603
let rewritePrefixWithVariables = rewritePrefix
604-
for (const [name, value] of Object.entries(request.params)) {
604+
for (const [name, value] of Object.entries(params)) {
605605
rewritePrefixWithVariables = rewritePrefixWithVariables.replace(`:${name}`, value)
606606
}
607607

@@ -610,20 +610,34 @@ async function fastifyHttpProxy (fastify, opts) {
610610
dest += `?${qs.stringify(queryParams)}`
611611
}
612612
} else {
613-
dest = dest.replace(this.prefix, rewritePrefix)
613+
dest = dest.replace(prefix, rewritePrefix)
614614
}
615615

616+
return { url: dest || '/', options: replyOpts }
617+
}
618+
619+
function handler (request, reply) {
620+
const { url, options } = fromParameters(request.url, request.params, this.prefix)
621+
616622
if (request.raw[kWs]) {
617623
reply.hijack()
618624
try {
619-
wsProxy.handleUpgrade(request, dest || '/', noop)
625+
wsProxy.handleUpgrade(request, url, noop)
620626
} /* c8 ignore start */ catch (err) {
621627
request.log.warn({ err }, 'websocket proxy error')
622628
} /* c8 ignore stop */
623629
return
624630
}
625-
reply.from(dest || '/', replyOpts)
631+
reply.from(url, options)
626632
}
633+
634+
fastify.decorateReply('fromParameters', fromParameters)
635+
fastify.decorateReply('wsProxy', {
636+
/* c8 ignore next 3 */
637+
getter () {
638+
return wsProxy
639+
}
640+
})
627641
}
628642

629643
module.exports = fp(fastifyHttpProxy, {

test/test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,43 @@ async function run () {
921921
const queryParams = JSON.stringify(qs.parse('foo=bar&foo=baz&abc=qux'))
922922
t.assert.strictEqual(firstProxyPrefix.body, `this is "variable-api" endpoint with id 123 and query params ${queryParams}`)
923923
})
924+
925+
test('manual from call via fromParameters', async t => {
926+
const server = Fastify()
927+
server.register(proxy, {
928+
upstream: `http://localhost:${origin.server.address().port}`,
929+
preHandler (request, reply, done) {
930+
if (request.url !== '/fake-a') {
931+
done()
932+
return
933+
}
934+
935+
const { url, options } = reply.fromParameters('/a')
936+
reply.from(url, options)
937+
}
938+
})
939+
940+
await server.listen({ port: 0 })
941+
t.after(() => server.close())
942+
943+
{
944+
const {
945+
statusCode,
946+
body
947+
} = await got(`http://localhost:${server.server.address().port}/`)
948+
t.assert.strictEqual(statusCode, 200)
949+
t.assert.strictEqual(body, 'this is root')
950+
}
951+
952+
{
953+
const {
954+
statusCode,
955+
body
956+
} = await got(`http://localhost:${server.server.address().port}/fake-a`)
957+
t.assert.strictEqual(statusCode, 200)
958+
t.assert.strictEqual(body, 'this is a')
959+
}
960+
})
924961
}
925962

926963
run()

0 commit comments

Comments
 (0)