From 68178cee60d496e422ce25beb4070141a88b07f1 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Tue, 11 Oct 2022 13:10:03 -0500 Subject: [PATCH] Move everything in-tree (WIP) --- 0.ts | 10 +++++ 1.ts | 69 +++++++++++++++++++++++++++++++ 2.ts | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 3.ts | 25 +++++++++++ main.ts => 4.ts | 0 5 files changed, 212 insertions(+) create mode 100644 0.ts create mode 100644 1.ts create mode 100644 2.ts create mode 100644 3.ts rename main.ts => 4.ts (100%) diff --git a/0.ts b/0.ts new file mode 100644 index 0000000..c6e2233 --- /dev/null +++ b/0.ts @@ -0,0 +1,10 @@ +import { copy } from "https://deno.land/std@0.156.0/streams/conversion.ts"; + +const PORT = 5588; + +console.log(`Listening on port ${PORT}`); + +for await (const conn of Deno.listen({ port: PORT })) { + await copy(conn, conn); + conn.close(); +} diff --git a/1.ts b/1.ts new file mode 100644 index 0000000..e4b57b6 --- /dev/null +++ b/1.ts @@ -0,0 +1,69 @@ +import { JsonParseStream } from "https://deno.land/std@0.157.0/encoding/json/stream.ts"; +import { TextLineStream } from "https://deno.land/std@0.156.0/streams/delimiter.ts"; + +const PORT = 5588; +const tEnc = new TextEncoder(); + +console.log(`Listening on port ${PORT}`); +for await (const conn of Deno.listen({ port: PORT })) { + console.log("Connection established:", conn.remoteAddr); + try { + for await (const request of requests(conn)) { + console.debug("Request:", request); + if ( + request == null || typeof request != "object" || + !("method" in request) || !("number" in request) + ) { + throw "Invalid request"; + } + if ( + request.method == "isPrime" && + typeof request.number == "number" + ) { + const response = { + method: "isPrime", + prime: isPrime(request.number), + }; + console.debug("Response:", response); + await conn.write(tEnc.encode(JSON.stringify(response) + "\n")); + } else { + console.debug("Request malformed"); + await conn.write(tEnc.encode("Malformed!\n")); + } + } + } catch (e) { + console.error(conn.remoteAddr, e); + } finally { + console.log("Connection closed:", conn.remoteAddr); + try { + conn.close(); + } catch (_err) { + // connection already closed + } + } +} + +function isPrime(n: number) { + if (!Number.isInteger(n)) return false; + for (let i = 2, s = Math.sqrt(n); i <= s; i++) if (n % i === 0) return false; + return n > 1; +} + +function requests(conn: Deno.Conn) { + try { + const readable = conn.readable + .pipeThrough(new TextDecoderStream()) + .pipeThrough(new TextLineStream()) + .pipeThrough(new JsonParseStream()); + + return { + async *[Symbol.asyncIterator]() { + for await (const data of readable) { + yield data; + } + }, + }; + } catch (err) { + console.error(err); + } +} diff --git a/2.ts b/2.ts new file mode 100644 index 0000000..9fa8c7a --- /dev/null +++ b/2.ts @@ -0,0 +1,108 @@ +import { readLines } from "https://deno.land/std@0.157.0/io/buffer.ts"; + +const PORT = 5588; +console.log(`Starting TCP listener on on 0.0.0.0:${PORT}`); + +const clients = new Map(); + +const tEnc = new TextEncoder(); + +async function chatSession(conn: Deno.Conn) { + try { + let myNick: string | null = null; + await sendPrompt(conn); + for await (const line of readLines(conn)) { + console.debug("Received line:", line); + const message = line.trim(); + if (myNick == null) { + if (isLegalNick(message)) { + myNick = message; + await join(myNick, conn); + } else { + console.error("Illegal nick:", message); + break; + } + } else { + const conns: Deno.Conn[] = []; + for (const themConn of clients.keys()) { + if (themConn != conn) conns.push(themConn); + } + await send(conns, `[${myNick}] ${message}`); + } + } + } finally { + if (clients.has(conn)) { + try { + await disconnect(conn); + } catch (err) { + console.error("Failed to disconnect:", err); + } + } + try { + if (conn.writable) { + send(conn, "You are being disconnected..."); + } + } catch (err) { + console.error("Failed to send disconnect notice:", err); + } + } + try { + conn.close(); + } catch (err) { + console.error("Failed to close connection:", err); + } +} + +async function send( + connsArg: Deno.Conn | Array, + text: string, +) { + const conns = !Array.isArray(connsArg) ? [connsArg] : connsArg; + const messages: Promise[] = []; + const bytes = tEnc.encode(text + "\n"); + conns.forEach((conn) => messages.push(conn.write(bytes))); + return await Promise.all(messages); +} + +const broadcast = async (text: string) => await send([...clients.keys()], text); + +const LEGAL_NICK_REGEXP = /^[a-z0-9]+$/i; +const isLegalNick = (nick: string) => { + console.debug( + `Checking nick: ${nick} (Bytes: ${tEnc.encode(nick)})`, + ); + return LEGAL_NICK_REGEXP.exec(nick) != null; +}; + +const sendPrompt = async (conn: Deno.Conn) => { + console.debug("Sending prompt..."); + return await send(conn, "Who you be?"); +}; + +async function join(myNick: string, conn: Deno.Conn) { + const userList: string[] = []; + clients.forEach((nick, _) => userList.push(nick)); + await broadcast(`* ${myNick} joined`); + clients.set(conn, myNick); + await send(conn, `* users: ${userList.join(", ")}`); +} + +async function disconnect(conn: Deno.Conn) { + const myNick = clients.get(conn); + clients.delete(conn); + await broadcast(`* ${myNick} left`); +} + +for await (const conn of Deno.listen({ port: PORT })) { + console.log("Connection established:", conn.remoteAddr); + try { + chatSession(conn); + } catch (e) { + console.error( + "Exception occurred during chat session:", + conn.remoteAddr, + e, + ); + } + console.log("Connection closed:", conn.remoteAddr); +} diff --git a/3.ts b/3.ts new file mode 100644 index 0000000..e052fbe --- /dev/null +++ b/3.ts @@ -0,0 +1,25 @@ +const PORT = 5588; +console.log(`Starting UDP listener on port ${PORT}`); + +const tDec = new TextDecoder(); +const tEnc = new TextEncoder(); + +const reservedData: Record = { version: "KeyVal 1.0" }; +const data = new Map(); + +const listener = Deno.listenDatagram({ port: PORT, transport: "udp" }); + +for await (const [bytes, addr] of listener) { + const message = tDec.decode(bytes).trim(); + console.debug("Datagram Received from:", addr, message); + if (message.includes("=")) { + const [key, val] = message.split("=", 1); + console.debug(`Setting ${key} to ${val}`); + if (!(key in reservedData)) data.set(key, val); + } else { + const value = reservedData[message] || data.get(message) || ""; + const bytes = tEnc.encode(`${message}=${value}`); + console.debug(`Sending ${message}=${value}`); + await listener.send(bytes, addr); + } +} diff --git a/main.ts b/4.ts similarity index 100% rename from main.ts rename to 4.ts