crossws 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/bunny.d.ts +2 -0
- package/dist/THIRD-PARTY-LICENSES.md +33 -0
- package/dist/_chunks/_request.mjs +1 -4
- package/dist/_chunks/_types.d.mts +2 -3
- package/dist/_chunks/adapter.d.mts +5 -13
- package/dist/_chunks/adapter.mjs +6 -7
- package/dist/_chunks/bun.d.mts +0 -3
- package/dist/_chunks/bunny.d.mts +22 -0
- package/dist/_chunks/cloudflare.d.mts +0 -3
- package/dist/_chunks/deno.d.mts +0 -3
- package/dist/_chunks/error.mjs +1 -4
- package/dist/_chunks/libs/ws.mjs +66 -1168
- package/dist/_chunks/node.d.mts +36 -7
- package/dist/_chunks/node.mjs +129 -0
- package/dist/_chunks/peer.mjs +1 -59
- package/dist/_chunks/rolldown-runtime.mjs +7 -15
- package/dist/_chunks/sse.d.mts +0 -3
- package/dist/_chunks/web.d.mts +0 -2
- package/dist/adapters/bun.mjs +1 -6
- package/dist/adapters/bunny.d.mts +2 -0
- package/dist/adapters/bunny.mjs +68 -0
- package/dist/adapters/cloudflare.mjs +7 -12
- package/dist/adapters/deno.mjs +1 -6
- package/dist/adapters/node.d.mts +2 -2
- package/dist/adapters/node.mjs +2 -125
- package/dist/adapters/sse.mjs +1 -6
- package/dist/adapters/uws.d.mts +0 -5
- package/dist/adapters/uws.mjs +2 -7
- package/dist/index.d.mts +81 -1
- package/dist/index.mjs +161 -2
- package/dist/server/bun.d.mts +0 -6
- package/dist/server/bun.mjs +3 -7
- package/dist/server/bunny.d.mts +5 -0
- package/dist/server/bunny.mjs +23 -0
- package/dist/server/cloudflare.d.mts +0 -6
- package/dist/server/cloudflare.mjs +3 -7
- package/dist/server/default.d.mts +0 -6
- package/dist/server/default.mjs +3 -7
- package/dist/server/deno.d.mts +0 -6
- package/dist/server/deno.mjs +3 -7
- package/dist/server/node.d.mts +0 -6
- package/dist/server/node.mjs +3 -9
- package/dist/websocket/native.d.mts +0 -2
- package/dist/websocket/native.mjs +1 -5
- package/dist/websocket/node.d.mts +0 -2
- package/dist/websocket/node.mjs +1 -7
- package/dist/websocket/sse.d.mts +0 -3
- package/dist/websocket/sse.mjs +1 -4
- package/package.json +42 -40
- package/server/bunny.d.ts +2 -0
package/dist/adapters/node.mjs
CHANGED
|
@@ -1,125 +1,2 @@
|
|
|
1
|
-
import "../_chunks/
|
|
2
|
-
|
|
3
|
-
import { n as import_websocket_server } from "../_chunks/libs/ws.mjs";
|
|
4
|
-
import { n as Message, r as toBufferLike, t as Peer } from "../_chunks/peer.mjs";
|
|
5
|
-
import { t as StubRequest } from "../_chunks/_request.mjs";
|
|
6
|
-
import { t as WSError } from "../_chunks/error.mjs";
|
|
7
|
-
|
|
8
|
-
//#region src/adapters/node.ts
|
|
9
|
-
const nodeAdapter = (options = {}) => {
|
|
10
|
-
if ("Deno" in globalThis || "Bun" in globalThis) throw new Error("[crossws] Using Node.js adapter in an incompatible environment.");
|
|
11
|
-
const hooks = new AdapterHookable(options);
|
|
12
|
-
const globalPeers = /* @__PURE__ */ new Map();
|
|
13
|
-
const wss = options.wss || new import_websocket_server.default({
|
|
14
|
-
noServer: true,
|
|
15
|
-
handleProtocols: () => false,
|
|
16
|
-
...options.serverOptions
|
|
17
|
-
});
|
|
18
|
-
wss.on("connection", (ws, nodeReq) => {
|
|
19
|
-
const request = new NodeReqProxy(nodeReq);
|
|
20
|
-
const peers = getPeers(globalPeers, nodeReq._namespace);
|
|
21
|
-
const peer = new NodePeer({
|
|
22
|
-
ws,
|
|
23
|
-
request,
|
|
24
|
-
peers,
|
|
25
|
-
nodeReq,
|
|
26
|
-
namespace: nodeReq._namespace
|
|
27
|
-
});
|
|
28
|
-
peers.add(peer);
|
|
29
|
-
hooks.callHook("open", peer);
|
|
30
|
-
ws.on("message", (data) => {
|
|
31
|
-
if (Array.isArray(data)) data = Buffer.concat(data);
|
|
32
|
-
hooks.callHook("message", peer, new Message(data, peer));
|
|
33
|
-
});
|
|
34
|
-
ws.on("error", (error) => {
|
|
35
|
-
peers.delete(peer);
|
|
36
|
-
hooks.callHook("error", peer, new WSError(error));
|
|
37
|
-
});
|
|
38
|
-
ws.on("close", (code, reason) => {
|
|
39
|
-
peers.delete(peer);
|
|
40
|
-
hooks.callHook("close", peer, {
|
|
41
|
-
code,
|
|
42
|
-
reason: reason?.toString()
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
wss.on("headers", (outgoingHeaders, req) => {
|
|
47
|
-
const upgradeHeaders = req._upgradeHeaders;
|
|
48
|
-
if (upgradeHeaders) for (const [key, value] of new Headers(upgradeHeaders)) outgoingHeaders.push(`${key}: ${value}`);
|
|
49
|
-
});
|
|
50
|
-
return {
|
|
51
|
-
...adapterUtils(globalPeers),
|
|
52
|
-
handleUpgrade: async (nodeReq, socket, head, webRequest) => {
|
|
53
|
-
const request = webRequest || new NodeReqProxy(nodeReq);
|
|
54
|
-
const { upgradeHeaders, endResponse, context, namespace } = await hooks.upgrade(request);
|
|
55
|
-
if (endResponse) return sendResponse(socket, endResponse);
|
|
56
|
-
nodeReq._request = request;
|
|
57
|
-
nodeReq._upgradeHeaders = upgradeHeaders;
|
|
58
|
-
nodeReq._context = context;
|
|
59
|
-
nodeReq._namespace = namespace;
|
|
60
|
-
wss.handleUpgrade(nodeReq, socket, head, (ws) => {
|
|
61
|
-
wss.emit("connection", ws, nodeReq);
|
|
62
|
-
});
|
|
63
|
-
},
|
|
64
|
-
closeAll: (code, data, force) => {
|
|
65
|
-
for (const client of wss.clients) if (force) client.terminate();
|
|
66
|
-
else client.close(code, data);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
var node_default = nodeAdapter;
|
|
71
|
-
var NodePeer = class extends Peer {
|
|
72
|
-
get remoteAddress() {
|
|
73
|
-
return this._internal.nodeReq.socket?.remoteAddress;
|
|
74
|
-
}
|
|
75
|
-
get context() {
|
|
76
|
-
return this._internal.nodeReq._context;
|
|
77
|
-
}
|
|
78
|
-
send(data, options) {
|
|
79
|
-
const dataBuff = toBufferLike(data);
|
|
80
|
-
const isBinary = typeof dataBuff !== "string";
|
|
81
|
-
this._internal.ws.send(dataBuff, {
|
|
82
|
-
compress: options?.compress,
|
|
83
|
-
binary: isBinary,
|
|
84
|
-
...options
|
|
85
|
-
});
|
|
86
|
-
return 0;
|
|
87
|
-
}
|
|
88
|
-
publish(topic, data, options) {
|
|
89
|
-
const dataBuff = toBufferLike(data);
|
|
90
|
-
const isBinary = typeof data !== "string";
|
|
91
|
-
const sendOptions = {
|
|
92
|
-
compress: options?.compress,
|
|
93
|
-
binary: isBinary,
|
|
94
|
-
...options
|
|
95
|
-
};
|
|
96
|
-
for (const peer of this._internal.peers) if (peer !== this && peer._topics.has(topic)) peer._internal.ws.send(dataBuff, sendOptions);
|
|
97
|
-
}
|
|
98
|
-
close(code, data) {
|
|
99
|
-
this._internal.ws.close(code, data);
|
|
100
|
-
}
|
|
101
|
-
terminate() {
|
|
102
|
-
this._internal.ws.terminate();
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
var NodeReqProxy = class extends StubRequest {
|
|
106
|
-
constructor(req) {
|
|
107
|
-
const host = req.headers["host"] || "localhost";
|
|
108
|
-
const url = `${req.socket?.encrypted ?? req.headers["x-forwarded-proto"] === "https" ? "https" : "http"}://${host}${req.url}`;
|
|
109
|
-
super(url, { headers: req.headers });
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
async function sendResponse(socket, res) {
|
|
113
|
-
const head = [`HTTP/1.1 ${res.status || 200} ${res.statusText || ""}`, ...[...res.headers.entries()].map(([key, value]) => `${encodeURIComponent(key)}: ${encodeURIComponent(value)}`)];
|
|
114
|
-
socket.write(head.join("\r\n") + "\r\n\r\n");
|
|
115
|
-
if (res.body) for await (const chunk of res.body) socket.write(chunk);
|
|
116
|
-
return new Promise((resolve) => {
|
|
117
|
-
socket.end(() => {
|
|
118
|
-
socket.destroy();
|
|
119
|
-
resolve();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
//#endregion
|
|
125
|
-
export { node_default as default };
|
|
1
|
+
import { n as fromNodeUpgradeHandler, t as nodeAdapter } from "../_chunks/node.mjs";
|
|
2
|
+
export { nodeAdapter as default, fromNodeUpgradeHandler };
|
package/dist/adapters/sse.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "../_chunks/adapter.mjs";
|
|
2
2
|
import { i as toString, n as Message, t as Peer } from "../_chunks/peer.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/adapters/sse.ts
|
|
5
3
|
const sseAdapter = (opts = {}) => {
|
|
6
4
|
const hooks = new AdapterHookable(opts);
|
|
7
5
|
const globalPeers = /* @__PURE__ */ new Map();
|
|
@@ -55,7 +53,6 @@ const sseAdapter = (opts = {}) => {
|
|
|
55
53
|
}
|
|
56
54
|
};
|
|
57
55
|
};
|
|
58
|
-
var sse_default = sseAdapter;
|
|
59
56
|
var SSEPeer = class extends Peer {
|
|
60
57
|
_sseStream;
|
|
61
58
|
_sseStreamController;
|
|
@@ -97,6 +94,4 @@ var SSEPeer = class extends Peer {
|
|
|
97
94
|
var SSEWebSocketStub = class {
|
|
98
95
|
readyState;
|
|
99
96
|
};
|
|
100
|
-
|
|
101
|
-
//#endregion
|
|
102
|
-
export { sse_default as default };
|
|
97
|
+
export { sseAdapter as default };
|
package/dist/adapters/uws.d.mts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { d as PeerContext, n as AdapterInstance, r as AdapterOptions, t as Adapter, u as Peer } from "../_chunks/adapter.mjs";
|
|
2
2
|
import { a as WebSocket } from "../_chunks/web.mjs";
|
|
3
3
|
import uws from "uWebSockets.js";
|
|
4
|
-
|
|
5
|
-
//#region src/_request.d.ts
|
|
6
4
|
declare const StubRequest: {
|
|
7
5
|
new (url: string, init?: RequestInit): Request;
|
|
8
6
|
};
|
|
9
|
-
//#endregion
|
|
10
|
-
//#region src/adapters/uws.d.ts
|
|
11
7
|
type UserData = {
|
|
12
8
|
peer?: UWSPeer;
|
|
13
9
|
req: uws.HttpRequest;
|
|
@@ -58,5 +54,4 @@ declare class UwsWebSocketProxy implements Partial<WebSocket> {
|
|
|
58
54
|
get protocol(): string;
|
|
59
55
|
get extensions(): string;
|
|
60
56
|
}
|
|
61
|
-
//#endregion
|
|
62
57
|
export { UWSAdapter, UWSOptions, uwsAdapter as default };
|
package/dist/adapters/uws.mjs
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "../_chunks/adapter.mjs";
|
|
2
2
|
import { n as Message, r as toBufferLike, t as Peer } from "../_chunks/peer.mjs";
|
|
3
3
|
import { t as StubRequest } from "../_chunks/_request.mjs";
|
|
4
|
-
|
|
5
|
-
//#region src/adapters/uws.ts
|
|
6
4
|
const uwsAdapter = (options = {}) => {
|
|
7
5
|
const hooks = new AdapterHookable(options);
|
|
8
6
|
const globalPeers = /* @__PURE__ */ new Map();
|
|
@@ -21,7 +19,7 @@ const uwsAdapter = (options = {}) => {
|
|
|
21
19
|
});
|
|
22
20
|
peer._internal.ws.readyState = 3;
|
|
23
21
|
},
|
|
24
|
-
message(ws, message,
|
|
22
|
+
message(ws, message, _isBinary) {
|
|
25
23
|
const peer = getPeer(ws, getPeers(globalPeers, ws.getUserData().namespace));
|
|
26
24
|
hooks.callHook("message", peer, new Message(message, peer));
|
|
27
25
|
},
|
|
@@ -72,7 +70,6 @@ const uwsAdapter = (options = {}) => {
|
|
|
72
70
|
}
|
|
73
71
|
};
|
|
74
72
|
};
|
|
75
|
-
var uws_default = uwsAdapter;
|
|
76
73
|
function getPeer(uws, peers) {
|
|
77
74
|
const uwsData = uws.getUserData();
|
|
78
75
|
if (uwsData.peer) return uwsData.peer;
|
|
@@ -153,6 +150,4 @@ var UwsWebSocketProxy = class {
|
|
|
153
150
|
return this._uws?.getUserData().extensions;
|
|
154
151
|
}
|
|
155
152
|
};
|
|
156
|
-
|
|
157
|
-
//#endregion
|
|
158
|
-
export { uws_default as default };
|
|
153
|
+
export { uwsAdapter as default };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,82 @@
|
|
|
1
1
|
import { a as Hooks, c as Message, d as PeerContext, f as WSError, i as defineWebSocketAdapter, l as AdapterInternal, n as AdapterInstance, o as ResolveHooks, r as AdapterOptions, s as defineHooks, t as Adapter, u as Peer } from "./_chunks/adapter.mjs";
|
|
2
|
-
|
|
2
|
+
import { n as WSOptions, t as ServerWithWSOptions } from "./_chunks/_types.mjs";
|
|
3
|
+
interface WebSocketProxyOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Target WebSocket URL to proxy to (`ws://` or `wss://`).
|
|
6
|
+
*
|
|
7
|
+
* Can be a static string/URL or a function that resolves the target dynamically
|
|
8
|
+
* based on the incoming {@link Peer}.
|
|
9
|
+
*/
|
|
10
|
+
target: string | URL | ((peer: Peer) => string | URL);
|
|
11
|
+
/**
|
|
12
|
+
* Forward the client's `sec-websocket-protocol` header to the upstream.
|
|
13
|
+
*
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
forwardProtocol?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum number of bytes buffered per peer while the upstream connection
|
|
19
|
+
* is still opening. If exceeded, the peer is closed with code `1009`
|
|
20
|
+
* (Message Too Big). Set to `0` to disable the limit.
|
|
21
|
+
*
|
|
22
|
+
* @default 1048576 (1 MiB)
|
|
23
|
+
*/
|
|
24
|
+
maxBufferSize?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Milliseconds to wait for the upstream WebSocket handshake to complete.
|
|
27
|
+
* If the upstream does not open within the timeout, the peer is closed
|
|
28
|
+
* with code `1011`. Set to `0` to disable the timeout.
|
|
29
|
+
*
|
|
30
|
+
* @default 10000
|
|
31
|
+
*/
|
|
32
|
+
connectTimeout?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Custom `WebSocket` constructor used to dial the upstream. Useful when
|
|
35
|
+
* the runtime does not expose a global `WebSocket` (Node.js < 22) or
|
|
36
|
+
* when you want to use a different client implementation (e.g. `ws`,
|
|
37
|
+
* `undici`, a mock for tests).
|
|
38
|
+
*
|
|
39
|
+
* @default globalThis.WebSocket
|
|
40
|
+
*/
|
|
41
|
+
WebSocket?: typeof WebSocket;
|
|
42
|
+
/**
|
|
43
|
+
* Extra headers to send on the upstream handshake. Can be a static
|
|
44
|
+
* object or a resolver called per peer.
|
|
45
|
+
*
|
|
46
|
+
* Useful to forward identity from the incoming request (`cookie`,
|
|
47
|
+
* `authorization`, `origin`), or to inject a shared secret the
|
|
48
|
+
* upstream expects.
|
|
49
|
+
*
|
|
50
|
+
* > [!NOTE]
|
|
51
|
+
* > The WHATWG global `WebSocket` constructor does not accept custom
|
|
52
|
+
* > headers — this option is only honored by `WebSocket` constructors
|
|
53
|
+
* > that take a third options argument (e.g. `ws`, `undici`). Pass
|
|
54
|
+
* > one via the {@link WebSocket} option to use it.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* createWebSocketProxy({
|
|
59
|
+
* target: "wss://backend.example.com",
|
|
60
|
+
* WebSocket: WsFromNodeWs,
|
|
61
|
+
* headers: (peer) => ({
|
|
62
|
+
* cookie: peer.request.headers.get("cookie") ?? "",
|
|
63
|
+
* "x-forwarded-for": peer.remoteAddress ?? "",
|
|
64
|
+
* }),
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
headers?: HeadersInit | ((peer: Peer) => HeadersInit | undefined | void);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a set of crossws hooks that proxy incoming WebSocket connections
|
|
72
|
+
* to an upstream `ws://` or `wss://` target.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { createWebSocketProxy } from "crossws";
|
|
77
|
+
*
|
|
78
|
+
* const hooks = createWebSocketProxy("wss://echo.websocket.org");
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function createWebSocketProxy(target: WebSocketProxyOptions["target"] | WebSocketProxyOptions): Partial<Hooks>;
|
|
82
|
+
export { type Adapter, type AdapterInstance, type AdapterInternal, type AdapterOptions, type Hooks, type Message, type Peer, type PeerContext, type ResolveHooks, type ServerWithWSOptions, type WSError, type WSOptions, type WebSocketProxyOptions, createWebSocketProxy, defineHooks, defineWebSocketAdapter };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,162 @@
|
|
|
1
1
|
import { a as defineHooks, n as defineWebSocketAdapter } from "./_chunks/adapter.mjs";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
const DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024;
|
|
3
|
+
const DEFAULT_CONNECT_TIMEOUT = 1e4;
|
|
4
|
+
const TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
5
|
+
function createWebSocketProxy(target) {
|
|
6
|
+
const options = typeof target === "string" || target instanceof URL || typeof target === "function" ? { target } : target;
|
|
7
|
+
const WebSocketCtor = options.WebSocket ?? globalThis.WebSocket;
|
|
8
|
+
if (typeof WebSocketCtor !== "function") throw new TypeError("createWebSocketProxy requires a `WebSocket` constructor. Pass one via the `WebSocket` option, or use a runtime that provides a global `WebSocket` (Node.js >= 22, Bun, Deno, Cloudflare Workers, browsers).");
|
|
9
|
+
const upstreams = /* @__PURE__ */ new Map();
|
|
10
|
+
return {
|
|
11
|
+
upgrade(request) {
|
|
12
|
+
const reqProtocol = request.headers.get("sec-websocket-protocol");
|
|
13
|
+
if (options.forwardProtocol === false || !reqProtocol) return;
|
|
14
|
+
const accepted = reqProtocol.split(",")[0].trim();
|
|
15
|
+
if (!TOKEN_RE.test(accepted)) return;
|
|
16
|
+
return { headers: { "sec-websocket-protocol": accepted } };
|
|
17
|
+
},
|
|
18
|
+
open(peer) {
|
|
19
|
+
let ws;
|
|
20
|
+
try {
|
|
21
|
+
const url = _resolveTarget(options.target, peer);
|
|
22
|
+
const protocols = _resolveProtocols(peer, options.forwardProtocol);
|
|
23
|
+
const wsOptions = _resolveWsOptions(options.headers, peer);
|
|
24
|
+
ws = wsOptions ? new WebSocketCtor(url, protocols, wsOptions) : new WebSocketCtor(url, protocols);
|
|
25
|
+
ws.binaryType = "arraybuffer";
|
|
26
|
+
} catch {
|
|
27
|
+
_safeClose(peer, 1011, "Upstream setup failed");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const state = {
|
|
31
|
+
ws,
|
|
32
|
+
buffer: [],
|
|
33
|
+
bufferSize: 0,
|
|
34
|
+
open: false,
|
|
35
|
+
timeout: void 0
|
|
36
|
+
};
|
|
37
|
+
upstreams.set(peer.id, state);
|
|
38
|
+
const timeoutMs = options.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT;
|
|
39
|
+
if (timeoutMs > 0) state.timeout = setTimeout(() => {
|
|
40
|
+
if (upstreams.get(peer.id) !== state || state.open) return;
|
|
41
|
+
_cleanupState(upstreams, peer.id, state);
|
|
42
|
+
_safeClose(peer, 1011, "Upstream connect timeout");
|
|
43
|
+
}, timeoutMs);
|
|
44
|
+
ws.addEventListener("open", () => {
|
|
45
|
+
_clearTimeout(state);
|
|
46
|
+
state.open = true;
|
|
47
|
+
for (const data of state.buffer) ws.send(data);
|
|
48
|
+
state.buffer.length = 0;
|
|
49
|
+
state.bufferSize = 0;
|
|
50
|
+
});
|
|
51
|
+
ws.addEventListener("message", (event) => {
|
|
52
|
+
_safeSend(peer, event.data);
|
|
53
|
+
});
|
|
54
|
+
ws.addEventListener("close", (event) => {
|
|
55
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
56
|
+
_cleanupState(upstreams, peer.id, state);
|
|
57
|
+
_safeClose(peer, _remapIncomingCode(event.code), event.reason);
|
|
58
|
+
});
|
|
59
|
+
ws.addEventListener("error", () => {
|
|
60
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
61
|
+
_cleanupState(upstreams, peer.id, state);
|
|
62
|
+
_safeClose(peer, 1011, "Upstream error");
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
message(peer, message) {
|
|
66
|
+
const state = upstreams.get(peer.id);
|
|
67
|
+
if (!state) return;
|
|
68
|
+
const raw = typeof message.rawData === "string" ? message.rawData : message.uint8Array();
|
|
69
|
+
if (state.open) {
|
|
70
|
+
try {
|
|
71
|
+
state.ws.send(raw);
|
|
72
|
+
} catch {}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const size = typeof raw === "string" ? raw.length * 3 : raw.byteLength;
|
|
76
|
+
const limit = options.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;
|
|
77
|
+
if (limit > 0 && state.bufferSize + size > limit) {
|
|
78
|
+
_cleanupState(upstreams, peer.id, state);
|
|
79
|
+
_safeClose(peer, 1009, "Proxy buffer limit exceeded");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
state.buffer.push(typeof raw === "string" ? raw : Uint8Array.from(raw));
|
|
83
|
+
state.bufferSize += size;
|
|
84
|
+
},
|
|
85
|
+
close(peer, details) {
|
|
86
|
+
const state = upstreams.get(peer.id);
|
|
87
|
+
if (!state) return;
|
|
88
|
+
_clearTimeout(state);
|
|
89
|
+
upstreams.delete(peer.id);
|
|
90
|
+
try {
|
|
91
|
+
state.ws.close(_normalizeOutgoingCode(details.code), _truncateReason(details.reason));
|
|
92
|
+
} catch {}
|
|
93
|
+
},
|
|
94
|
+
error(peer) {
|
|
95
|
+
const state = upstreams.get(peer.id);
|
|
96
|
+
if (!state) return;
|
|
97
|
+
_clearTimeout(state);
|
|
98
|
+
upstreams.delete(peer.id);
|
|
99
|
+
try {
|
|
100
|
+
state.ws.close(1011, "Peer error");
|
|
101
|
+
} catch {}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function _cleanupState(upstreams, id, state) {
|
|
106
|
+
_clearTimeout(state);
|
|
107
|
+
upstreams.delete(id);
|
|
108
|
+
try {
|
|
109
|
+
state.ws.close();
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
function _clearTimeout(state) {
|
|
113
|
+
if (state.timeout !== void 0) {
|
|
114
|
+
clearTimeout(state.timeout);
|
|
115
|
+
state.timeout = void 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function _resolveTarget(target, peer) {
|
|
119
|
+
const raw = typeof target === "function" ? target(peer) : target;
|
|
120
|
+
return raw instanceof URL ? raw : new URL(raw);
|
|
121
|
+
}
|
|
122
|
+
function _resolveWsOptions(headers, peer) {
|
|
123
|
+
if (!headers) return;
|
|
124
|
+
const resolved = typeof headers === "function" ? headers(peer) : headers;
|
|
125
|
+
if (!resolved) return;
|
|
126
|
+
return { headers: resolved };
|
|
127
|
+
}
|
|
128
|
+
function _resolveProtocols(peer, forwardProtocol) {
|
|
129
|
+
if (forwardProtocol === false) return;
|
|
130
|
+
const header = peer.request?.headers.get("sec-websocket-protocol");
|
|
131
|
+
if (!header) return;
|
|
132
|
+
return header.split(",").map((p) => p.trim()).filter(Boolean);
|
|
133
|
+
}
|
|
134
|
+
function _safeClose(peer, code, reason) {
|
|
135
|
+
try {
|
|
136
|
+
peer.close(code, _truncateReason(reason));
|
|
137
|
+
} catch {}
|
|
138
|
+
}
|
|
139
|
+
function _safeSend(peer, data) {
|
|
140
|
+
try {
|
|
141
|
+
peer.send(data);
|
|
142
|
+
} catch {}
|
|
143
|
+
}
|
|
144
|
+
function _truncateReason(reason) {
|
|
145
|
+
if (!reason) return reason;
|
|
146
|
+
const bytes = new TextEncoder().encode(reason);
|
|
147
|
+
if (bytes.length <= 123) return reason;
|
|
148
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(bytes.subarray(0, 123));
|
|
149
|
+
}
|
|
150
|
+
function _remapIncomingCode(code) {
|
|
151
|
+
if (code === void 0) return void 0;
|
|
152
|
+
if (code === 1005) return 1e3;
|
|
153
|
+
if (code === 1006 || code === 1015) return 1011;
|
|
154
|
+
return code;
|
|
155
|
+
}
|
|
156
|
+
function _normalizeOutgoingCode(code) {
|
|
157
|
+
if (code === void 0) return void 0;
|
|
158
|
+
if (code === 1e3) return 1e3;
|
|
159
|
+
if (code >= 3e3 && code <= 4999) return code;
|
|
160
|
+
return 1e3;
|
|
161
|
+
}
|
|
162
|
+
export { createWebSocketProxy, defineHooks, defineWebSocketAdapter };
|
package/dist/server/bun.d.mts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import "../_chunks/bun.mjs";
|
|
2
|
-
import "../_chunks/cloudflare.mjs";
|
|
3
|
-
import "../_chunks/node.mjs";
|
|
4
1
|
import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
|
|
5
2
|
import { Server, ServerPlugin } from "srvx";
|
|
6
|
-
|
|
7
|
-
//#region src/server/bun.d.ts
|
|
8
3
|
declare function plugin(wsOpts: WSOptions): ServerPlugin;
|
|
9
4
|
declare function serve(options: ServerWithWSOptions): Server;
|
|
10
|
-
//#endregion
|
|
11
5
|
export { plugin, serve };
|
package/dist/server/bun.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import bunAdapter from "../adapters/bun.mjs";
|
|
2
2
|
import { serve as serve$1 } from "srvx/bun";
|
|
3
|
-
|
|
4
|
-
//#region src/server/bun.ts
|
|
5
3
|
function plugin(wsOpts) {
|
|
6
4
|
return (server) => {
|
|
7
|
-
const ws =
|
|
5
|
+
const ws = bunAdapter({
|
|
8
6
|
hooks: wsOpts,
|
|
9
7
|
resolve: wsOpts.resolve,
|
|
10
8
|
...wsOpts.options?.bun
|
|
@@ -25,6 +23,4 @@ function serve(options) {
|
|
|
25
23
|
}
|
|
26
24
|
return serve$1(options);
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
//#endregion
|
|
30
|
-
export { plugin, serve };
|
|
26
|
+
export { plugin, serve };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
|
|
2
|
+
import { Server, ServerPlugin } from "srvx";
|
|
3
|
+
declare function plugin(wsOpts: WSOptions): ServerPlugin;
|
|
4
|
+
declare function serve(options: ServerWithWSOptions): Server;
|
|
5
|
+
export { plugin, serve };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import bunnyAdapter from "../adapters/bunny.mjs";
|
|
2
|
+
import { serve as serve$1 } from "srvx/bunny";
|
|
3
|
+
function plugin(wsOpts) {
|
|
4
|
+
return (server) => {
|
|
5
|
+
const ws = bunnyAdapter({
|
|
6
|
+
hooks: wsOpts,
|
|
7
|
+
resolve: wsOpts.resolve,
|
|
8
|
+
...wsOpts.options?.bunny
|
|
9
|
+
});
|
|
10
|
+
server.options.middleware.unshift((req, next) => {
|
|
11
|
+
if (req.headers.get("upgrade")?.toLowerCase() === "websocket") return ws.handleUpgrade(req);
|
|
12
|
+
return next();
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function serve(options) {
|
|
17
|
+
if (options.websocket) {
|
|
18
|
+
options.plugins ||= [];
|
|
19
|
+
options.plugins.push(plugin(options.websocket));
|
|
20
|
+
}
|
|
21
|
+
return serve$1(options);
|
|
22
|
+
}
|
|
23
|
+
export { plugin, serve };
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import "../_chunks/bun.mjs";
|
|
2
|
-
import "../_chunks/cloudflare.mjs";
|
|
3
|
-
import "../_chunks/node.mjs";
|
|
4
1
|
import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
|
|
5
2
|
import { Server, ServerPlugin } from "srvx";
|
|
6
|
-
|
|
7
|
-
//#region src/server/cloudflare.d.ts
|
|
8
3
|
declare function plugin(wsOpts: WSOptions): ServerPlugin;
|
|
9
4
|
declare function serve(options: ServerWithWSOptions): Server;
|
|
10
|
-
//#endregion
|
|
11
5
|
export { plugin, serve };
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cloudflareAdapter from "../adapters/cloudflare.mjs";
|
|
2
2
|
import { serve as serve$1 } from "srvx/cloudflare";
|
|
3
|
-
|
|
4
|
-
//#region src/server/cloudflare.ts
|
|
5
3
|
function plugin(wsOpts) {
|
|
6
4
|
return (server) => {
|
|
7
|
-
const ws =
|
|
5
|
+
const ws = cloudflareAdapter({
|
|
8
6
|
hooks: wsOpts,
|
|
9
7
|
resolve: wsOpts.resolve,
|
|
10
8
|
...wsOpts.options?.cloudflare
|
|
@@ -22,6 +20,4 @@ function serve(options) {
|
|
|
22
20
|
}
|
|
23
21
|
return serve$1(options);
|
|
24
22
|
}
|
|
25
|
-
|
|
26
|
-
//#endregion
|
|
27
|
-
export { plugin, serve };
|
|
23
|
+
export { plugin, serve };
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import "../_chunks/bun.mjs";
|
|
2
|
-
import "../_chunks/cloudflare.mjs";
|
|
3
|
-
import "../_chunks/node.mjs";
|
|
4
1
|
import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
|
|
5
2
|
import { Server, ServerPlugin } from "srvx";
|
|
6
|
-
|
|
7
|
-
//#region src/server/default.d.ts
|
|
8
3
|
declare function plugin(wsOpts: WSOptions): ServerPlugin;
|
|
9
4
|
declare function serve(options: ServerWithWSOptions): Server;
|
|
10
|
-
//#endregion
|
|
11
5
|
export { plugin, serve };
|
package/dist/server/default.mjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import sseAdapter from "../adapters/sse.mjs";
|
|
2
2
|
import { serve as serve$1 } from "srvx";
|
|
3
|
-
|
|
4
|
-
//#region src/server/default.ts
|
|
5
3
|
function plugin(wsOpts) {
|
|
6
|
-
const ws =
|
|
4
|
+
const ws = sseAdapter({
|
|
7
5
|
hooks: wsOpts,
|
|
8
6
|
resolve: wsOpts.resolve,
|
|
9
7
|
...wsOpts.options?.sse
|
|
@@ -23,6 +21,4 @@ function serve(options) {
|
|
|
23
21
|
}
|
|
24
22
|
return serve$1(options);
|
|
25
23
|
}
|
|
26
|
-
|
|
27
|
-
//#endregion
|
|
28
|
-
export { plugin, serve };
|
|
24
|
+
export { plugin, serve };
|
package/dist/server/deno.d.mts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import "../_chunks/bun.mjs";
|
|
2
|
-
import "../_chunks/cloudflare.mjs";
|
|
3
|
-
import "../_chunks/node.mjs";
|
|
4
1
|
import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
|
|
5
2
|
import { Server, ServerPlugin } from "srvx";
|
|
6
|
-
|
|
7
|
-
//#region src/server/deno.d.ts
|
|
8
3
|
declare function plugin(wsOpts: WSOptions): ServerPlugin;
|
|
9
4
|
declare function serve(options: ServerWithWSOptions): Server;
|
|
10
|
-
//#endregion
|
|
11
5
|
export { plugin, serve };
|
package/dist/server/deno.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import denoAdapter from "../adapters/deno.mjs";
|
|
2
2
|
import { serve as serve$1 } from "srvx/deno";
|
|
3
|
-
|
|
4
|
-
//#region src/server/deno.ts
|
|
5
3
|
function plugin(wsOpts) {
|
|
6
4
|
return (server) => {
|
|
7
|
-
const ws =
|
|
5
|
+
const ws = denoAdapter({
|
|
8
6
|
hooks: wsOpts,
|
|
9
7
|
resolve: wsOpts.resolve,
|
|
10
8
|
...wsOpts.options?.deno
|
|
@@ -22,6 +20,4 @@ function serve(options) {
|
|
|
22
20
|
}
|
|
23
21
|
return serve$1(options);
|
|
24
22
|
}
|
|
25
|
-
|
|
26
|
-
//#endregion
|
|
27
|
-
export { plugin, serve };
|
|
23
|
+
export { plugin, serve };
|
package/dist/server/node.d.mts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import "../_chunks/bun.mjs";
|
|
2
|
-
import "../_chunks/cloudflare.mjs";
|
|
3
|
-
import "../_chunks/node.mjs";
|
|
4
1
|
import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
|
|
5
2
|
import { Server, ServerPlugin } from "srvx";
|
|
6
|
-
|
|
7
|
-
//#region src/server/node.d.ts
|
|
8
3
|
declare function plugin(wsOpts: WSOptions): ServerPlugin;
|
|
9
4
|
declare function serve(options: ServerWithWSOptions): Server;
|
|
10
|
-
//#endregion
|
|
11
5
|
export { plugin, serve };
|
package/dist/server/node.mjs
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import "../_chunks/
|
|
2
|
-
import "../_chunks/libs/ws.mjs";
|
|
3
|
-
import node_default from "../adapters/node.mjs";
|
|
1
|
+
import { t as nodeAdapter } from "../_chunks/node.mjs";
|
|
4
2
|
import { NodeRequest, serve as serve$1 } from "srvx/node";
|
|
5
|
-
|
|
6
|
-
//#region src/server/node.ts
|
|
7
3
|
function plugin(wsOpts) {
|
|
8
4
|
return (server) => {
|
|
9
|
-
const ws =
|
|
5
|
+
const ws = nodeAdapter({
|
|
10
6
|
hooks: wsOpts,
|
|
11
7
|
resolve: wsOpts.resolve,
|
|
12
8
|
...wsOpts.options?.deno
|
|
@@ -33,6 +29,4 @@ function serve(options) {
|
|
|
33
29
|
}
|
|
34
30
|
return serve$1(options);
|
|
35
31
|
}
|
|
36
|
-
|
|
37
|
-
//#endregion
|
|
38
|
-
export { plugin, serve };
|
|
32
|
+
export { plugin, serve };
|