Skip to content

Commit 240f6a4

Browse files
committed
Merge branch 'master' of github.com:fastify/fastify-http-proxy
2 parents ac21cf0 + 102f547 commit 240f6a4

File tree

6 files changed

+127
-8
lines changed

6 files changed

+127
-8
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This [`fastify`](https://www.fastify.io) plugin forwards all requests
99
received with a given prefix (or none) to an upstream. All Fastify hooks are still applied.
1010

1111
`@fastify/http-proxy` is built on top of
12-
[`@fastify/reply-from`](http://npm.im/fastify-reply-from), which enables single route proxying.
12+
[`@fastify/reply-from`](https://npm.im/@fastify/reply-from), which enables single route proxying.
1313

1414
This plugin can be used in a variety of circumstances, for example if you have to proxy an internal domain to an external domain (useful to avoid CORS problems) or to implement your own API gateway for a microservices architecture.
1515

@@ -187,6 +187,14 @@ A few things are missing:
187187

188188
Pull requests are welcome to finish this feature.
189189

190+
### `wsUpstream`
191+
Working only if property `websocket` is `true`.
192+
193+
An URL (including protocol) that represents the target websockets to use for proxying websockets.
194+
Accepted both `https://` and `wss://`.
195+
196+
Note that if property `wsUpstream` not specified then proxy will try to connect with the `upstream` property.
197+
190198
### `wsServerOptions`
191199

192200
The options passed to [`new ws.Server()`](https://github.com/websockets/ws/blob/HEAD/doc/ws.md#class-websocketserver).

index.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,19 @@ class WebSocketProxy {
147147

148148
findUpstream (request) {
149149
const source = new URL(request.url, 'ws://127.0.0.1')
150+
150151
for (const { prefix, rewritePrefix, upstream, wsClientOptions } of this.prefixList) {
152+
// If the "upstream" have path then need get the Base of url, otherwise the "target" path will be broken.
153+
// Example: upstream is "ws://localhost:22/some/path" and after this code
154+
// "target = new URL(source.pathname.replace(prefix, rewritePrefix), upstream)"
155+
// The target.pathname will be "some/some/path"
156+
const upstreamUrl = new URL(upstream)
157+
const upstreamBase = upstreamUrl.pathname && upstreamUrl.pathname !== '/'
158+
? upstreamUrl.href.replace(upstreamUrl.pathname, '')
159+
: upstream
160+
151161
if (source.pathname.startsWith(prefix)) {
152-
const target = new URL(source.pathname.replace(prefix, rewritePrefix), upstream)
162+
const target = new URL(source.pathname.replace(prefix, rewritePrefix), upstreamBase)
153163
target.search = source.search
154164
return { target, wsClientOptions }
155165
}
@@ -195,8 +205,16 @@ function setupWebSocketProxy (fastify, options, rewritePrefix) {
195205
httpWss.set(fastify.server, wsProxy)
196206
}
197207

198-
if (options.upstream !== '') {
199-
wsProxy.addUpstream(fastify.prefix, rewritePrefix, options.upstream, options.wsClientOptions)
208+
if (
209+
(typeof options.wsUpstream === 'string' && options.wsUpstream !== '') ||
210+
(typeof options.upstream === 'string' && options.upstream !== '')
211+
) {
212+
wsProxy.addUpstream(
213+
fastify.prefix,
214+
rewritePrefix,
215+
options.wsUpstream ? options.wsUpstream : options.upstream,
216+
options.wsClientOptions
217+
)
200218
// The else block is validate earlier in the code
201219
} else {
202220
wsProxy.findUpstream = function (request) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"devDependencies": {
3030
"@fastify/pre-commit": "^2.0.2",
3131
"@fastify/websocket": "^8.0.0",
32-
"@types/node": "^18.0.0",
32+
"@types/node": "^20.1.0",
3333
"@types/ws": "^8.2.2",
3434
"@typescript-eslint/eslint-plugin": "^5.9.1",
3535
"@typescript-eslint/parser": "^5.9.1",

test/websocket.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,68 @@ test('Should gracefully close when clients attempt to connect after calling clos
407407
await server.close()
408408
await p
409409
})
410+
411+
test('Proxy websocket with custom upstream url', async (t) => {
412+
t.plan(5)
413+
414+
const origin = createServer()
415+
const wss = new WebSocket.Server({ server: origin })
416+
417+
t.teardown(wss.close.bind(wss))
418+
t.teardown(origin.close.bind(origin))
419+
420+
const serverMessages = []
421+
wss.on('connection', (ws, request) => {
422+
ws.on('message', (message, binary) => {
423+
// Also need save request.url for check from what url the message is coming.
424+
serverMessages.push([message.toString(), binary, request.url])
425+
ws.send(message, { binary })
426+
})
427+
})
428+
429+
await promisify(origin.listen.bind(origin))({ port: 0 })
430+
// Path for wsUpstream and for later check.
431+
const path = '/some/path'
432+
const server = Fastify()
433+
server.register(proxy, {
434+
upstream: `ws://localhost:${origin.address().port}`,
435+
// Start proxy with different upstream, added path.
436+
wsUpstream: `ws://localhost:${origin.address().port}${path}`,
437+
websocket: true
438+
})
439+
440+
await server.listen({ port: 0 })
441+
t.teardown(server.close.bind(server))
442+
443+
// Start websocket with different upstream for connect, added path.
444+
const ws = new WebSocket(`ws://localhost:${server.server.address().port}${path}`)
445+
await once(ws, 'open')
446+
447+
const data = [{ message: 'hello', binary: false }, { message: 'fastify', binary: true, isBuffer: true }]
448+
const dataLength = data.length
449+
let dataIndex = 0
450+
451+
for (; dataIndex < dataLength; dataIndex++) {
452+
const { message: msg, binary, isBuffer } = data[dataIndex]
453+
const message = isBuffer
454+
? Buffer.from(msg)
455+
: msg
456+
457+
ws.send(message, { binary })
458+
459+
const [reply, binaryAnswer] = await once(ws, 'message')
460+
461+
t.equal(reply.toString(), msg)
462+
t.equal(binaryAnswer, binary)
463+
}
464+
// Also check "path", must be the same.
465+
t.strictSame(serverMessages, [
466+
['hello', false, path],
467+
['fastify', true, path]
468+
])
469+
470+
await Promise.all([
471+
once(ws, 'close'),
472+
server.close()
473+
])
474+
})

types/index.d.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,19 @@ import {
99

1010
import { ClientOptions, ServerOptions } from 'ws';
1111

12-
type FastifyHttpProxy = FastifyPluginCallback<fastifyHttpProxy.FastifyHttpProxyOptions>;
12+
interface FastifyHttpProxyWebsocketOptionsEnabled {
13+
websocket: true;
14+
wsUpstream?: string;
15+
}
16+
interface FastifyHttpProxyWebsocketOptionsDisabled {
17+
websocket?: false | never;
18+
wsUpstream?: never;
19+
}
20+
21+
type FastifyHttpProxy = FastifyPluginCallback<
22+
fastifyHttpProxy.FastifyHttpProxyOptions
23+
& (FastifyHttpProxyWebsocketOptionsEnabled | FastifyHttpProxyWebsocketOptionsDisabled)
24+
>;
1325

1426
declare namespace fastifyHttpProxy {
1527
export interface FastifyHttpProxyOptions extends FastifyReplyFromOptions {
@@ -21,7 +33,6 @@ declare namespace fastifyHttpProxy {
2133
beforeHandler?: preHandlerHookHandler;
2234
config?: Object;
2335
replyOptions?: FastifyReplyFromHooks;
24-
websocket?: boolean;
2536
wsClientOptions?: ClientOptions;
2637
wsServerOptions?: ServerOptions;
2738
httpMethods?: string[];

types/index.test-d.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,28 @@ app.register(fastifyHttpProxy, {
4242
timeout: 20000
4343
}
4444
},
45-
constraints: { version: '1.0.2' }
45+
constraints: { version: '1.0.2' },
46+
websocket: true,
47+
wsUpstream: 'ws://origin.asd/connection'
4648
});
4749

4850
expectError(
4951
app.register(fastifyHttpProxy, {
5052
thisOptionDoesNotExist: 'triggers a typescript error'
5153
})
5254
);
55+
56+
expectError(
57+
app.register(fastifyHttpProxy, {
58+
upstream: 'http://origin.asd',
59+
wsUpstream: 'ws://origin.asd'
60+
})
61+
);
62+
63+
expectError(
64+
app.register(fastifyHttpProxy, {
65+
upstream: 'http://origin.asd',
66+
websocket: false,
67+
wsUpstream: 'asdf'
68+
})
69+
);

0 commit comments

Comments
 (0)