ftown-bridge 0.19.19 → 0.19.21

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.
@@ -0,0 +1,228 @@
1
+ import http from 'node:http';
2
+ import crypto from 'node:crypto';
3
+ /**
4
+ * ws-proxy — P1-P5 proxying for solo mode (contract v4).
5
+ *
6
+ * S20 SINGLE-PARSE SEAM — the ONE coherent interpretation of routing-table row
7
+ * "GET /hub/connection/websocket → proxied to hub" + P1 + the hub-config note:
8
+ *
9
+ * 1. The PUBLIC request path is matched against ^/hub/connection/websocket$
10
+ * (case-sensitive, exact — no trailing segments, no percent-encoded
11
+ * slashes, no double slashes, no NUL suffixes; query strings allowed).
12
+ * 2. On match, the FORWARD path sent to the hub is the public path with the
13
+ * single leading '/hub' prefix stripped → '/connection/websocket', which
14
+ * is centrifugo's default path (no hub path options needed).
15
+ *
16
+ * (P1's regex describes the public allowlist; stripping happens only for
17
+ * forwarding. Anything else under /hub is never forwarded — solo-server 404s
18
+ * it; this module exposes only parse + forwarding.)
19
+ */
20
+ /** Public path allowlist (P1) — exact match required. */
21
+ export const HUB_UPGRADE_PUBLIC_PATH = '/hub/connection/websocket';
22
+ /** Forward path to the hub after stripping the single leading '/hub'. */
23
+ export const HUB_UPSTREAM_WS_PATH = '/connection/websocket';
24
+ /**
25
+ * Inbound hop-by-hop headers stripped per P3 — exact contract list.
26
+ */
27
+ export const HOP_BY_HOP_HEADERS = [
28
+ 'connection',
29
+ 'keep-alive',
30
+ 'proxy-authenticate',
31
+ 'proxy-authorization',
32
+ 'te',
33
+ 'trailer',
34
+ 'transfer-encoding',
35
+ 'upgrade',
36
+ ];
37
+ const HOP_BY_HOP = new Set(HOP_BY_HOP_HEADERS);
38
+ const BAD_GATEWAY_BODY = '{"error":"upstream unavailable"}';
39
+ /**
40
+ * S20 seam: parse req.url ONCE and decide whether it targets the proxied hub
41
+ * upgrade path. Percent-decoded / differently-cased / extra-segment variants
42
+ * are NOT the allowlist path (goldens pinned in tests).
43
+ */
44
+ export function parseHubTarget(url) {
45
+ try {
46
+ const { pathname } = new URL(url, 'http://solo.invalid');
47
+ return { isHubUpgradePath: pathname === HUB_UPGRADE_PUBLIC_PATH };
48
+ }
49
+ catch {
50
+ return { isHubUpgradePath: false };
51
+ }
52
+ }
53
+ /**
54
+ * P3/P4 inbound header sanitizer shared by both proxy paths:
55
+ * - drops every hop-by-hop header in HOP_BY_HOP_HEADERS,
56
+ * - drops every sec-websocket-* header (upstream handshake recomputes;
57
+ * handleHubUpgrade re-adds a fresh key/version so no deflate survives),
58
+ * - drops every inbound x-forwarded-* header (never relayed — S13),
59
+ * - rewrites Host to 127.0.0.1:<targetPort> (P2),
60
+ * - sets X-Forwarded-Proto from the caller-supplied scheme (P4).
61
+ */
62
+ function sanitizeRequestHeaders(headers, targetPort, forwardedProto) {
63
+ const out = {};
64
+ for (const [name, value] of Object.entries(headers)) {
65
+ const lower = name.toLowerCase();
66
+ if (HOP_BY_HOP.has(lower))
67
+ continue;
68
+ if (lower.startsWith('sec-websocket-'))
69
+ continue;
70
+ if (lower.startsWith('x-forwarded-'))
71
+ continue;
72
+ out[lower] = value;
73
+ }
74
+ out['host'] = `127.0.0.1:${targetPort}`;
75
+ out['x-forwarded-proto'] = forwardedProto;
76
+ return out;
77
+ }
78
+ /** Strip hop-by-hop headers from an upstream response before relaying (P3). */
79
+ function sanitizeResponseHeaders(headers) {
80
+ const out = {};
81
+ for (const [name, value] of Object.entries(headers)) {
82
+ const lower = name.toLowerCase();
83
+ if (HOP_BY_HOP.has(lower))
84
+ continue;
85
+ out[lower] = value;
86
+ }
87
+ return out;
88
+ }
89
+ function writeJsonError(socket, statusCode, statusText, error) {
90
+ try {
91
+ socket.end(`HTTP/1.1 ${statusCode} ${statusText}\r\n` +
92
+ 'content-type: application/json\r\n' +
93
+ `content-length: ${Buffer.byteLength(error)}\r\n` +
94
+ 'connection: close\r\n' +
95
+ '\r\n' +
96
+ error);
97
+ }
98
+ catch {
99
+ // Socket already gone — nothing to relay the error to.
100
+ }
101
+ }
102
+ /**
103
+ * P2/P3/P4 plain-HTTP proxy hop to 127.0.0.1:<targetPort>. Request and response
104
+ * body streams are piped both directions; status/headers propagate minus
105
+ * hop-by-hop. Upstream failure yields {"error":...} with 502 (headers not yet
106
+ * sent) or destroys the response.
107
+ */
108
+ export function proxyHttpRequest(req, res, targetPort, forwardedProto = 'http') {
109
+ const upstream = http.request({
110
+ host: '127.0.0.1',
111
+ port: targetPort,
112
+ path: req.url ?? '/',
113
+ method: req.method ?? 'GET',
114
+ headers: sanitizeRequestHeaders(req.headers, targetPort, forwardedProto),
115
+ }, (upstreamRes) => {
116
+ res.writeHead(upstreamRes.statusCode ?? 502, sanitizeResponseHeaders(upstreamRes.headers));
117
+ upstreamRes.pipe(res);
118
+ });
119
+ req.pipe(upstream);
120
+ upstream.on('error', () => {
121
+ if (res.headersSent) {
122
+ res.destroy();
123
+ return;
124
+ }
125
+ res.writeHead(502, {
126
+ 'content-type': 'application/json',
127
+ 'content-length': Buffer.byteLength(BAD_GATEWAY_BODY),
128
+ connection: 'close',
129
+ });
130
+ res.end(BAD_GATEWAY_BODY);
131
+ });
132
+ }
133
+ /**
134
+ * WebSocket upgrade proxy (P1/P2/P3/P5): validates the S20 allowlist first,
135
+ * then opens an HTTP upgrade to 127.0.0.1:<targetPort> at
136
+ * HUB_UPSTREAM_WS_PATH with sanitized headers. All inbound sec-websocket-*
137
+ * are stripped (no extension offer survives — compression stays off), then
138
+ * the handshake identity is re-set: version 13 plus the CLIENT'S OWN
139
+ * Sec-WebSocket-Key (fresh random fallback). Preserving the client key lets
140
+ * the UPSTREAM handshake compute a Sec-WebSocket-Accept that validates
141
+ * end-to-end while this module recomputes nothing locally. On upstream 101
142
+ * the upstream-computed sec-websocket-* headers are relayed verbatim, then
143
+ * both sockets are piped so protocol pings/pongs pass untouched (P5).
144
+ */
145
+ export function handleHubUpgrade(req, socket, head, targetPort, forwardedProto = 'http') {
146
+ if (!parseHubTarget(req.url ?? '').isHubUpgradePath || (req.headers.upgrade ?? '').toLowerCase() !== 'websocket') {
147
+ writeJsonError(socket, 404, 'Not Found', '{"error":"not found"}');
148
+ return;
149
+ }
150
+ const headers = sanitizeRequestHeaders(req.headers, targetPort, forwardedProto);
151
+ headers['connection'] = 'Upgrade';
152
+ headers['upgrade'] = 'websocket';
153
+ const clientKey = req.headers['sec-websocket-key'];
154
+ headers['sec-websocket-key'] =
155
+ typeof clientKey === 'string' && clientKey.length > 0
156
+ ? clientKey
157
+ : crypto.randomBytes(16).toString('base64');
158
+ headers['sec-websocket-version'] = '13';
159
+ const killBoth = (a, b) => {
160
+ a.destroy();
161
+ b.destroy();
162
+ };
163
+ const upstream = http.request({
164
+ host: '127.0.0.1',
165
+ port: targetPort,
166
+ path: HUB_UPSTREAM_WS_PATH,
167
+ method: 'GET',
168
+ headers,
169
+ });
170
+ upstream.on('upgrade', (upstreamRes, upstreamSocket, upstreamHead) => {
171
+ // Relay upstream's 101 + its OWN sec-websocket-* accept headers (P3).
172
+ const lines = ['HTTP/1.1 101 Switching Protocols', 'connection: Upgrade', 'upgrade: websocket'];
173
+ for (const name of ['sec-websocket-accept', 'sec-websocket-protocol', 'sec-websocket-extensions']) {
174
+ const value = upstreamRes.headers[name];
175
+ if (value !== undefined) {
176
+ lines.push(`${name}: ${Array.isArray(value) ? value.join(', ') : value}`);
177
+ }
178
+ }
179
+ try {
180
+ socket.write(lines.join('\r\n') + '\r\n\r\n');
181
+ if (head.length > 0)
182
+ socket.write(head);
183
+ if (upstreamHead.length > 0)
184
+ socket.write(upstreamHead);
185
+ }
186
+ catch {
187
+ upstreamSocket.destroy();
188
+ socket.destroy();
189
+ return;
190
+ }
191
+ upstreamSocket.pipe(socket);
192
+ socket.pipe(upstreamSocket);
193
+ upstreamSocket.on('error', () => killBoth(upstreamSocket, socket));
194
+ socket.on('error', () => killBoth(upstreamSocket, socket));
195
+ upstreamSocket.on('close', () => socket.destroy());
196
+ socket.on('close', () => upstreamSocket.destroy());
197
+ });
198
+ // Hub answered with a plain HTTP response (e.g. rejected handshake): relay
199
+ // it once, then close.
200
+ upstream.on('response', (upstreamRes) => {
201
+ const chunks = [];
202
+ upstreamRes.on('data', (chunk) => chunks.push(chunk));
203
+ upstreamRes.on('end', () => {
204
+ const body = Buffer.concat(chunks);
205
+ const relayed = [`HTTP/1.1 ${upstreamRes.statusCode} ${upstreamRes.statusMessage ?? ''}`.trimEnd()];
206
+ for (const [name, value] of Object.entries(sanitizeResponseHeaders(upstreamRes.headers))) {
207
+ if (name === 'content-length')
208
+ continue;
209
+ if (value !== undefined)
210
+ relayed.push(`${name}: ${Array.isArray(value) ? value.join(', ') : value}`);
211
+ }
212
+ relayed.push(`content-length: ${body.length}`, 'connection: close', '');
213
+ try {
214
+ socket.end(relayed.join('\r\n') + '\r\n' + (body.length > 0 ? body : ''));
215
+ }
216
+ catch {
217
+ socket.destroy();
218
+ }
219
+ });
220
+ upstreamRes.on('error', () => socket.destroy());
221
+ });
222
+ // Private child down: clean 502-style close instead of a hang/crash.
223
+ upstream.on('error', () => {
224
+ writeJsonError(socket, 502, 'Bad Gateway', BAD_GATEWAY_BODY);
225
+ });
226
+ upstream.end();
227
+ }
228
+ //# sourceMappingURL=ws-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-proxy.js","sourceRoot":"","sources":["../../src/solo/ws-proxy.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ftown-bridge",
3
- "version": "0.19.19",
3
+ "version": "0.19.21",
4
4
  "description": "CLI bridge for ftown — generic PTY-over-Centrifugo relay",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",