Refactor server stuff
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/// <reference lib="deno.ns" />
|
||||
import { assertEquals } from "jsr:@std/assert";
|
||||
import { close, connect, flush, href, setup } from "../testSetup.ts";
|
||||
import { getHandler } from "./deno.ts";
|
||||
|
||||
Deno.test(
|
||||
"Welcome all clients on connect.",
|
||||
setup(async () => {
|
||||
const [s1, m1] = await connect(href);
|
||||
const [s2, m2] = await connect(href);
|
||||
|
||||
await flush();
|
||||
|
||||
assertEquals(m1, ["Welcome 1, type /join <room>."]);
|
||||
assertEquals(m2, ["Welcome 2, type /join <room>."]);
|
||||
|
||||
await close(s2);
|
||||
await close(s1);
|
||||
}, getHandler()),
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"Messages all clients in a room on joining.",
|
||||
setup(async () => {
|
||||
const [s1, m1] = await connect(href);
|
||||
const [s2, m2] = await connect(href);
|
||||
|
||||
await flush();
|
||||
m1.length = 0;
|
||||
m2.length = 0;
|
||||
|
||||
s1.send("/join meh");
|
||||
s2.send("/join meh");
|
||||
await flush();
|
||||
|
||||
assertEquals(m1, ["1 has joined!", "2 has joined!"]);
|
||||
assertEquals(m2, ["2 has joined!"]);
|
||||
|
||||
await close(s2);
|
||||
await close(s1);
|
||||
}, getHandler()),
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"Messages remaining members if one left the room.",
|
||||
setup(async () => {
|
||||
const [s1, m1] = await connect(href);
|
||||
const [s2, m2] = await connect(href);
|
||||
|
||||
s1.send("/join meh");
|
||||
s2.send("/join meh");
|
||||
await flush();
|
||||
|
||||
m1.length = 0;
|
||||
m2.length = 0;
|
||||
await close(s2);
|
||||
await flush();
|
||||
|
||||
assertEquals(m1, ["2 has left!"]);
|
||||
assertEquals(m2, []);
|
||||
|
||||
await close(s1);
|
||||
}, getHandler()),
|
||||
);
|
||||
@@ -0,0 +1,80 @@
|
||||
/// <reference lib="deno.ns" />
|
||||
|
||||
export function getHandler(): Deno.ServeHandler {
|
||||
let nextId = 1;
|
||||
|
||||
const clientsMap = new Map<number, WebSocket>();
|
||||
const clientRoomMap = new Map<number, string>();
|
||||
const roomsMap = new Map<string, number[]>();
|
||||
|
||||
const broadcast = (room: string, message: string): void => {
|
||||
const members = roomsMap.get(room)!;
|
||||
|
||||
for (const member of members) {
|
||||
const socket = clientsMap.get(member);
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const commands = {
|
||||
join: (id: number, args: string[]) => {
|
||||
const room = args[0];
|
||||
if (roomsMap.has(room)) {
|
||||
roomsMap.get(room)!.push(id);
|
||||
} else {
|
||||
roomsMap.set(room, [id]);
|
||||
}
|
||||
|
||||
clientRoomMap.getOrInsert(id, room); // not sure about that
|
||||
broadcast(room, `${id} has joined!`);
|
||||
},
|
||||
} as const;
|
||||
|
||||
const handleConnection = (socket: WebSocket): void => {
|
||||
const id = nextId++;
|
||||
|
||||
socket.onopen = () => {
|
||||
clientsMap.set(id, socket);
|
||||
socket.send(`Welcome ${id}, type /join <room>.`);
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
const room = clientRoomMap.get(id);
|
||||
if (room) {
|
||||
clientRoomMap.delete(id);
|
||||
const members = roomsMap.get(room)!;
|
||||
members.splice(members.indexOf(id), 1);
|
||||
broadcast(room, `${id} has left!`);
|
||||
}
|
||||
clientsMap.delete(id);
|
||||
};
|
||||
|
||||
socket.onmessage = (message: MessageEvent) => {
|
||||
const text = String(message.data);
|
||||
if (text.startsWith("/")) {
|
||||
const [command, ...args] = text.substring(1).split(" ");
|
||||
try {
|
||||
// @ts-ignore
|
||||
commands[command](id, args);
|
||||
} catch (error) {
|
||||
socket.send(JSON.stringify(error));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = console.error;
|
||||
};
|
||||
|
||||
return (request) => {
|
||||
if (request.headers.get("upgrade") !== "websocket") {
|
||||
return new Response("Upgrade Required", { status: 426 });
|
||||
}
|
||||
|
||||
const { socket, response } = Deno.upgradeWebSocket(request);
|
||||
handleConnection(socket);
|
||||
|
||||
return response;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user