diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eaab670..765dcf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: uses: actions/setup-node@1f8c6b94b26d0feae1e387ca63ccbdc44d27b561 with: always-auth: true - node-version: 16.x + node-version: 18.x - name: Get npm cache directory id: npm-cache-dir diff --git a/Dockerfile b/Dockerfile index 545a8f0..b56cbcc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16 as base +FROM node:18 as base WORKDIR /home/node/app diff --git a/README.md b/README.md index 9f80a51..7396c56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Signaling server +# Signaling Server + +[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ## Development Setup diff --git a/package.json b/package.json index 07462f3..b340fa3 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@types/node": "^18.6.2", "@types/prometheus-gc-stats": "^0.6.2", "@types/sinon": "^10.0.13", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.4", "chai": "^4.3.6", "clinic": "^11.1.2", "mocha": "^10.0.0", @@ -40,7 +40,7 @@ "prometheus-gc-stats": "^0.6.3", "rxjs": "^7.5.6", "tslog": "^4.8.2", - "uWebSockets.js": "uNetworking/uWebSockets.js#v20.10.0", + "uWebSockets.js": "uNetworking/uWebSockets.js#v20.30.0", "zod": "^3.17.10" } } diff --git a/src/server.ts b/src/server.ts index 898a836..c2381f7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -28,6 +28,13 @@ import { RemoteData, } from './messages/_types' +type Data = { + id: string + connectionId: string + source: string + targetClient: string +} + const collectDefaultMetrics = prometheusClient.collectDefaultMetrics collectDefaultMetrics() @@ -83,7 +90,7 @@ const server = async () => { return !!(await redis.publisher.sIsMember(key, value)) } - const subscribe = async (ws: uWs.WebSocket, dataChanel: string) => { + const subscribe = async (ws: uWs.WebSocket, dataChanel: string) => { const t0 = performance.now() await redis.subscriber.subscribe(dataChanel, (raw) => { parseJSON< @@ -100,7 +107,7 @@ const server = async () => { redisSubscribeTime.set(t1 - t0) } - const send = (ws: uWs.WebSocket, message: MessageTypes) => { + const send = (ws: uWs.WebSocket, message: MessageTypes) => { try { log.trace({ event: 'SendWSMessage', @@ -121,6 +128,9 @@ const server = async () => { uWs .App() .ws('/*', { + sendPingsAutomatically: true, + idleTimeout: 0, + maxLifetime: 0, upgrade: (res, req, context) => { res.upgrade( { @@ -133,11 +143,12 @@ const server = async () => { context ) }, - open: async (ws) => { + open: async (ws: uWs.WebSocket) => { try { + console.log(ws) ++connections connectedClientsGauge.set(connections) - const url: URL = ws.url + const url: URL = (ws as any).url const connectionId = url.pathname.slice(1) const target = url.searchParams.get('target') const source = url.searchParams.get('source') @@ -162,15 +173,15 @@ const server = async () => { } const websocketId = randomUUID() - ws.id = websocketId - ws.connectionId = connectionId - ws.source = source - ws.targetClient = `${ws.connectionId}:${target}` + ws.getUserData().id = websocketId + ws.getUserData().connectionId = connectionId + ws.getUserData().source = source + ws.getUserData().targetClient = `${connectionId}:${target}` const [, , targetWebsocketIds] = await Promise.all([ subscribe(ws, websocketId), setData(`${connectionId}:${source}`, websocketId), - getTargetWebsocketIds(ws.targetClient), + getTargetWebsocketIds(ws.getUserData().targetClient), ]) log.trace({ @@ -227,11 +238,13 @@ const server = async () => { }) } - if (await isMember(ws.targetClient, parsed.targetClientId)) { + if ( + await isMember(ws.getUserData().targetClient, parsed.targetClientId) + ) { await publish(parsed.targetClientId, { info: 'remoteData', data: parsed, - remoteClientId: ws.id, + remoteClientId: ws.getUserData().id, requestId: parsed.requestId, }) } else { @@ -253,24 +266,27 @@ const server = async () => { --connections connectedClientsGauge.set(connections) try { - if (ws.id) { + if (ws.getUserData().id) { const targetWebsocketIds = await getTargetWebsocketIds( - ws.targetClient + ws.getUserData().targetClient ) if (targetWebsocketIds && targetWebsocketIds.length) { await Promise.all( targetWebsocketIds.map((targetWebsocketId) => publish(targetWebsocketId, { info: 'remoteClientDisconnected', - remoteClientId: ws.id, + remoteClientId: ws.getUserData().id, }) ) ) } await Promise.all([ - redis.subscriber.unsubscribe(ws.id), - removeData(`${ws.connectionId}:${ws.source}`, ws.id), + redis.subscriber.unsubscribe(ws.getUserData().id), + removeData( + `${ws.getUserData().connectionId}:${ws.getUserData().source}`, + ws.getUserData().id + ), ]) } diff --git a/src/utils/rate-limit.ts b/src/utils/rate-limit.ts index 2813799..36acf51 100644 --- a/src/utils/rate-limit.ts +++ b/src/utils/rate-limit.ts @@ -6,12 +6,12 @@ const RateLimit = (limit: number, interval: number) => { const last = Symbol() as unknown as string const count = Symbol() as unknown as string setInterval(() => ++now, interval) - return (ws: uWs.WebSocket) => { - if (ws[last] != now) { - ws[last] = now - ws[count] = 1 + return (ws: uWs.WebSocket) => { + if (ws.getUserData()[last] != now) { + ws.getUserData()[last] = now + ws.getUserData()[count] = 1 } else { - return ++ws[count] > limit + return ++ws.getUserData()[count] > limit } } } diff --git a/yarn.lock b/yarn.lock index 0fcfd97..a458386 100644 --- a/yarn.lock +++ b/yarn.lock @@ -360,10 +360,10 @@ resolved "https://registry.yarnpkg.com/@types/webgl-ext/-/webgl-ext-0.0.30.tgz#0ce498c16a41a23d15289e0b844d945b25f0fb9d" integrity sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg== -"@types/ws@^8.5.3": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== +"@types/ws@^8.5.4": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" @@ -5315,9 +5315,9 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== -uWebSockets.js@uNetworking/uWebSockets.js#v20.10.0: - version "20.10.0" - resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/806df48c9da86af7b3341f3e443388c7cd15c3de" +uWebSockets.js@uNetworking/uWebSockets.js#v20.30.0: + version "20.30.0" + resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/d39d4181daf5b670d44cbc1b18f8c28c85fd4142" umd@^3.0.0: version "3.0.3"