grpc-libp2p-client 0.0.21 → 0.0.23

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.
@@ -6,6 +6,7 @@ const FRAME_TYPES = {
6
6
  RST_STREAM: 0x3, // 终止流
7
7
  SETTINGS: 0x4, // 设置连接参数
8
8
  PING: 0x6, // 测量RTT和活性检查
9
+ GOAWAY: 0x7, // 优雅地关闭连接
9
10
  WINDOW_UPDATE: 0x8};
10
11
  const FRAME_FLAGS = {
11
12
  ACK: 0x1,
@@ -1515,18 +1516,25 @@ function _createPayload(settings) {
1515
1516
  }
1516
1517
 
1517
1518
  class HTTP2Parser {
1518
- constructor(writer) {
1519
+ constructor(writer, options) {
1519
1520
  this.buffer = new Uint8Array(0);
1520
1521
  this.settingsAckReceived = false;
1522
+ this.peerSettingsReceived = false;
1521
1523
  // 初始化连接级别的流控制窗口大小(默认值:65,535)
1522
1524
  this.connectionWindowSize = 4 << 20;
1523
1525
  // 存储流的Map
1524
1526
  this.streams = new Map();
1525
1527
  // 默认的流级别初始窗口大小
1526
1528
  this.defaultStreamWindowSize = 4 << 20;
1529
+ // 发送方向窗口(对端接收窗口)默认均为 65535
1530
+ this.sendConnWindow = 65535;
1531
+ this.sendStreamWindows = new Map();
1532
+ this.peerInitialStreamWindow = 65535;
1533
+ this.sendWindowWaiters = [];
1527
1534
  // 结束标志
1528
1535
  this.endFlag = false;
1529
1536
  this.writer = writer;
1537
+ this.compatibilityMode = options?.compatibilityMode ?? false;
1530
1538
  }
1531
1539
  // 持续处理流数据
1532
1540
  async processStream(stream) {
@@ -1567,6 +1575,17 @@ class HTTP2Parser {
1567
1575
  console.error("Error processing stream:", error);
1568
1576
  throw error;
1569
1577
  }
1578
+ finally {
1579
+ if (!this.compatibilityMode && !this.endFlag) {
1580
+ this.endFlag = true;
1581
+ try {
1582
+ this.onEnd?.();
1583
+ }
1584
+ catch (err) {
1585
+ console.error("Error during onEnd callback:", err);
1586
+ }
1587
+ }
1588
+ }
1570
1589
  }
1571
1590
  isHttp2Preface(buffer) {
1572
1591
  const PREFACE = new TextEncoder().encode("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
@@ -1598,6 +1617,102 @@ class HTTP2Parser {
1598
1617
  }, 30000);
1599
1618
  });
1600
1619
  }
1620
+ // 等待接收来自对端的 SETTINGS(非 ACK)
1621
+ waitForPeerSettings(timeoutMs = 30000) {
1622
+ return new Promise((resolve, reject) => {
1623
+ if (this.peerSettingsReceived) {
1624
+ resolve();
1625
+ return;
1626
+ }
1627
+ const interval = setInterval(() => {
1628
+ if (this.peerSettingsReceived) {
1629
+ clearInterval(interval);
1630
+ clearTimeout(timeout);
1631
+ resolve();
1632
+ }
1633
+ }, 100);
1634
+ const timeout = setTimeout(() => {
1635
+ clearInterval(interval);
1636
+ reject(new Error("Peer SETTINGS timeout"));
1637
+ }, timeoutMs);
1638
+ });
1639
+ }
1640
+ // 注册我们要发送数据的出站流(用于初始化该流的对端窗口)
1641
+ registerOutboundStream(streamId) {
1642
+ if (!this.sendStreamWindows.has(streamId)) {
1643
+ this.sendStreamWindows.set(streamId, this.peerInitialStreamWindow);
1644
+ }
1645
+ }
1646
+ // 获取发送窗口
1647
+ getSendWindows(streamId) {
1648
+ const s = this.sendStreamWindows.get(streamId) ?? 0;
1649
+ return { conn: this.sendConnWindow, stream: s };
1650
+ }
1651
+ // 消耗发送窗口(成功写入 DATA 之后调用)
1652
+ consumeSendWindow(streamId, bytes) {
1653
+ this.sendConnWindow = Math.max(0, this.sendConnWindow - bytes);
1654
+ const cur = this.sendStreamWindows.get(streamId) ?? 0;
1655
+ this.sendStreamWindows.set(streamId, Math.max(0, cur - bytes));
1656
+ }
1657
+ // 非标准兜底:在对端未及时发送 WINDOW_UPDATE 时,手动回填窗口额度以避免阻塞
1658
+ unsafeForceExtendSendWindow(streamId, bytes) {
1659
+ if (this.compatibilityMode)
1660
+ return;
1661
+ if (bytes <= 0)
1662
+ return;
1663
+ this.sendConnWindow = Math.min(0x7fffffff, this.sendConnWindow + bytes);
1664
+ const cur = this.sendStreamWindows.get(streamId) ?? 0;
1665
+ this.sendStreamWindows.set(streamId, Math.min(0x7fffffff, cur + bytes));
1666
+ }
1667
+ // 等待可用发送窗口(两个窗口都需要 >0)
1668
+ async waitForSendWindow(streamId, minBytes = 1, timeoutMs = 30000) {
1669
+ const start = Date.now();
1670
+ return new Promise((resolve, reject) => {
1671
+ let interval = null;
1672
+ let settled = false;
1673
+ const check = () => {
1674
+ const { conn, stream } = this.getSendWindows(streamId);
1675
+ if (conn >= minBytes && stream >= minBytes) {
1676
+ if (!settled) {
1677
+ settled = true;
1678
+ if (interval) {
1679
+ clearInterval(interval);
1680
+ interval = null;
1681
+ }
1682
+ resolve();
1683
+ }
1684
+ return true;
1685
+ }
1686
+ if (Date.now() - start > timeoutMs) {
1687
+ if (!settled) {
1688
+ settled = true;
1689
+ if (interval) {
1690
+ clearInterval(interval);
1691
+ interval = null;
1692
+ }
1693
+ reject(new Error('Send window wait timeout'));
1694
+ }
1695
+ return true;
1696
+ }
1697
+ return false;
1698
+ };
1699
+ if (check())
1700
+ return;
1701
+ const tick = () => {
1702
+ if (!check()) ;
1703
+ };
1704
+ const wake = () => { tick(); };
1705
+ // 简单的等待模型:依赖 WINDOW_UPDATE 到达时调用 wake
1706
+ this.sendWindowWaiters.push(wake);
1707
+ // 同时做一个轻微的轮询,防止错过唤醒
1708
+ interval = setInterval(() => {
1709
+ if (check() && interval) {
1710
+ clearInterval(interval);
1711
+ interval = null;
1712
+ }
1713
+ }, 50);
1714
+ });
1715
+ }
1601
1716
  // 处理单个帧
1602
1717
  async _handleFrame(frameHeader, frameData) {
1603
1718
  switch (frameHeader.type) {
@@ -1608,6 +1723,8 @@ class HTTP2Parser {
1608
1723
  else {
1609
1724
  //接收到Setting请求,进行解析
1610
1725
  const settingsPayload = frameData.slice(9);
1726
+ let initialWindowDelta = 0;
1727
+ let maxConcurrentStreams;
1611
1728
  for (let i = 0; i < settingsPayload.length; i += 6) {
1612
1729
  // 正确解析:2字节ID + 4字节值
1613
1730
  const id = (settingsPayload[i] << 8) | settingsPayload[i + 1];
@@ -1617,13 +1734,48 @@ class HTTP2Parser {
1617
1734
  settingsPayload[i + 5];
1618
1735
  if (id === 4) {
1619
1736
  // SETTINGS_INITIAL_WINDOW_SIZE
1620
- this.defaultStreamWindowSize = value;
1737
+ this.defaultStreamWindowSize = value; // 我方接收窗口(入站)
1738
+ initialWindowDelta = value - this.peerInitialStreamWindow;
1739
+ this.peerInitialStreamWindow = value; // 对端接收窗口(我方发送)
1740
+ }
1741
+ else if (id === 3) {
1742
+ // SETTINGS_MAX_CONCURRENT_STREAMS
1743
+ maxConcurrentStreams = value;
1621
1744
  }
1622
1745
  }
1746
+ if (!this.compatibilityMode && initialWindowDelta !== 0) {
1747
+ for (const [sid, current] of this.sendStreamWindows.entries()) {
1748
+ const updated = Math.max(0, current + initialWindowDelta);
1749
+ this.sendStreamWindows.set(sid, updated);
1750
+ }
1751
+ }
1752
+ try {
1753
+ if (this.onSettingsParsed && (maxConcurrentStreams !== undefined || initialWindowDelta !== 0)) {
1754
+ const payload = {};
1755
+ if (maxConcurrentStreams !== undefined) {
1756
+ payload.maxConcurrentStreams = maxConcurrentStreams;
1757
+ }
1758
+ if (initialWindowDelta !== 0) {
1759
+ payload.initialWindowSize = this.peerInitialStreamWindow;
1760
+ }
1761
+ this.onSettingsParsed(payload);
1762
+ }
1763
+ }
1764
+ catch (err) {
1765
+ console.error('Error handling parsed SETTINGS callback:', err);
1766
+ }
1623
1767
  //发送ACK
1624
1768
  if (this.onSettings) {
1625
1769
  this.onSettings(frameHeader);
1626
1770
  }
1771
+ // 标记已收到对端 SETTINGS
1772
+ this.peerSettingsReceived = true;
1773
+ // 唤醒等待窗口(以防部分实现通过 SETTINGS 改变有效窗口)
1774
+ const waiters = this.sendWindowWaiters.splice(0);
1775
+ waiters.forEach(fn => { try {
1776
+ fn();
1777
+ }
1778
+ catch { } });
1627
1779
  }
1628
1780
  break;
1629
1781
  case FRAME_TYPES.DATA:
@@ -1673,11 +1825,60 @@ class HTTP2Parser {
1673
1825
  case FRAME_TYPES.WINDOW_UPDATE:
1674
1826
  // 处理窗口更新帧
1675
1827
  this.handleWindowUpdateFrame(frameHeader, frameData, frameHeader.streamId);
1828
+ // 更新发送窗口(对端接收窗口)
1829
+ try {
1830
+ const inc = this.parseWindowUpdateFrame(frameData, frameHeader).windowSizeIncrement;
1831
+ if (frameHeader.streamId === 0) {
1832
+ this.sendConnWindow += inc;
1833
+ }
1834
+ else {
1835
+ const cur = this.sendStreamWindows.get(frameHeader.streamId) ?? this.peerInitialStreamWindow;
1836
+ this.sendStreamWindows.set(frameHeader.streamId, cur + inc);
1837
+ }
1838
+ const waiters = this.sendWindowWaiters.splice(0);
1839
+ waiters.forEach(fn => { try {
1840
+ fn();
1841
+ }
1842
+ catch { } });
1843
+ }
1844
+ catch (e) { }
1676
1845
  break;
1677
1846
  case FRAME_TYPES.PING:
1678
1847
  // 处理PING帧
1679
1848
  this._handlePingFrame(frameHeader, frameData);
1680
1849
  break;
1850
+ case FRAME_TYPES.GOAWAY: {
1851
+ let info;
1852
+ try {
1853
+ const body = frameData.subarray(9);
1854
+ if (body.length >= 8) {
1855
+ const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
1856
+ const lastStreamId = view.getUint32(0, false) & 0x7fffffff;
1857
+ const errorCode = view.getUint32(4, false);
1858
+ info = { lastStreamId, errorCode };
1859
+ console.warn('[HTTP2] GOAWAY received', info);
1860
+ }
1861
+ else {
1862
+ console.warn('[HTTP2] GOAWAY received');
1863
+ info = {};
1864
+ }
1865
+ }
1866
+ catch { }
1867
+ try {
1868
+ this.onGoaway?.(info ?? {});
1869
+ }
1870
+ catch (err) {
1871
+ console.error('Error during GOAWAY callback:', err);
1872
+ }
1873
+ this.endFlag = true;
1874
+ try {
1875
+ this.onEnd?.();
1876
+ }
1877
+ catch (err) {
1878
+ console.error('Error during GOAWAY onEnd callback:', err);
1879
+ }
1880
+ break;
1881
+ }
1681
1882
  // case FRAME_TYPES.PUSH_PROMISE:
1682
1883
  // // 处理服务器推送承诺帧
1683
1884
  // this.handlePushPromiseFrame(frameHeader, frameData);