connectbase-client 3.7.1 → 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 +156 -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,7 +2074,10 @@ ${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}`;
1957
2083
  localHeaders["connection"] = "Upgrade";
@@ -1980,9 +2106,28 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
1980
2106
  websocket: true
1981
2107
  });
1982
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
+ });
1983
2128
  sk.on("data", (chunk) => {
1984
2129
  if (cancelled) return;
1985
- sendBinary(sock, streamId, chunk);
2130
+ upstreamParser.feed(chunk);
1986
2131
  });
1987
2132
  sk.on("close", () => {
1988
2133
  if (cancelled) return;
@@ -2017,8 +2162,16 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
2017
2162
  req.end();
2018
2163
  const forwarder = {
2019
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.
2020
2172
  feedBody: (chunk) => {
2021
- if (!cancelled && upstream) upstream.write(chunk);
2173
+ if (cancelled || !upstream) return;
2174
+ upstream.write(createUpstreamTextFrame(chunk));
2022
2175
  },
2023
2176
  endBody: () => {
2024
2177
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.7.1",
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",