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