forked from localtunnel/server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.ts
87 lines (70 loc) · 2.56 KB
/
server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { serve, type ServerWebSocket } from "bun";
import { uid } from "./utils";
import type { Client, Payload } from "./types";
const port = Bun.env.PORT || 1234;
const scheme = Bun.env.SCHEME || "http";
const domain = Bun.env.DOMAIN || `localhost:${port}`;
const clients = new Map<string, ServerWebSocket<Client>>();
const requesters = new Map<string, WritableStream>();
serve<Client>({
port,
fetch: async (req, server) => {
const reqUrl = new URL(req.url);
if (reqUrl.searchParams.has("new")) {
const requested = reqUrl.searchParams.get("subdomain");
let id = requested || uid();
if (clients.has(id)) id = uid();
const upgraded = server.upgrade(req, { data: { id } });
if (upgraded) return;
else return new Response("upgrade failed", { status: 500 });
}
const subdomain = reqUrl.hostname.split(".")[0];
if (!clients.has(subdomain)) {
return new Response(`${subdomain} not found`, { status: 404 });
}
// The magic: forward the req to the client
const client = clients.get(subdomain)!;
const { method, url, headers: reqHeaders } = req;
const reqBody = await req.text();
const pathname = new URL(url).pathname;
const payload: Payload = {
method,
pathname,
body: reqBody,
headers: reqHeaders,
};
const { writable, readable } = new TransformStream();
requesters.set(`${method}:${subdomain}${pathname}`, writable);
client.send(JSON.stringify(payload));
const res = await readable.getReader().read();
const { status, statusText, headers, body } = JSON.parse(res.value);
delete headers["content-encoding"]; // remove problematic header
return new Response(body, { status, statusText, headers });
},
websocket: {
open(ws) {
clients.set(ws.data.id, ws);
console.log(`\x1b[32m+ ${ws.data.id} (${clients.size} total)\x1b[0m`);
ws.send(
JSON.stringify({
url: `${scheme}://${ws.data.id}.${domain}`,
})
);
},
message: async ({ data: { id } }, message: string) => {
console.log("message from", id);
const { method, pathname } = JSON.parse(message) as Payload;
const writable = requesters.get(`${method}:${id}${pathname}`);
if (!writable) throw "connection not found";
if (writable.locked) return;
const writer = writable.getWriter();
await writer.write(message);
await writer.close();
},
close({ data }) {
console.log("closing", data.id);
clients.delete(data.id);
},
},
});
console.log(`websocket server up at ws://${domain}`);