diff --git a/runtimes/web/src/netplay/peer-manager.ts b/runtimes/web/src/netplay/peer-manager.ts index 10cda0ed..690dac61 100644 --- a/runtimes/web/src/netplay/peer-manager.ts +++ b/runtimes/web/src/netplay/peer-manager.ts @@ -1,5 +1,5 @@ /** WebRTC signaling messages. */ -type Message = WhoAmIRequestMessage | WhoAmIReplyMessage | OfferMessage | AnswerMessage | CandidateMessage | AbortMessage; +type Message = WhoAmIRequestMessage | WhoAmIReplyMessage | OfferMessage | AnswerMessage | CandidateMessage | AbortMessage | KeepaliveMessage; /** Sent by a newly connecting client requesting its peer ID. */ type WhoAmIRequestMessage = { @@ -31,6 +31,10 @@ type AbortMessage = { type: "ABORT"; } +type KeepaliveMessage = { + type: "KEEPALIVE"; +} + /** * Connects to our websocket server for exchanging the signaling messages needed to establish WebRTC * peer-to-peer connections. @@ -38,9 +42,15 @@ type AbortMessage = { class SignalClient { private socket?: WebSocket; private readonly bufferedOutput: string[] = []; + private keepaliveInterval: number; constructor (private onMessage: (source: string, message: Message) => void) { this.connect(); + + // Regularly send messages to keep the websocket connection from an idle timeout + this.keepaliveInterval = window.setInterval(() => { + this.send("", { type: "KEEPALIVE" }, false); + }, 15000); } private async connect (): Promise { @@ -81,18 +91,19 @@ class SignalClient { this.bufferedOutput.length = 0; } - send (target: string, message: Message) { + send (target: string, message: Message, deferIfNotReady = true) { // console.log(`Sent ${message.type} message to ${target}`); const output = JSON.stringify({ target, message }); if (this.socket?.readyState == 1) { this.socket.send(output); - } else { + } else if (deferIfNotReady) { this.bufferedOutput.push(output); } } close () { this.socket?.close(); + window.clearInterval(this.keepaliveInterval); } } diff --git a/runtimes/web/webrtc-signal-server/src/main.ts b/runtimes/web/webrtc-signal-server/src/main.ts index 7d8113ab..386c2351 100644 --- a/runtimes/web/webrtc-signal-server/src/main.ts +++ b/runtimes/web/webrtc-signal-server/src/main.ts @@ -23,20 +23,26 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async ({ body, request if (body) { const { target, message } = JSON.parse(body); - if (message.type === "WHOAMI_REQUEST") { - await sendToPeer("server", requestContext.connectionId, { - type: "WHOAMI_REPLY", - yourPeerId: requestContext.connectionId, - }); - } else { - try { - await sendToPeer(requestContext.connectionId, target, message); - } catch (error) { - // Peer not found, send an abort message - await sendToPeer(target, requestContext.connectionId, { - type: "ABORT", + switch (message.type) { + case "WHOAMI_REQUEST": + await sendToPeer("server", requestContext.connectionId, { + type: "WHOAMI_REPLY", + yourPeerId: requestContext.connectionId, }); - } + break; + case "KEEPALIVE": + // Ignore this message, it only exists to keep the connection open + break; + default: + // Route the message to the given target + try { + await sendToPeer(requestContext.connectionId, target, message); + } catch (error) { + // Peer not found, send an abort message + await sendToPeer(target, requestContext.connectionId, { + type: "ABORT", + }); + } } }