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.
Files changed (2) hide show
  1. package/dist/cli.js +158 -3
  2. 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
- if (k.toLowerCase() !== "host") localHeaders[k] = v;
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
- sendBinary(sock, streamId, chunk);
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 (!cancelled && upstream) upstream.write(chunk);
2173
+ if (cancelled || !upstream) return;
2174
+ upstream.write(createUpstreamTextFrame(chunk));
2020
2175
  },
2021
2176
  endBody: () => {
2022
2177
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.7.0",
3
+ "version": "3.7.2",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",