~ev/denochat

348863e3e340e900c4e89c6471dfb2b36f620b38 — Ev Bogue 1 year, 11 months ago master
initial
3 files changed, 170 insertions(+), 0 deletions(-)

A README.md
A index.html
A server.ts
A  => README.md +9 -0
@@ 1,9 @@
# deno chat

this is just copy and pasted from deno.land/std/examples, because cloning down std is apparently a huge mysterious programming challenge at the moment

to run 

```
deno run server.ts
```

A  => index.html +81 -0
@@ 1,81 @@
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <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"></ul>
    <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");
      input.addEventListener("keydown", keyDownEvent);
      function send() {
        const msg = input.value;
        ws.send(msg);
        applyState({inputValue: ""});
      }
      function keyDownEvent(e) {
        if (e.keyCode === 13) return send();
      }
      function connect() {
        if (ws) ws.close();
        ws = new WebSocket(`ws://${location.host}/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>

A  => server.ts +80 -0
@@ 1,80 @@
import { listenAndServe } from "https://deno.land/std/http/server.ts";
import {
  acceptWebSocket,
  acceptable,
  WebSocket,
  isWebSocketCloseEvent,
} from "https://deno.land/std/ws/mod.ts";
import { fromFileUrl } from "https://deno.land/std/path/mod.ts";

const clients = new Map<number, WebSocket>();
let clientId = 0;
function dispatch(msg: string): 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) {
    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(async (resp) => {
        const body = new Uint8Array(await resp.arrayBuffer());
        return req.respond({
          status: resp.status,
          headers: new Headers({
            "content-type": "text/html",
          }),
          body,
        });
      });
    } else {
      // server launched by deno run ./server.ts
      const file = await Deno.open(fromFileUrl(u));
      req.respond({
        status: 200,
        headers: new Headers({
          "content-type": "text/html",
        }),
        body: file,
      });
    }
  }
  if (req.method === "GET" && req.url === "/favicon.ico") {
    req.respond({
      status: 302,
      headers: new Headers({
        location: "https://deno.land/favicon.ico",
      }),
    });
  }
  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....");