diff options
Diffstat (limited to 'std')
-rw-r--r-- | std/examples/README.md | 10 | ||||
-rw-r--r-- | std/examples/chat/index.html | 76 | ||||
-rw-r--r-- | std/examples/chat/server.ts | 65 | ||||
-rw-r--r-- | std/examples/chat/server_test.ts | 46 |
4 files changed, 196 insertions, 1 deletions
diff --git a/std/examples/README.md b/std/examples/README.md index 4f12f2a02..ea85da542 100644 --- a/std/examples/README.md +++ b/std/examples/README.md @@ -10,7 +10,7 @@ bookmark to a program.) ### A TCP echo server ```shell -deno https://deno.land/std/examples/echo_server.ts --allow-net +deno --allow-net https://deno.land/std/examples/echo_server.ts ``` Or @@ -51,3 +51,11 @@ deno install --allow-net --allow-env gist https://deno.land/std/examples/gist.ts gist --title "Example gist 1" script.ts gist --t "Example gist 2" script2.ts ``` + +### chat - WebSocket chat server and browser client + +```shell +deno --allow-net --allow-read https://deno.land/std/examples/chat/server.ts +``` + +Open http://localhost:8080 on the browser. diff --git a/std/examples/chat/index.html b/std/examples/chat/index.html new file mode 100644 index 000000000..b84a9f1dd --- /dev/null +++ b/std/examples/chat/index.html @@ -0,0 +1,76 @@ +<html> + <head> + <title>ws chat example</title> + </head> + <body> + <div> + <input type="text" id="input" /> + <button id="sendButton" disabled>send</button> + <button id="connectButton" disabled>connect</button> + <button id="closeButton" disabled>close</button> + </div> + <div id="status"></div> + <ul id="timeline"></div> + <script> + let ws; + function messageDom(msg) { + const div = document.createElement("li"); + div.className = "message"; + div.innerText = msg; + return div; + } + const timeline = document.getElementById("timeline"); + const sendButton = document.getElementById("sendButton"); + sendButton.onclick = send; + const closeButton =document.getElementById("closeButton"); + closeButton.onclick=close; + const connectButton = document.getElementById("connectButton"); + connectButton.onclick=connect; + const status = document.getElementById("status"); + const input = document.getElementById("input"); + function send() { + const msg = input.value; + ws.send(msg); + applyState({inputValue: ""}) + } + function connect() { + if (ws) ws.close(); + ws = new WebSocket("ws://0.0.0.0:8080/ws"); + ws.addEventListener("open", () => { + console.log("open", ws); + applyState({connected: true}); + }); + ws.addEventListener("message", ({data}) => { + timeline.appendChild(messageDom(data)); + }); + ws.addEventListener("close", () => { + applyState({connect: false}); + }); + } + function close() { + ws.close(); + applyState({connected: false}); + } + function applyState({connected, status, inputValue}) { + if (inputValue != null) { + input.value = inputValue; + } + if(status != null) { + status.innerText = status; + } + if (connected != null) { + if (connected) { + sendButton.disabled = false; + connectButton.disabled = true; + closeButton.disabled= false; + } else { + sendButton.disabled= true; + connectButton.disabled=false; + closeButton.disabled=true; + } + } + } + connect(); + </script> + </body> +</html> diff --git a/std/examples/chat/server.ts b/std/examples/chat/server.ts new file mode 100644 index 000000000..7a5a3ea14 --- /dev/null +++ b/std/examples/chat/server.ts @@ -0,0 +1,65 @@ +import { listenAndServe } from "../../http/server.ts"; +import { + acceptWebSocket, + acceptable, + WebSocket, + isWebSocketCloseEvent +} from "../../ws/mod.ts"; + +const clients = new Map<number, WebSocket>(); +let clientId = 0; +async function dispatch(msg: string): Promise<void> { + for (const client of clients.values()) { + client.send(msg); + } +} +async function wsHandler(ws: WebSocket): Promise<void> { + const id = ++clientId; + clients.set(id, ws); + dispatch(`Connected: [${id}]`); + for await (const msg of ws.receive()) { + console.log(`msg:${id}`, msg); + if (typeof msg === "string") { + dispatch(`[${id}]: ${msg}`); + } else if (isWebSocketCloseEvent(msg)) { + clients.delete(id); + dispatch(`Closed: [${id}]`); + break; + } + } +} + +listenAndServe({ port: 8080 }, async req => { + if (req.method === "GET" && req.url === "/") { + //Serve with hack + const u = new URL("./index.html", import.meta.url); + if (u.protocol.startsWith("http")) { + // server launched by deno run http(s)://.../server.ts, + fetch(u.href).then(resp => { + resp.headers.set("content-type", "text/html"); + return req.respond(resp); + }); + } else { + // server launched by deno run ./server.ts + const file = await Deno.open("./index.html"); + req.respond({ + status: 200, + headers: new Headers({ + "content-type": "text/html" + }), + body: file + }); + } + } + if (req.method === "GET" && req.url === "/ws") { + if (acceptable(req)) { + acceptWebSocket({ + conn: req.conn, + bufReader: req.r, + bufWriter: req.w, + headers: req.headers + }).then(wsHandler); + } + } +}); +console.log("chat server starting on :8080...."); diff --git a/std/examples/chat/server_test.ts b/std/examples/chat/server_test.ts new file mode 100644 index 000000000..d43e41693 --- /dev/null +++ b/std/examples/chat/server_test.ts @@ -0,0 +1,46 @@ +import { assert, assertEquals } from "../../testing/asserts.ts"; +import { TextProtoReader } from "../../textproto/mod.ts"; +import { BufReader } from "../../io/bufio.ts"; +import { connectWebSocket, WebSocket } from "../../ws/mod.ts"; + +let server: Deno.Process | undefined; +async function startServer(): Promise<void> { + server = Deno.run({ + args: [Deno.execPath(), "--allow-net", "--allow-read", "server.ts"], + cwd: "examples/chat", + stdout: "piped" + }); + try { + assert(server.stdout != null); + const r = new TextProtoReader(new BufReader(server.stdout)); + const s = await r.readLine(); + assert(s !== Deno.EOF && s.includes("chat server starting")); + } catch { + server.close(); + } +} + +const { test } = Deno; + +await startServer(); + +test("GET / should serve html", async () => { + const resp = await fetch("http://0.0.0.0:8080/"); + assertEquals(resp.status, 200); + assertEquals(resp.headers.get("content-type"), "text/html"); + const html = await resp.body.text(); + assert(html.includes("ws chat example"), "body is ok"); +}); + +let ws: WebSocket | undefined; +test("GET /ws should upgrade conn to ws", async () => { + ws = await connectWebSocket("http://0.0.0.0:8080/ws"); + const it = ws.receive(); + assertEquals((await it.next()).value, "Connected: [1]"); + ws.send("Hello"); + assertEquals((await it.next()).value, "[1]: Hello"); +}); +test("afterAll", () => { + server?.close(); + ws?.conn.close(); +}); |