Skip to content

Commit 750873d

Browse files
wbambergbsmth
andauthored
Add websockets example (#326)
* Add websockets example * Update websockets/README.md Co-authored-by: Brian Smith <brian@smith.berlin> * Update websockets/README.md Co-authored-by: Brian Smith <brian@smith.berlin> --------- Co-authored-by: Brian Smith <brian@smith.berlin>
1 parent 9bcdedf commit 750873d

File tree

6 files changed

+131
-0
lines changed

6 files changed

+131
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ Code examples that accompany various MDN DOM and Web API documentation pages.
114114

115115
- The "webgpu-render-demo" directory contains an example that demonstrates basic usage of the [WebGPU API](https://developer.mozilla.org/docs/Web/API/WebGPU_API) render pipeline, which is used for rendering high-performance graphics via the GPU. [View the demo live](https://mdn.github.io/dom-examples/webgpu-render-demo/).
116116

117+
- The "websockets" directory contains an example that demonstrates basic usage of the [WebSockets API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API).
118+
117119
- The "webxr" directory contains an example showing how to [start up a WebXR session](https://mdn.github.io/dom-examples/webxr/).
118120
See [Starting up and shutting down a WebXR session](https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API/Startup_and_shutdown) for details.
119121

websockets/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# README
2+
3+
This directory contains a minimal example [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) client and server.
4+
5+
When the page is loaded, it creates a WebSocket connection to the server, then sends a ping every second. The server listens for the ping and sends a response. The client listens for the responses and logs them.
6+
7+
The client starts the connection on the [`pageshow`](https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event) event and closes it on [`pagehide`](https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event): this allows browser to keep the page in the [bfcache](https://developer.mozilla.org/en-US/docs/Glossary/bfcache), which improves page load if the user navigates back to it.
8+
9+
## Running the example
10+
11+
The server-side is written in [Deno](https://deno.com/) so Deno needs to be installed first. Then, with Deno in your path, you can start the server with a command like:
12+
13+
```bash
14+
deno run --allow-net=0.0.0.0:80 --allow-read=./index.html,./client.js,client.css main.js
15+
```
16+
17+
You can then navigate to http://localhost:80/ and you should see the application running.

websockets/client.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#log {
2+
height: 80vh;
3+
overflow: scroll;
4+
padding: 0.5rem;
5+
border: 1px solid black;
6+
}

websockets/client.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const wsUri = "ws://127.0.0.1/";
2+
let websocket = null;
3+
let pingInterval;
4+
let counter = 0;
5+
6+
const logElement = document.querySelector("#log");
7+
function log(text) {
8+
logElement.innerText = `${logElement.innerText}${text}\n`;
9+
logElement.scrollTop = logElement.scrollHeight;
10+
}
11+
12+
// Open the websocket when the page is shown
13+
window.addEventListener("pageshow", () => {
14+
log("OPENING");
15+
16+
websocket = new WebSocket(wsUri);
17+
18+
websocket.addEventListener("open", () => {
19+
log("CONNECTED");
20+
pingInterval = setInterval(() => {
21+
log(`SENT: ping: ${counter}`);
22+
websocket.send("ping");
23+
}, 1000);
24+
});
25+
26+
websocket.addEventListener("close", () => {
27+
log("DISCONNECTED");
28+
clearInterval(pingInterval);
29+
});
30+
31+
websocket.addEventListener("message", (e) => {
32+
log(`RECEIVED: ${e.data}: ${counter}`);
33+
counter++;
34+
});
35+
36+
websocket.addEventListener("error", (e) => {
37+
log(`ERROR: ${e.data}`);
38+
});
39+
});
40+
41+
// Close the websocket when the user leaves.
42+
window.addEventListener("pagehide", () => {
43+
if (websocket) {
44+
log("CLOSING");
45+
websocket.close();
46+
websocket = null;
47+
window.clearInterval(pingInterval);
48+
}
49+
});

websockets/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<script src="client.js" defer></script>
5+
<link rel="stylesheet" href="client.css" />
6+
<title>WebSocket Test</title>
7+
</head>
8+
<body>
9+
<h2>WebSocket Test</h2>
10+
<p>Sends a ping every second</p>
11+
<pre id="log"></pre>
12+
</body>
13+
</html>

websockets/main.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
async function createResponse(path, mimeType) {
2+
const file = await Deno.open(path, { read: true });
3+
const response = new Response(file.readable);
4+
response.headers.set("Content-Type", mimeType);
5+
return response;
6+
}
7+
8+
Deno.serve({
9+
port: 80,
10+
async handler(request) {
11+
if (request.headers.get("upgrade") !== "websocket") {
12+
const url = new URL(request.url);
13+
// If the request is a normal HTTP request,
14+
// we serve the client HTML, CSS, or JS.
15+
switch (url.pathname) {
16+
case "/client.js":
17+
return await createResponse("./client.js", "text/javascript");
18+
case "/client.css":
19+
return await createResponse("./client.css", "text/css");
20+
case "/":
21+
return await createResponse("./index.html", "text/html");
22+
default:
23+
return new Response("Not found", {
24+
status: 404,
25+
});
26+
}
27+
}
28+
// If the request is a websocket upgrade,
29+
// we need to use the Deno.upgradeWebSocket helper
30+
const { socket, response } = Deno.upgradeWebSocket(request);
31+
32+
socket.onopen = () => {
33+
console.log("CONNECTED");
34+
};
35+
socket.onmessage = (event) => {
36+
console.log(`RECEIVED: ${event.data}`);
37+
socket.send("pong");
38+
};
39+
socket.onclose = () => console.log("DISCONNECTED");
40+
socket.onerror = (error) => console.error("ERROR:", error);
41+
42+
return response;
43+
},
44+
});

0 commit comments

Comments
 (0)