Initial commit
This commit is contained in:
commit
888f397776
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*.log
|
||||
36
example/test.ts
Normal file
36
example/test.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { WebSocket } from "ws";
|
||||
|
||||
let cliId = 1;
|
||||
|
||||
function newClient(room, interval) {
|
||||
const id = cliId++;
|
||||
let messageId = 0;
|
||||
console.log(`Client #${id} room is`, room)
|
||||
const ws = new WebSocket(`ws://localhost:8080/${room}`);
|
||||
ws.addEventListener("open", () => {
|
||||
ws.addEventListener("message", ev => {
|
||||
console.log(`[Client#${id}] Received message:`, ev.data.toString());
|
||||
});
|
||||
const int = setInterval(() => {
|
||||
console.log(`[Client#${id}] Sending message ${messageId}`);
|
||||
ws.send(`Message#${messageId++} from Client#${id}`);
|
||||
}, interval);
|
||||
ws.addEventListener("close", () => {
|
||||
console.log(`[Client#${id}] Closed`);
|
||||
clearInterval(int);
|
||||
});
|
||||
});
|
||||
return ws;
|
||||
}
|
||||
|
||||
const user1 = newClient("foo", 900);
|
||||
const user2 = newClient("bar", 500);
|
||||
const user3 = newClient("foo", 400);
|
||||
const user4 = newClient("bar", 800);
|
||||
|
||||
setTimeout(() => {
|
||||
user1.close();
|
||||
user2.close();
|
||||
user3.close();
|
||||
user4.close();
|
||||
}, 4000);
|
||||
41
index.ts
Normal file
41
index.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import {serve, ServerWebSocket} from "bun";
|
||||
|
||||
interface WSData {
|
||||
room: string;
|
||||
}
|
||||
|
||||
const PORT = 8080;
|
||||
const MAIN_CHANNEL = "central";
|
||||
|
||||
const msg = (data: Record<string, unknown>) => JSON.stringify(data);
|
||||
|
||||
const server = serve({
|
||||
fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
const room = url.pathname.split("/")[1] ?? MAIN_CHANNEL;
|
||||
if (server.upgrade(req, {data: {room}})) {
|
||||
return;
|
||||
}
|
||||
return new Response("Upgrade failed :(", { status: 500 });
|
||||
},
|
||||
websocket: {
|
||||
open(ws: ServerWebSocket<WSData>) {
|
||||
const room = ws.data.room ?? MAIN_CHANNEL;
|
||||
console.log(`${ws.remoteAddress} connected to '${room}' room`);
|
||||
ws.subscribe(room);
|
||||
},
|
||||
message(ws, message) {
|
||||
const room = ws.data.room ?? MAIN_CHANNEL;
|
||||
ws.publish(room, message as unknown as BufferSource);
|
||||
},
|
||||
close(ws) {
|
||||
const room = ws.data.room ?? MAIN_CHANNEL;
|
||||
console.log(`${ws.remoteAddress} disconnected from '${room}' room`);
|
||||
ws.unsubscribe(room);
|
||||
ws.publish(room, msg({message: "someone left"}));
|
||||
},
|
||||
},
|
||||
port: PORT,
|
||||
});
|
||||
|
||||
console.log(`Listening on ${server.hostname}:${server.port}`);
|
||||
30
package-lock.json
generated
Normal file
30
package-lock.json
generated
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "websocket-relay",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "websocket-relay",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"bun-types": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bun-types": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.1.tgz",
|
||||
"integrity": "sha512-7NrXqhMIaNKmWn2dSWEQ50znMZqrN/5Z0NBMXvQTRu/+Y1CvoXRznFy0pnqLe024CeZgVdXoEpARNO1JZLAPGw==",
|
||||
"dev": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"bun-types": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.1.tgz",
|
||||
"integrity": "sha512-7NrXqhMIaNKmWn2dSWEQ50znMZqrN/5Z0NBMXvQTRu/+Y1CvoXRznFy0pnqLe024CeZgVdXoEpARNO1JZLAPGw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
15
package.json
Normal file
15
package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "websocket-relay",
|
||||
"version": "1.0.0",
|
||||
"author": "",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"bun-types": "^1.0.1",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"description": "",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
}
|
||||
}
|
||||
41
test/server.test.ts
Normal file
41
test/server.test.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
||||
import { spawn, ChildProcessWithoutNullStreams } from "child_process";
|
||||
import { WebSocket } from "ws";
|
||||
|
||||
describe("Test connections", () => {
|
||||
let process: ChildProcessWithoutNullStreams;
|
||||
|
||||
beforeAll(async () => {
|
||||
process = spawn("bun", ["run", "./index.ts"]);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
process.on("error", (err) => reject(err))
|
||||
process.stdout.on("data", chunk => {
|
||||
if (chunk.toString().includes("Listening")) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.kill();
|
||||
});
|
||||
|
||||
it("test", async () => {
|
||||
const ws1 = new WebSocket("ws://localhost:8080/");
|
||||
const ws2 = new WebSocket("ws://localhost:8080/");
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
ws1.onerror = reject;
|
||||
ws2.onerror = reject;
|
||||
|
||||
ws2.onmessage = (data) => {
|
||||
expect(data.data).toBe("TEST");
|
||||
resolve();
|
||||
};
|
||||
ws1.onopen = () => {
|
||||
ws1.send("TEST");
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"module": "nodenext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "nodenext",
|
||||
"strict": false,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist/",
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user