connectbase-client 3.7.0 → 3.7.2
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/dist/cli.js +158 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1456,6 +1456,129 @@ function createWsBinaryFrame(payload) {
|
|
|
1456
1456
|
}
|
|
1457
1457
|
return Buffer.concat([header, maskKey, masked]);
|
|
1458
1458
|
}
|
|
1459
|
+
var UpstreamWsFrameParser = class {
|
|
1460
|
+
constructor(handlers) {
|
|
1461
|
+
this.buffer = Buffer.alloc(0);
|
|
1462
|
+
this.fragmentBuffer = null;
|
|
1463
|
+
this.h = handlers;
|
|
1464
|
+
}
|
|
1465
|
+
feed(chunk) {
|
|
1466
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
1467
|
+
try {
|
|
1468
|
+
this.parse();
|
|
1469
|
+
} catch (e) {
|
|
1470
|
+
this.h.onError(e instanceof Error ? e : new Error(String(e)));
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
parse() {
|
|
1474
|
+
while (this.buffer.length >= 2) {
|
|
1475
|
+
const firstByte = this.buffer[0];
|
|
1476
|
+
const secondByte = this.buffer[1];
|
|
1477
|
+
const fin = (firstByte & 128) !== 0;
|
|
1478
|
+
const rsv1 = (firstByte & 64) !== 0;
|
|
1479
|
+
const opcode = firstByte & 15;
|
|
1480
|
+
const isMasked = (secondByte & 128) !== 0;
|
|
1481
|
+
let payloadLen = secondByte & 127;
|
|
1482
|
+
let offset = 2;
|
|
1483
|
+
if (payloadLen === 126) {
|
|
1484
|
+
if (this.buffer.length < 4) return;
|
|
1485
|
+
payloadLen = this.buffer.readUInt16BE(2);
|
|
1486
|
+
offset = 4;
|
|
1487
|
+
} else if (payloadLen === 127) {
|
|
1488
|
+
if (this.buffer.length < 10) return;
|
|
1489
|
+
const big = this.buffer.readBigUInt64BE(2);
|
|
1490
|
+
if (big > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
1491
|
+
throw new Error(`payload too large: ${big}`);
|
|
1492
|
+
}
|
|
1493
|
+
payloadLen = Number(big);
|
|
1494
|
+
offset = 10;
|
|
1495
|
+
}
|
|
1496
|
+
let maskKey = null;
|
|
1497
|
+
if (isMasked) {
|
|
1498
|
+
if (this.buffer.length < offset + 4) return;
|
|
1499
|
+
maskKey = this.buffer.subarray(offset, offset + 4);
|
|
1500
|
+
offset += 4;
|
|
1501
|
+
}
|
|
1502
|
+
if (this.buffer.length < offset + payloadLen) return;
|
|
1503
|
+
if (rsv1 && (opcode === 1 || opcode === 2 || opcode === 0)) {
|
|
1504
|
+
throw new Error("upstream sent compressed (RSV1) frame without negotiation");
|
|
1505
|
+
}
|
|
1506
|
+
let payload = this.buffer.subarray(offset, offset + payloadLen);
|
|
1507
|
+
if (maskKey) {
|
|
1508
|
+
const unmasked = Buffer.alloc(payloadLen);
|
|
1509
|
+
for (let i = 0; i < payloadLen; i++) {
|
|
1510
|
+
unmasked[i] = payload[i] ^ maskKey[i % 4];
|
|
1511
|
+
}
|
|
1512
|
+
payload = unmasked;
|
|
1513
|
+
}
|
|
1514
|
+
this.buffer = this.buffer.subarray(offset + payloadLen);
|
|
1515
|
+
const payloadCopy = Buffer.from(payload);
|
|
1516
|
+
switch (opcode) {
|
|
1517
|
+
case 0:
|
|
1518
|
+
if (this.fragmentBuffer == null) {
|
|
1519
|
+
throw new Error("continuation frame without preceding non-final data frame");
|
|
1520
|
+
}
|
|
1521
|
+
this.fragmentBuffer = Buffer.concat([this.fragmentBuffer, payloadCopy]);
|
|
1522
|
+
if (fin) {
|
|
1523
|
+
const out = this.fragmentBuffer;
|
|
1524
|
+
this.fragmentBuffer = null;
|
|
1525
|
+
this.h.onPayload(out);
|
|
1526
|
+
}
|
|
1527
|
+
break;
|
|
1528
|
+
case 1:
|
|
1529
|
+
// TEXT
|
|
1530
|
+
case 2:
|
|
1531
|
+
if (fin) {
|
|
1532
|
+
this.h.onPayload(payloadCopy);
|
|
1533
|
+
} else {
|
|
1534
|
+
this.fragmentBuffer = payloadCopy;
|
|
1535
|
+
}
|
|
1536
|
+
break;
|
|
1537
|
+
case 8:
|
|
1538
|
+
this.h.onClose();
|
|
1539
|
+
break;
|
|
1540
|
+
case 9:
|
|
1541
|
+
this.h.onPing(payloadCopy);
|
|
1542
|
+
break;
|
|
1543
|
+
case 10:
|
|
1544
|
+
break;
|
|
1545
|
+
default:
|
|
1546
|
+
throw new Error(`unknown WS opcode 0x${opcode.toString(16)}`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
function createUpstreamTextFrame(payload) {
|
|
1552
|
+
return buildClientFrame(129, payload);
|
|
1553
|
+
}
|
|
1554
|
+
function createUpstreamPongFrame(payload) {
|
|
1555
|
+
return buildClientFrame(138, payload);
|
|
1556
|
+
}
|
|
1557
|
+
function buildClientFrame(firstByte, payload) {
|
|
1558
|
+
const len = payload.length;
|
|
1559
|
+
const maskKey = crypto.randomBytes(4);
|
|
1560
|
+
let header;
|
|
1561
|
+
if (len < 126) {
|
|
1562
|
+
header = Buffer.alloc(2);
|
|
1563
|
+
header[0] = firstByte;
|
|
1564
|
+
header[1] = 128 | len;
|
|
1565
|
+
} else if (len < 65536) {
|
|
1566
|
+
header = Buffer.alloc(4);
|
|
1567
|
+
header[0] = firstByte;
|
|
1568
|
+
header[1] = 128 | 126;
|
|
1569
|
+
header.writeUInt16BE(len, 2);
|
|
1570
|
+
} else {
|
|
1571
|
+
header = Buffer.alloc(10);
|
|
1572
|
+
header[0] = firstByte;
|
|
1573
|
+
header[1] = 128 | 127;
|
|
1574
|
+
header.writeBigUInt64BE(BigInt(len), 2);
|
|
1575
|
+
}
|
|
1576
|
+
const masked = Buffer.alloc(len);
|
|
1577
|
+
for (let i = 0; i < len; i++) {
|
|
1578
|
+
masked[i] = payload[i] ^ maskKey[i % 4];
|
|
1579
|
+
}
|
|
1580
|
+
return Buffer.concat([header, maskKey, masked]);
|
|
1581
|
+
}
|
|
1459
1582
|
var WsFrameParser = class {
|
|
1460
1583
|
constructor(handlers) {
|
|
1461
1584
|
this.buffer = Buffer.alloc(0);
|
|
@@ -1951,9 +2074,14 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1951
2074
|
const fullPath = query ? `${reqPath}?${query}` : reqPath;
|
|
1952
2075
|
const localHeaders = {};
|
|
1953
2076
|
for (const [k, v] of Object.entries(headers)) {
|
|
1954
|
-
|
|
2077
|
+
const lk = k.toLowerCase();
|
|
2078
|
+
if (lk === "host") continue;
|
|
2079
|
+
if (lk === "sec-websocket-extensions") continue;
|
|
2080
|
+
localHeaders[k] = v;
|
|
1955
2081
|
}
|
|
1956
2082
|
localHeaders["host"] = `localhost:${localPort}`;
|
|
2083
|
+
localHeaders["connection"] = "Upgrade";
|
|
2084
|
+
localHeaders["upgrade"] = "websocket";
|
|
1957
2085
|
let cancelled = false;
|
|
1958
2086
|
let upstream = null;
|
|
1959
2087
|
const req = http.request({
|
|
@@ -1978,9 +2106,28 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1978
2106
|
websocket: true
|
|
1979
2107
|
});
|
|
1980
2108
|
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${colors.cyan}WS${colors.reset} ${reqPath} \u2192 101`);
|
|
2109
|
+
const upstreamParser = new UpstreamWsFrameParser({
|
|
2110
|
+
onPayload: (payload) => {
|
|
2111
|
+
if (cancelled) return;
|
|
2112
|
+
sendBinary(sock, streamId, payload);
|
|
2113
|
+
},
|
|
2114
|
+
onPing: (payload) => {
|
|
2115
|
+
if (cancelled || !upstream) return;
|
|
2116
|
+
upstream.write(createUpstreamPongFrame(payload));
|
|
2117
|
+
},
|
|
2118
|
+
onClose: () => {
|
|
2119
|
+
if (cancelled) return;
|
|
2120
|
+
sendControl(sock, { type: "stream_close", stream_id: streamId });
|
|
2121
|
+
streams.delete(streamId);
|
|
2122
|
+
},
|
|
2123
|
+
onError: (err) => {
|
|
2124
|
+
sendControl(sock, { type: "stream_close", stream_id: streamId, error: `upstream_frame_error: ${err.message}` });
|
|
2125
|
+
streams.delete(streamId);
|
|
2126
|
+
}
|
|
2127
|
+
});
|
|
1981
2128
|
sk.on("data", (chunk) => {
|
|
1982
2129
|
if (cancelled) return;
|
|
1983
|
-
|
|
2130
|
+
upstreamParser.feed(chunk);
|
|
1984
2131
|
});
|
|
1985
2132
|
sk.on("close", () => {
|
|
1986
2133
|
if (cancelled) return;
|
|
@@ -2015,8 +2162,16 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
2015
2162
|
req.end();
|
|
2016
2163
|
const forwarder = {
|
|
2017
2164
|
kind: "ws",
|
|
2165
|
+
// Encode the v2 payload as an upstream WS frame BEFORE writing to
|
|
2166
|
+
// the socket. The prior implementation wrote raw payload to the
|
|
2167
|
+
// socket which is not a valid WS frame; upstream WS servers (e.g.
|
|
2168
|
+
// ComfyUI) would error out. Default to TEXT opcode — the v2
|
|
2169
|
+
// protocol does not propagate the original opcode from the client,
|
|
2170
|
+
// and most WS APIs (JSON-based) use text. Binary client→upstream
|
|
2171
|
+
// is a known limitation pending opcode propagation in v2.
|
|
2018
2172
|
feedBody: (chunk) => {
|
|
2019
|
-
if (
|
|
2173
|
+
if (cancelled || !upstream) return;
|
|
2174
|
+
upstream.write(createUpstreamTextFrame(chunk));
|
|
2020
2175
|
},
|
|
2021
2176
|
endBody: () => {
|
|
2022
2177
|
},
|