The room id was read from the query string with a hand-rolled parser that
never decoded it, while the id was written back into the URL without any
encoding:
getFromURL = (key, search) =>
search.slice(1).split('&')
.find((param) => param.startsWith(`${key}=`))?.split('=')[1];
window.history.pushState({roomId: id}, '', id ? `?room=${id}` : '?');
Browsers percent-encode non-ASCII characters when a URL is serialized, so a
room named "不辞" is shared as `?room=%E4%B8%8D%E8%BE%9E`, but the client then
joins the literal `%E4%B8%8D%E8%BE%9E` room, which does not exist:
-> {"type":"join","payload":{"id":"%E4%B8%8D%E8%BE%9E"}}
<- close 1000 "room with id %E4%B8%8D%E8%BE%9E does not exist"
The same asymmetry also broke ids containing `&`, `=`, `+`, `#` or `%`, and
made ids that already contain percent escapes gain another encoding level on
every write, producing broken (double encoded) share links.
Use the platform URL API, which decodes on read and encodes on write exactly
once:
new URLSearchParams(search).get(key) ?? undefined
const params = new URLSearchParams();
params.set('room', id);
window.history.pushState({roomId: id}, '', `?${params.toString()}`);
No server change is needed: the room id stays the raw, human-readable string
that the ws `create`/`join` payload and the server-side room map already use,
and existing share links keep working.
Verified: URL round-trip for 不辞 / "my room" / "a+b" / "a&b=c" / "100%",
protocol-level e2e against a live server (encoded id -> "does not exist",
decoded id -> joins, cleanup on owner leave), `yarn build` (tsc + vite) and
`prettier --check src/useRoomID.ts`.
Both connectionstatechange handlers treated 'disconnected' the same as
'closed' and 'failed' and immediately called peer.close().
Per the WebRTC spec, 'disconnected' only means that ICE connectivity checks
are currently failing. The connection frequently recovers on its own -- brief
network hiccups, Wi-Fi roaming between access points, or NAT rebinding all
trigger it. Calling close() makes that recovery impossible, because a closed
RTCPeerConnection cannot be reopened.
The result was that a short network glitch permanently killed the stream and
the viewer had to reload the page. Leaving 'disconnected' unhandled lets the
browser's own ICE recovery do its job.
Refs #53