Skip to content

Commit b9ce213

Browse files
committed
feat(websockets): channels (grouping + broadcasting)
1 parent 0c45106 commit b9ce213

File tree

4 files changed

+54
-3
lines changed

4 files changed

+54
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
called _after_ any available route handlers.
1010
- `Document` wrapper for JSX pages (inc. doctype, head, and body markup and
1111
class transformation with [Uno CSS](https://github.com/unocss/unocss)).
12+
- WebSocket connections can be grouped into channels for message broadcasting.
1213

1314
### Changed
1415

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# 🐍 nadder
22

3-
**nadder** is an opinionated HTTP/WebSocket server framework.
3+
**nadder** is an opinionated HTTP/WebSocket server framework for Deno.
4+
45
It includes **[URL Pattern](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API)**
56
routing, post-route middleware, helpers for creating/reading/manipulating cookies and responses,
7+
upgrading HTTP connections to WebSocket connections (inc. sorting into channels),
68
**PostgreSQL** (or in-memory) session storage (inc. garbage collection and expiry), a React-free
79
**JSX transformer** and atomic CSS with [**Uno**](https://github.com/unocss/unocss).
810

server.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ const getRoute = (method: RequestMethod, href: string) => {
3636
return undefined;
3737
};
3838

39+
const activeSocketConnections: Map<string, Set<WebSocket>> = new Map(),
40+
removeSocketFromChannel = (name: string, socket: WebSocket) => {
41+
if (!activeSocketConnections.get(name)) return;
42+
activeSocketConnections.get(name)!.delete(socket);
43+
},
44+
addSocketToChannel = (name: string, socket: WebSocket) => {
45+
if (!activeSocketConnections.get(name)) {
46+
activeSocketConnections.set(name, new Set());
47+
}
48+
activeSocketConnections.get(name)!.add(socket);
49+
};
50+
3951
const listenAndServe = (port = 3000, log = console.log) => {
4052
log("");
4153
log(`✨ server started at http://localhost:${port}/`);
@@ -110,18 +122,47 @@ const listenAndServe = (port = 3000, log = console.log) => {
110122
const upgrade = Deno.upgradeWebSocket(req);
111123
socket = upgrade.socket;
112124
override = upgrade.response;
125+
addSocketToChannel(ctx.upgrade.channel.name, socket);
113126
return socket;
114127
},
115128
channel: {
116129
name: "",
117130
join: (name) => {
131+
const socket = ctx.upgrade.socket();
132+
if (socket) {
133+
removeSocketFromChannel(ctx.upgrade.channel.name, socket);
134+
addSocketToChannel(name, socket);
135+
}
118136
const channel =
119137
(ctx.upgrade.channel as Mutable<Context["upgrade"]["channel"]>);
120138
channel.name = name;
121139
},
122140
broadcast: (message) => {
123-
// todo
124-
console.log(ctx.upgrade.channel.name, message);
141+
const { name } = ctx.upgrade.channel,
142+
channel = activeSocketConnections.get(name);
143+
if (!channel) return;
144+
const safe = typeof message === "string" ||
145+
message instanceof Blob || message instanceof ArrayBuffer ||
146+
message instanceof SharedArrayBuffer ||
147+
message instanceof Int8Array ||
148+
message instanceof Uint8Array ||
149+
message instanceof Uint8ClampedArray ||
150+
message instanceof Int16Array ||
151+
message instanceof Uint16Array ||
152+
message instanceof Int32Array ||
153+
message instanceof Uint32Array ||
154+
message instanceof Float32Array ||
155+
message instanceof Float64Array ||
156+
message instanceof BigInt64Array ||
157+
message instanceof BigUint64Array ||
158+
message instanceof DataView;
159+
if (!safe) message = JSON.stringify(message);
160+
const data = message as
161+
| string
162+
| Blob
163+
| ArrayBufferView
164+
| ArrayBufferLike;
165+
for (const s of channel) s.send(data);
125166
},
126167
},
127168
},

test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ nadder.handleRoute("GET", "/{index.html}?", async (ctx) => {
3333
</>,
3434
);
3535
ctx.res.inferContentType("html");
36+
37+
const socket = ctx.upgrade.socket();
38+
if (!socket) return;
39+
socket.onmessage = (ev) => {
40+
if (ev.data === "channel") ctx.upgrade.channel.join("channel2");
41+
ctx.upgrade.channel.broadcast(ev.data);
42+
};
3643
});
3744

3845
nadder.listenAndServe();

0 commit comments

Comments
 (0)