tunnelfetch 1.6.2 → 1.6.4

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/README.md CHANGED
@@ -434,10 +434,20 @@ pinned in `test/tls/fingerprint.test.js` and `test/http2/fingerprint.test.js`.
434
434
  **The default `SETTINGS` values are this package's, not curl's, and that is a deliberate trade.**
435
435
  `profiles.curl` carries curl 8.21.0's real `INITIAL_WINDOW_SIZE` of **64 KiB**, captured and pinned
436
436
  in `test/tls/_captured-h2.js`. The connection default without a profile is **10 MiB**, which is what
437
- curl 8.7.1 sent and what this package keeps for throughput: a 64 KiB stream window means far more
438
- `WINDOW_UPDATE` round trips on a large body, and the cost work in this README shows body transfer is
439
- where the money is. **That throughput difference has not been measured**, so if you are moving large
440
- bodies under `profiles.curl`, measure it before relying on it or set `http2Settings` yourself.
437
+ curl 8.7.1 sent and what this package keeps: a 64 KiB stream window means one `WINDOW_UPDATE` per
438
+ 32 KiB consumed instead of one per 5 MiB.
439
+
440
+ **Measured, on a 8.7 MB body over h2 against a real origin, both windows interleaved in one
441
+ isolate: the 64 KiB window costs about +1.4 ms of CPU per decompressed MB** — roughly **6–7%** on
442
+ top of the ~21.7 ms/MB this package spends moving a large body. That is the price of the accurate
443
+ fingerprint, and for most callers it is worth paying; if you are moving large bodies and do not need
444
+ to look like curl, set `http2Settings` yourself.
445
+
446
+ Two cautions on that number. The minimum-of-samples rule this document recommends elsewhere **fails
447
+ here**: with unequal sample counts and a lossy origin the minima moved between +2 ms and +13 ms
448
+ across sweeps, because the arm with more samples gets a lower minimum for free. The figure above is
449
+ p25 and median, which agreed with each other and with the arithmetic — 8.7 MB at a 32 KiB replenish
450
+ threshold is ~276 extra frames, and ~276 × ~47 µs is ~13 ms.
441
451
 
442
452
  Until 1.6.2 the profile carried curl **8.7.1's** window while presenting curl **8.21.0's**
443
453
  ClientHello: one named client, two source versions, and a split identity that only a capture could
package/README.zh-CN.md CHANGED
@@ -341,9 +341,16 @@ curl(8.7.1 / nghttp2),照线上抓包原样复刻。这么做是实证需
341
341
 
342
342
  **默认的 `SETTINGS` 取值是本包自己的,不是 curl 的,这是一个有意的取舍。** `profiles.curl` 带的是
343
343
  curl 8.21.0 实测的 `INITIAL_WINDOW_SIZE` = **64 KiB**,抓包固化在 `test/tls/_captured-h2.js`。而不加
344
- profile 时连接的默认值是 **10 MiB**——那是 curl 8.7.1 发的值,本包为吞吐保留它:64 KiB 的流窗口意味着大
345
- body 上要多得多的 `WINDOW_UPDATE` 往返,而本文档的成本部分已经说明,钱就花在 body 传输上。**这个吞吐差异
346
- 我没有实测过**,所以如果你在 `profiles.curl` 下搬大 body,先测再依赖,或者自己设 `http2Settings`。
344
+ profile 时连接的默认值是 **10 MiB**——那是 curl 8.7.1 发的值,本包保留它:64 KiB 的流窗口意味着每消费
345
+ 32 KiB 就发一次 `WINDOW_UPDATE`,而不是每 5 MiB 一次。
346
+
347
+ **实测:8.7 MB 的 body、走 h2、打真实源站、两档窗口在同一 isolate 里交错——64 KiB 窗口每解压 MB 多花约
348
+ 1.4 ms CPU**,相对本包搬大 body 的约 21.7 ms/MB 是**多 6–7%**。这就是准确指纹的价钱,对多数调用方值得付;
349
+ 如果你要搬大 body 又不需要长得像 curl,自己设 `http2Settings`。
350
+
351
+ 这个数字有两点要说明。本文档在别处推荐的"取最小值"规则**在这里失效了**:两档样本数不等、源站又有丢失,最小值
352
+ 在两轮扫描之间从 +2 ms 跳到 +13 ms——样本多的那一档白得一个更低的最小值。上面的数取自 p25 和中位数,两者一致,
353
+ 并且和算术对得上:8.7 MB 在 32 KiB 的补充阈值下约是 276 个额外帧,276 × 约 47 µs 约等于 13 ms。
347
354
 
348
355
  1.6.2 之前,这个 profile 带着 curl **8.7.1** 的窗口,却呈现 curl **8.21.0** 的 ClientHello:同一个具名
349
356
  客户端,两个来源版本——一个只有抓包才能发现的身份分裂,因为两半各自都真的属于某个 curl。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tunnelfetch",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "A fetch-shaped HTTP client that can route through HTTP CONNECT / HTTPS / SOCKS5 proxies on runtimes with only raw TCP, such as Cloudflare Workers. Implements TLS in userland because the runtime cannot verify a tunnelled peer.",
5
5
  "keywords": [
6
6
  "fetch",
@@ -201,7 +201,21 @@ export class Http2Connection {
201
201
 
202
202
  // Our advertised settings. The defaults ARE the fingerprint (see constants.js); overrides
203
203
  // exist for tests, and shifting them shifts what the server sees, so production leaves them.
204
- this._ourInitialWindow = opts.initialWindowSize ?? CLIENT_INITIAL_WINDOW_SIZE;
204
+ // The window we ADVERTISE and the window we ACCOUNT FOR must be the same number, and until
205
+ // 1.6.3 they were two independent settings that silently disagreed.
206
+ //
207
+ // `opts.settings` is the SETTINGS flight, which is how a fingerprint profile states its
208
+ // identity; SETTINGS_INITIAL_WINDOW_SIZE (id 4) inside it is what the peer is told. This field
209
+ // is what `_replenish` measures against — it only emits a WINDOW_UPDATE once the consumer has
210
+ // drained half of it. Take them from different places and a profile advertising 64 KiB against
211
+ // a default accounting of 10 MiB sets a replenish threshold of 5 MiB that a 64 KiB window can
212
+ // never reach: the peer sends one window's worth, stops, and no WINDOW_UPDATE ever comes. Every
213
+ // response larger than the advertised window hangs until the idle deadline fires.
214
+ //
215
+ // That is not hypothetical — `profiles.curl` gained curl 8.21.0's real 65536 in 1.6.2 and this
216
+ // is what it did. One source of truth: if the flight names id 4, that IS the window.
217
+ const advertised = (opts.settings ?? []).find(([id]) => id === 0x4)?.[1];
218
+ this._ourInitialWindow = opts.initialWindowSize ?? advertised ?? CLIENT_INITIAL_WINDOW_SIZE;
205
219
  this._ourConnWindow = opts.connectionWindow ?? CLIENT_CONNECTION_WINDOW;
206
220
  this._ourMaxConcurrent = opts.maxConcurrentStreams ?? CLIENT_MAX_CONCURRENT_STREAMS;
207
221
  this._ourHeaderTableSize = opts.maxHeaderTableSize ?? DEFAULT_HEADER_TABLE_SIZE;