From bb9ddd33bd4c21c9b5762cbc29c62c684e626f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrg=C3=BCn=20Day=C4=B1o=C4=9Flu?= Date: Sat, 27 Apr 2024 15:00:22 +0200 Subject: [PATCH] Update for Fastify v5 (#347) * update * remove coverage check * update ci --- .github/workflows/ci.yml | 2 +- .taprc | 7 +--- index.js | 31 +++++++------- package.json | 36 ++++++++-------- test/websocket-pathname.js | 75 ++++++++++++++++++++++++++++++++++ test/websocket.js | 67 ------------------------------ test/ws-prefix-rewrite-core.js | 9 ++-- test/ws-prefix-rewrite.js | 9 ++-- 8 files changed, 123 insertions(+), 113 deletions(-) create mode 100644 test/websocket-pathname.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index babd56d..444baa7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,6 @@ on: jobs: test: - uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3 + uses: fastify/workflows/.github/workflows/plugins-ci.yml@v4.1.0 with: license-check: true diff --git a/.taprc b/.taprc index 530de00..4452414 100644 --- a/.taprc +++ b/.taprc @@ -1,5 +1,2 @@ -ts: false -jsx: false -flow: false -coverage: true -check-coverage: true +files: + - test/**/*.js diff --git a/index.js b/index.js index 4f24ca1..f7c8470 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,7 @@ const kWsHead = Symbol('wsHead') const kWsUpgradeListener = Symbol('wsUpgradeListener') function liftErrorCode (code) { - /* istanbul ignore next */ + /* c8 ignore start */ if (typeof code !== 'number') { // Sometimes "close" event emits with a non-numeric value return 1011 @@ -23,6 +23,7 @@ function liftErrorCode (code) { } else { return code } + /* c8 ignore stop */ } function closeWebSocket (socket, code, reason) { @@ -52,27 +53,27 @@ function proxyWebSockets (source, target) { } source.on('message', (data, binary) => waitConnection(target, () => target.send(data, { binary }))) - /* istanbul ignore next */ + /* c8 ignore start */ source.on('ping', data => waitConnection(target, () => target.ping(data))) - /* istanbul ignore next */ source.on('pong', data => waitConnection(target, () => target.pong(data))) + /* c8 ignore stop */ source.on('close', close) - /* istanbul ignore next */ + /* c8 ignore start */ source.on('error', error => close(1011, error.message)) - /* istanbul ignore next */ source.on('unexpected-response', () => close(1011, 'unexpected response')) + /* c8 ignore stop */ // source WebSocket is already connected because it is created by ws server target.on('message', (data, binary) => source.send(data, { binary })) - /* istanbul ignore next */ + /* c8 ignore start */ target.on('ping', data => source.ping(data)) - /* istanbul ignore next */ + /* c8 ignore stop */ target.on('pong', data => source.pong(data)) target.on('close', close) - /* istanbul ignore next */ + /* c8 ignore start */ target.on('error', error => close(1011, error.message)) - /* istanbul ignore next */ target.on('unexpected-response', () => close(1011, 'unexpected response')) + /* c8 ignore stop */ } function handleUpgrade (fastify, rawRequest, socket, head) { @@ -129,7 +130,6 @@ class WebSocketProxy { fastify.server.close = function (done) { wss.close(() => { oldClose.call(this, (err) => { - /* istanbul ignore next */ done && done(err) }) }) @@ -139,11 +139,11 @@ class WebSocketProxy { } } - /* istanbul ignore next */ + /* c8 ignore start */ wss.on('error', (err) => { - /* istanbul ignore next */ this.logger.error(err) }) + /* c8 ignore stop */ this.wss = wss this.prefixList = [] @@ -167,7 +167,7 @@ class WebSocketProxy { const upstream = this.getUpstream(request, '') const target = new URL(dest, upstream) - /* istanbul ignore next */ + /* c8 ignore next */ target.protocol = upstream.indexOf('http:') === 0 ? 'ws:' : 'wss' target.search = search return target @@ -314,10 +314,9 @@ async function fastifyHttpProxy (fastify, opts) { reply.hijack() try { wsProxy.handleUpgrade(request, dest || '/', noop) - } catch (err) { - /* istanbul ignore next */ + } /* c8 ignore start */ catch (err) { request.log.warn({ err }, 'websocket proxy error') - } + } /* c8 ignore stop */ return } reply.from(dest || '/', replyOpts) diff --git a/package.json b/package.json index 9ef5a2e..3848d1f 100644 --- a/package.json +++ b/package.json @@ -28,34 +28,34 @@ }, "homepage": "https://github.com/fastify/fastify-http-proxy#readme", "devDependencies": { - "@fastify/pre-commit": "^2.0.2", - "@fastify/websocket": "^9.0.0", - "@types/node": "^20.1.0", - "@types/ws": "^8.2.2", - "@typescript-eslint/eslint-plugin": "^7.1.0", - "@typescript-eslint/parser": "^7.1.0", - "express": "^4.17.2", + "@fastify/pre-commit": "^2.1.0", + "@fastify/websocket": "^10.0.1", + "@types/node": "^20.12.7", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^7.6.0", + "@typescript-eslint/parser": "^7.6.0", + "express": "^4.19.2", "express-http-proxy": "^2.0.0", "fast-proxy": "^2.1.0", - "fastify": "^4.0.0-rc.2", - "got": "^11.8.3", + "fastify": "^4.26.2", + "got": "^11.8.6", "http-errors": "^2.0.0", "http-proxy": "^1.18.1", - "simple-get": "^4.0.0", + "simple-get": "^4.0.1", "snazzy": "^9.0.0", - "socket.io": "^4.4.1", - "socket.io-client": "^4.4.1", - "standard": "^17.0.0", - "tap": "^16.0.0", + "socket.io": "^4.7.5", + "socket.io-client": "^4.7.5", + "standard": "^17.1.0", + "tap": "^18.7.2", "tsd": "^0.31.0", - "typescript": "^5.0.2", + "typescript": "^5.4.5", "why-is-node-running": "^2.2.2" }, "dependencies": { - "@fastify/reply-from": "^9.0.0", + "@fastify/reply-from": "^9.7.0", "fast-querystring": "^1.1.2", - "fastify-plugin": "^4.5.0", - "ws": "^8.4.2" + "fastify-plugin": "^4.5.1", + "ws": "^8.16.0" }, "tsd": { "directory": "test/types" diff --git a/test/websocket-pathname.js b/test/websocket-pathname.js new file mode 100644 index 0000000..afaea07 --- /dev/null +++ b/test/websocket-pathname.js @@ -0,0 +1,75 @@ +const { test } = require('tap') +const Fastify = require('fastify') +const proxy = require('..') +const WebSocket = require('ws') +const { createServer } = require('node:http') +const { promisify } = require('node:util') +const { once } = require('node:events') + +// TODO: this test is flaky, probably because of promise resolution +test('keep proxy websocket pathname', async (t) => { + t.plan(5) + + const origin = createServer() + const wss = new WebSocket.Server({ server: origin }) + + t.teardown(wss.close.bind(wss)) + t.teardown(origin.close.bind(origin)) + + const serverMessages = [] + wss.on('connection', (ws, request) => { + ws.on('message', (message, binary) => { + // Also need save request.url for check from what url the message is coming. + serverMessages.push([message.toString(), binary, request.headers.host.split(':', 1)[0], request.url]) + ws.send(message, { binary }) + }) + }) + + await promisify(origin.listen.bind(origin))({ port: 0, host: '127.0.0.1' }) + // Host for wsUpstream and for later check. + const host = '127.0.0.1' + // Path for wsUpstream and for later check. + const path = '/keep/path' + const server = Fastify() + server.register(proxy, { + upstream: `ws://127.0.0.1:${origin.address().port}`, + // Start proxy with different upstream, without path + wsUpstream: `ws://${host}:${origin.address().port}`, + websocket: true + }) + + await server.listen({ port: 0, host: '127.0.0.1' }) + t.teardown(server.close.bind(server)) + + // Start websocket with different upstream for connect, added path. + const ws = new WebSocket(`ws://${host}:${server.server.address().port}${path}`) + await once(ws, 'open') + + const data = [{ message: 'hello', binary: false }, { message: 'fastify', binary: true, isBuffer: true }] + const dataLength = data.length + let dataIndex = 0 + + for (; dataIndex < dataLength; dataIndex++) { + const { message: msg, binary, isBuffer } = data[dataIndex] + const message = isBuffer + ? Buffer.from(msg) + : msg + + ws.send(message, { binary }) + + const [reply, binaryAnswer] = await once(ws, 'message') + + t.equal(reply.toString(), msg) + t.equal(binaryAnswer, binary) + } + // Also check "path", must be the same. + t.strictSame(serverMessages, [ + ['hello', false, host, path], + ['fastify', true, host, path] + ]) + + await Promise.all([ + once(ws, 'close'), + server.close() + ]) +}) diff --git a/test/websocket.js b/test/websocket.js index a711895..b08395e 100644 --- a/test/websocket.js +++ b/test/websocket.js @@ -574,70 +574,3 @@ test('multiple websocket upstreams with distinct server options', async (t) => { server.close() ]) }) - -test('keep proxy websocket pathname', async (t) => { - t.plan(5) - - const origin = createServer() - const wss = new WebSocket.Server({ server: origin }) - - t.teardown(wss.close.bind(wss)) - t.teardown(origin.close.bind(origin)) - - const serverMessages = [] - wss.on('connection', (ws, request) => { - ws.on('message', (message, binary) => { - // Also need save request.url for check from what url the message is coming. - serverMessages.push([message.toString(), binary, request.headers.host.split(':', 1)[0], request.url]) - ws.send(message, { binary }) - }) - }) - - await promisify(origin.listen.bind(origin))({ port: 0, host: '127.0.0.1' }) - // Host for wsUpstream and for later check. - const host = '127.0.0.1' - // Path for wsUpstream and for later check. - const path = '/keep/path' - const server = Fastify() - server.register(proxy, { - upstream: `ws://127.0.0.1:${origin.address().port}`, - // Start proxy with different upstream, without path - wsUpstream: `ws://${host}:${origin.address().port}`, - websocket: true - }) - - await server.listen({ port: 0, host: '127.0.0.1' }) - t.teardown(server.close.bind(server)) - - // Start websocket with different upstream for connect, added path. - const ws = new WebSocket(`ws://${host}:${server.server.address().port}${path}`) - await once(ws, 'open') - - const data = [{ message: 'hello', binary: false }, { message: 'fastify', binary: true, isBuffer: true }] - const dataLength = data.length - let dataIndex = 0 - - for (; dataIndex < dataLength; dataIndex++) { - const { message: msg, binary, isBuffer } = data[dataIndex] - const message = isBuffer - ? Buffer.from(msg) - : msg - - ws.send(message, { binary }) - - const [reply, binaryAnswer] = await once(ws, 'message') - - t.equal(reply.toString(), msg) - t.equal(binaryAnswer, binary) - } - // Also check "path", must be the same. - t.strictSame(serverMessages, [ - ['hello', false, host, path], - ['fastify', true, host, path] - ]) - - await Promise.all([ - once(ws, 'close'), - server.close() - ]) -}) diff --git a/test/ws-prefix-rewrite-core.js b/test/ws-prefix-rewrite-core.js index ccbd4ae..77c7b4c 100644 --- a/test/ws-prefix-rewrite-core.js +++ b/test/ws-prefix-rewrite-core.js @@ -76,9 +76,12 @@ async function handleProxy (info, { backendPath, proxyOptions, wrapperOptions }, handler: (req, reply) => { reply.send(req.url) }, - wsHandler: (conn, req) => { - conn.write(req.url) - conn.end() + wsHandler: (socket, req) => { + socket.send(req.url) + + socket.once('message', chunk => { + socket.close() + }) } }) diff --git a/test/ws-prefix-rewrite.js b/test/ws-prefix-rewrite.js index 58af545..ca74a71 100644 --- a/test/ws-prefix-rewrite.js +++ b/test/ws-prefix-rewrite.js @@ -74,9 +74,12 @@ async function handleProxy (info, { backendPath, proxyOptions, wrapperOptions }, handler: (req, reply) => { reply.send(req.url) }, - wsHandler: (conn, req) => { - conn.write(req.url) - conn.end() + wsHandler: (socket, req) => { + socket.send(req.url) + + socket.once('message', chunk => { + socket.close() + }) } })