mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-23 22:47:19 +00:00
fix(puter-js): ignore peer offers for unknown connections (#3473)
createAnswer ran outside the connection null-check, so stale or unknown offer ids threw on the signalling socket. Guard the whole offer path and return the #message promise from onmessage so callers can observe errors.
This commit is contained in:
@@ -44,7 +44,7 @@ class PuterPeerConnectionErrorEvent extends Event {
|
||||
}
|
||||
}
|
||||
|
||||
class PuterPeerServer extends EventTarget {
|
||||
export class PuterPeerServer extends EventTarget {
|
||||
#wsconn;
|
||||
#oncreateresolve;
|
||||
|
||||
@@ -83,7 +83,7 @@ class PuterPeerServer extends EventTarget {
|
||||
|
||||
this.#wsconn.onmessage = (event) => {
|
||||
let data = JSON.parse(event.data);
|
||||
this.#message(data);
|
||||
return this.#message(data);
|
||||
};
|
||||
|
||||
this.#wsconn.onclose = () => {
|
||||
@@ -168,12 +168,12 @@ class PuterPeerServer extends EventTarget {
|
||||
if ( data.server.offer ) {
|
||||
let uuid = data.server.offer.id;
|
||||
let connection = this.connections.get(uuid);
|
||||
if ( connection ) {
|
||||
await connection.setRemoteDescription(
|
||||
new RTCSessionDescription(data.server.offer.offer),
|
||||
);
|
||||
if ( ! connection ) {
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.setRemoteDescription(
|
||||
new RTCSessionDescription(data.server.offer.offer),
|
||||
);
|
||||
const answer = await connection.createAnswer();
|
||||
this.#wsconn.send(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { PuterPeerServer } from './Peer.js';
|
||||
|
||||
/**
|
||||
* Orphan offers (unknown connection id) used to call createAnswer on
|
||||
* undefined after the setRemoteDescription guard, producing an unhandled
|
||||
* rejection on the signalling websocket.
|
||||
*/
|
||||
|
||||
class FakeWebSocket {
|
||||
static latest = null;
|
||||
sent = [];
|
||||
onopen = null;
|
||||
onmessage = null;
|
||||
onerror = null;
|
||||
onclose = null;
|
||||
|
||||
constructor () {
|
||||
FakeWebSocket.latest = this;
|
||||
}
|
||||
|
||||
send (data) {
|
||||
this.sent.push(data);
|
||||
}
|
||||
|
||||
close () {}
|
||||
}
|
||||
|
||||
const origWebSocket = globalThis.WebSocket;
|
||||
|
||||
beforeEach(() => {
|
||||
FakeWebSocket.latest = null;
|
||||
globalThis.WebSocket = FakeWebSocket;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.WebSocket = origWebSocket;
|
||||
});
|
||||
|
||||
const flushMicrotasks = async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
};
|
||||
|
||||
const startServer = async () => {
|
||||
const server = new PuterPeerServer({
|
||||
signallerUrl: 'ws://signaller.test/',
|
||||
authToken: 'token',
|
||||
});
|
||||
const started = server.start();
|
||||
// Resolve the open handshake, then let start() install onmessage.
|
||||
FakeWebSocket.latest.onopen();
|
||||
await flushMicrotasks();
|
||||
await FakeWebSocket.latest.onmessage({
|
||||
data: JSON.stringify({
|
||||
server: { create: { success: true, invitecode: 'invite-1' } },
|
||||
}),
|
||||
});
|
||||
await started;
|
||||
return server;
|
||||
};
|
||||
|
||||
describe('PuterPeerServer orphan offers', () => {
|
||||
it('ignores offers for unknown connection ids without throwing', async () => {
|
||||
await startServer();
|
||||
const ws = FakeWebSocket.latest;
|
||||
const sentBefore = ws.sent.length;
|
||||
|
||||
await expect(ws.onmessage({
|
||||
data: JSON.stringify({
|
||||
server: {
|
||||
offer: {
|
||||
id: 'missing-connection',
|
||||
offer: { type: 'offer', sdp: 'v=0' },
|
||||
},
|
||||
},
|
||||
}),
|
||||
})).resolves.toBeUndefined();
|
||||
|
||||
expect(ws.sent.length).toBe(sentBefore);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user