diff --git a/package.json b/package.json index 03ec6fc..85e1437 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,10 @@ "author": "Dave Honneffer ", "license": "MIT", "dependencies": { - "@fastify/http-proxy": "^7.0.0", - "@fastify/static": "^5.0.0", + "@fastify/http-proxy": "^7.1.0", + "@fastify/static": "^5.0.2", "date-time": "^4.0.0", - "fastify": "^3.28.0", + "fastify": "^3.29.0", "fastify-plugin": "^3.0.1", "fastify-request-timing": "^2.0.1", "femtocolor": "^2.0.2", @@ -56,8 +56,9 @@ }, "devDependencies": { "drnm": "^0.9.0", - "eslint": "^8.14.0", - "rollup": "^2.70.2", - "uvu": "^0.5.2" + "eslint": "^8.15.0", + "rollup": "^2.73.0", + "uvu": "^0.5.2", + "ws": "^8.6.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 448cacf..f583682 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,26 +1,27 @@ lockfileVersion: 5.3 specifiers: - '@fastify/http-proxy': ^7.0.0 - '@fastify/static': ^5.0.0 + '@fastify/http-proxy': ^7.1.0 + '@fastify/static': ^5.0.2 date-time: ^4.0.0 drnm: ^0.9.0 - eslint: ^8.14.0 - fastify: ^3.28.0 + eslint: ^8.15.0 + fastify: ^3.29.0 fastify-plugin: ^3.0.1 fastify-request-timing: ^2.0.1 femtocolor: ^2.0.2 get-port: ^5.1.1 joi: ^17.4.2 ms: ^2.1.3 - rollup: ^2.70.2 + rollup: ^2.73.0 uvu: ^0.5.2 + ws: ^8.6.0 dependencies: - '@fastify/http-proxy': 7.0.0 - '@fastify/static': 5.0.0 + '@fastify/http-proxy': 7.1.0 + '@fastify/static': 5.0.2 date-time: 4.0.0 - fastify: 3.28.0 + fastify: 3.29.0 fastify-plugin: 3.0.1 fastify-request-timing: 2.0.1 femtocolor: 2.0.3 @@ -30,20 +31,21 @@ dependencies: devDependencies: drnm: 0.9.0 - eslint: 8.14.0 - rollup: 2.70.2 + eslint: 8.15.0 + rollup: 2.73.0 uvu: 0.5.3 + ws: 8.6.0 packages: - /@eslint/eslintrc/1.2.2: - resolution: {integrity: sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==} + /@eslint/eslintrc/1.2.3: + resolution: {integrity: sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.3.1 - globals: 13.13.0 + espree: 9.3.2 + globals: 13.15.0 ignore: 5.2.0 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -59,36 +61,40 @@ packages: ajv: 6.12.6 dev: false - /@fastify/http-proxy/7.0.0: - resolution: {integrity: sha512-nkx8NedKDNrnoxwzFsKUjexi2b5Tgw8IMaTKUK/BtZDop5eM86j3kEjecnIT25qTpxymvIFDsuTLk4iVmt2G3A==} + /@fastify/error/2.0.0: + resolution: {integrity: sha512-wI3fpfDT0t7p8E6dA2eTECzzOd+bZsZCJ2Hcv+Onn2b7ZwK3RwD27uW2QDaMtQhAfWQQP+WNK7nKf0twLsBf9w==} + dev: false + + /@fastify/http-proxy/7.1.0: + resolution: {integrity: sha512-G15TPeO657N8n4mhlGvbKneZH3exiV8fjbSoJgzcgwzaEmidc+4v3Xg/loTp21palsq+hQPlzVl0eHSzDTAuKA==} dependencies: - '@fastify/reply-from': 7.0.0 - ws: 8.5.0 + '@fastify/reply-from': 7.0.1 + ws: 8.6.0 transitivePeerDependencies: - bufferutil - utf-8-validate dev: false - /@fastify/reply-from/7.0.0: - resolution: {integrity: sha512-La5+sAjrhPuxFjbq2u/fnqRPsHNA94STBx4Guz0/VK3sN/m3JJB6k4rkfogNKEt9QrWH+ZVcfQWrWuPoDS+Pvg==} + /@fastify/reply-from/7.0.1: + resolution: {integrity: sha512-ikp6GpmEJ7AVxcDdSVE9MhpUtC9KnImQDegc5ePZ+H7QZcraIjotP7YndwT/fP8lYj2Qr1h4RtuFNU8Wdwleuw==} engines: {node: '>=12.18'} dependencies: end-of-stream: 1.4.4 fastify-plugin: 3.0.1 http-errors: 2.0.0 pump: 3.0.0 - semver: 7.3.5 + semver: 7.3.7 tiny-lru: 8.0.2 - undici: 5.0.0 + undici: 5.2.0 dev: false - /@fastify/static/5.0.0: - resolution: {integrity: sha512-GGltJkO0idXa7yCZ0PfdTZ6qokWDX/vigCvmRpjOU2A3jc93c9p+oHDvHmwHK60hwWoBEGqHjGofVyn3H1CjZg==} + /@fastify/static/5.0.2: + resolution: {integrity: sha512-HvyXZ5a7hUHoSBRq9jKUuKIUCkHMkCDcmiAeEmixXlGOx8pEWx3NYOIaiivcjWa6/NLvfdUT+t/jzfVQ2PA7Gw==} dependencies: content-disposition: 0.5.4 encoding-negotiator: 2.0.1 fastify-plugin: 3.0.1 - glob: 7.2.0 + glob: 7.2.3 p-limit: 3.1.0 readable-stream: 3.6.0 send: 0.17.2 @@ -181,7 +187,7 @@ packages: dev: true /archy/1.0.0: - resolution: {integrity: sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=} + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: false /argparse/2.0.1: @@ -247,8 +253,8 @@ packages: safe-buffer: 5.2.1 dev: false - /cookie/0.4.2: - resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + /cookie/0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} dev: false @@ -366,13 +372,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.14.0: + /eslint-utils/3.0.0_eslint@8.15.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.14.0 + eslint: 8.15.0 eslint-visitor-keys: 2.1.0 dev: true @@ -386,12 +392,12 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.14.0: - resolution: {integrity: sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==} + /eslint/8.15.0: + resolution: {integrity: sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint/eslintrc': 1.2.2 + '@eslint/eslintrc': 1.2.3 '@humanwhocodes/config-array': 0.9.5 ajv: 6.12.6 chalk: 4.1.2 @@ -400,16 +406,16 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.14.0 + eslint-utils: 3.0.0_eslint@8.15.0 eslint-visitor-keys: 3.3.0 - espree: 9.3.1 + espree: 9.3.2 esquery: 1.4.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 functional-red-black-tree: 1.0.1 glob-parent: 6.0.2 - globals: 13.13.0 + globals: 13.15.0 ignore: 5.2.0 import-fresh: 3.3.0 imurmurhash: 0.1.4 @@ -430,8 +436,8 @@ packages: - supports-color dev: true - /espree/9.3.1: - resolution: {integrity: sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==} + /espree/9.3.2: + resolution: {integrity: sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.7.1 @@ -501,10 +507,6 @@ packages: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} dev: false - /fastify-error/0.3.1: - resolution: {integrity: sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ==} - dev: false - /fastify-plugin/3.0.1: resolution: {integrity: sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==} dev: false @@ -515,17 +517,17 @@ packages: fastify-plugin: 3.0.1 dev: false - /fastify/3.28.0: - resolution: {integrity: sha512-LAQtGllpkRe8L6Tpf3zdbvXzXFOrgaWV3Tbvp3xMv9ngcr9zht9U2/mo5zq9qp9kplSiBJ0w43aVAMqv6PBMbw==} + /fastify/3.29.0: + resolution: {integrity: sha512-zXSiDTdHJCHcmDrSje1f1RfzTmUTjMtHnPhh6cdokgfHhloQ+gy0Du+KlEjwTbcNC3Djj4GAsBzl6KvfI9Ah2g==} dependencies: '@fastify/ajv-compiler': 1.1.0 + '@fastify/error': 2.0.0 abstract-logging: 2.0.1 avvio: 7.2.5 fast-json-stringify: 2.7.13 - fastify-error: 0.3.1 find-my-way: 4.5.1 flatstr: 1.0.12 - light-my-request: 4.9.0 + light-my-request: 4.10.1 pino: 6.14.0 process-warning: 1.0.0 proxy-addr: 2.0.7 @@ -617,18 +619,18 @@ packages: is-glob: 4.0.3 dev: true - /glob/7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.1 + minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - /globals/13.13.0: - resolution: {integrity: sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==} + /globals/13.15.0: + resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -750,11 +752,11 @@ packages: type-check: 0.4.0 dev: true - /light-my-request/4.9.0: - resolution: {integrity: sha512-b1U3z4OVPoO/KanT14NRkXMr9rRtXAiq0ORqNrqhDyb5bGkZjAdEc6GRN1GWCfgaLBG+aq73qkCLDNeB3c2sLw==} + /light-my-request/4.10.1: + resolution: {integrity: sha512-l+zWk0HXGhGzY7IYTZnYEqIpj3Mpcyk2f8+FkKUyREywvaiWCf2jyQVxpasKRsploY/nVpoqTlxx72CIeQNcIQ==} dependencies: ajv: 8.11.0 - cookie: 0.4.2 + cookie: 0.5.0 process-warning: 1.0.0 set-cookie-parser: 2.4.8 dev: false @@ -776,16 +778,10 @@ packages: hasBin: true dev: false - /minimatch/3.1.1: - resolution: {integrity: sha512-reLxBcKUPNBnc/sVtAbxgRVFSegoGeLaSjmphNhcwcolhYLRgtJscn5mRl6YRZNQv40Y7P6JM2YhSIsbL9OB5A==} - dependencies: - brace-expansion: 1.1.11 - /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - dev: true /mri/1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} @@ -954,11 +950,11 @@ packages: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: - glob: 7.2.0 + glob: 7.2.3 dev: true - /rollup/2.70.2: - resolution: {integrity: sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==} + /rollup/2.73.0: + resolution: {integrity: sha512-h/UngC3S4Zt28mB3g0+2YCMegT5yoftnQplwzPqGZcKvlld5e+kT/QRmJiL+qxGyZKOYpgirWGdLyEO1b0dpLQ==} engines: {node: '>=10.0.0'} hasBin: true optionalDependencies: @@ -990,14 +986,6 @@ packages: resolution: {integrity: sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==} dev: false - /semver/7.3.5: - resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: false - /semver/7.3.7: resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} engines: {node: '>=10'} @@ -1122,8 +1110,8 @@ packages: engines: {node: '>=10'} dev: true - /undici/5.0.0: - resolution: {integrity: sha512-VhUpiZ3No1DOPPQVQnsDZyfcbTTcHdcgWej1PdFnSvOeJmOVDgiOHkunJmBLfmjt4CqgPQddPVjSWW0dsTs5Yg==} + /undici/5.2.0: + resolution: {integrity: sha512-XY6+NS3WH9b3TKOHeNz2CjR+qrVz/k4fO9g3etPpLozRvULoQmZ1+dk9JbIz40ehn27xzFk4jYVU2MU3Nle62A==} engines: {node: '>=12.18'} dev: false @@ -1167,8 +1155,8 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} - /ws/8.5.0: - resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} + /ws/8.6.0: + resolution: {integrity: sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -1178,7 +1166,6 @@ packages: optional: true utf-8-validate: optional: true - dev: false /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} diff --git a/test/proxy-ws.test.js b/test/proxy-ws.test.js new file mode 100644 index 0000000..151a696 --- /dev/null +++ b/test/proxy-ws.test.js @@ -0,0 +1,59 @@ +import { suite } from 'uvu' +import * as assert from 'uvu/assert' +import { init } from '#init' +import { serverDefaults } from '#config' +import WebSocket from 'ws' +import { createServer } from 'http' +import { promisify } from 'util' +import { once } from 'events' + +const ProxyWs = suite('Proxy-websocket') + +ProxyWs.before(async t => { + t.origin = createServer() + t.wss = new WebSocket.Server({ server: t.origin }) + + await promisify(t.origin.listen.bind(t.origin))({ port: 0 }) + try { + t.server = await init({ + dirs: ['support'], + proxy: [ + { from: '/my-wss', to: `ws://localhost:${t.origin.address().port}`, opts: { websocket: true } } + ], + server: { ...serverDefaults } + }) + await t.server.listen({ port: 0 }) + } catch (err) { + console.error(err) + } +}) + +ProxyWs.after(async t => { + t.wss.close() + t.origin.close() +}) + +ProxyWs('basic websocket proxy', async t => { + const cookieValue = 'foo=bar' + t.wss.on('connection', ws => { + ws.on('message', message => { + assert.is(message.toString(), 'hello') + ws.send(message) + }) + }) + + const options = { headers: { Cookie: cookieValue } } + const ws = new WebSocket(`ws://localhost:${t.server.server.address().port}/my-wss`, options) + await once(ws, 'open') + const stream = WebSocket.createWebSocketStream(ws) + stream.write('hello') + const [buf] = await once(stream, 'data') + assert.is(buf.toString(), 'hello') + + await Promise.all([ + once(ws, 'close'), + t.server.close() + ]) +}) + +ProxyWs.run()