diff --git a/std/examples/README.md b/std/examples/README.md
index 351c4a6b4c7c4c..b4d276cdb26d84 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 00000000000000..b84a9f1dd1f67e
--- /dev/null
+++ b/std/examples/chat/index.html
@@ -0,0 +1,76 @@
+
+
+ ws chat example
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/std/examples/chat/server.ts b/std/examples/chat/server.ts
new file mode 100644
index 00000000000000..7a5a3ea141f361
--- /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();
+let clientId = 0;
+async function dispatch(msg: string): Promise {
+ for (const client of clients.values()) {
+ client.send(msg);
+ }
+}
+async function wsHandler(ws: WebSocket): Promise {
+ 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 00000000000000..d43e41693e94d8
--- /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 {
+ 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();
+});