Restore Connect thread-loading speedup, without the gzip corruption - #915
Merged
SawyerHood merged 2 commits intoJul 30, 2026
Merged
Conversation
This reverts commit 17d652c.
#909 lets the tunnel client forward the origin's compressed bytes verbatim. The gate then rebuilt those bytes into new Response objects under workerd's default encodeBody "automatic", which means "this body is identity and I own the encoding" — workerd dropped content-encoding and shipped gzip bytes labelled text/html, so browsers rendered raw gzip. Two sites relayed pre-encoded bodies that way: - TunnelDO's resp-head reconstruction (every tunnelled response), and - the edge cache's hit path, which re-serves the still-compressed body the Cache API stored. Both now build with encodeBody "manual". The cache miss path is deliberately left alone: workerd content-decodes a subrequest body as the gate reads it, so there the bytes really are identity and marking them pre-encoded would advertise a gzip body that isn't gzipped. The regression test drives the real TunnelDO and the real cache layer inside workerd via miniflare, with a fake tunnel client relaying gzip frames — Node's Response ignores encodeBody, so a Node-only test would pass with or without the fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SawyerHood
deleted the
bb/restore-connect-performance-fix-safely-thr_meeeihbta8
branch
July 30, 2026 23:17
amadad
pushed a commit
to amadad/bb
that referenced
this pull request
Aug 14, 2026
…et-bb#915) Restores get-bb#909 (reverted in 17d652c) and fixes the response corruption that forced the revert. ## The corruption get-bb#909 lets the tunnel client forward the origin's compressed bytes verbatim. The gate then rebuilt those bytes into new `Response` objects under workerd's default `encodeBody: "automatic"`, which means *"this body is identity and I own the encoding"* — so workerd dropped `content-encoding` and shipped gzip bytes labelled `text/html`. Browsers rendered raw gzip. Two sites relayed pre-encoded bodies that way, and the correct fix is **asymmetric**: | Site | Body it wraps | Encoding | |---|---|---| | `tunnel-do.ts` resp-head | raw origin bytes from tunnel frames | `manual` — was corrupt | | `cache.ts` **hit** | Cache API stores bytes still compressed | `manual` — was corrupt | | `cache.ts` **miss** | workerd content-decodes a subrequest body as the gate reads it | **automatic** — already correct | Marking the miss path `manual` (the symmetric-looking fix) actively breaks it: it advertises gzip over plaintext and clients fail with `Z_DATA_ERROR`. The regression test catches that. ## Regression test `apps/connect/src/response-encoding.test.ts` drives the **real `TunnelDO` and real `serveWithCache`** inside workerd (miniflare), with a fake tunnel client relaying gzip frames over a real WebSocket. Node's `Response` ignores `encodeBody`, so a Node-only test would pass with or without this fix. Mutation-checked: reverting the `tunnel-do.ts` call site fails 4 tests, reverting the `cache.ts` hit path fails 1. Two control routes pin workerd's default behaviour so the tests document the bug rather than merely guarding it. ## Verification on real Cloudflare Deployed to staging (`bb-connect-staging`, version `0946eaab`, `sawyer.vibecodethis.site`). Because the tunnelled path needs a client + owner session, the end-to-end check ran against a throwaway workers.dev probe running this same `TunnelDO` + `cache.ts`, driven by the **real `TunnelSession` client** in front of the real built bb app (383 precompressed `.gz` assets, served as `apps/server` does). Probe deleted afterwards. - `index.html`: gzip-only client got 1509 raw bytes (`1f8b0800`) gunzipping to exactly 4255 bytes of real HTML; browser-style clients got it transcoded to zstd by the edge — which the edge can only do if it correctly understands the body as encoded. - 342 KB app bundle, miss → hit → hit: `x-bb-cache: miss`/`hit` plus `cf-cache-status: HIT`, every response gunzipping to sha256 `18cb8f3a…`, byte-identical to the origin file. - Legacy control at the edge reproduces the production symptom: `content-encoding` dropped, edge zstd-wraps the raw gzip, browser renders `^_M-^K^H…` as `text/html`. - `Accept-Encoding: identity`: correct 4255 plain bytes, no encoding header. - Identity bodies measured through both paths are byte-identical (19984 → 4882 gzip, zstd for browser-style), so `manual` does **not** suppress edge compression for uncompressed origins — port shares and pre-get-bb#909 clients keep it. ## Compatibility Old client → new gate is safe: a pre-get-bb#909 client strips `accept-encoding` and drops `content-encoding` from resp-head, so its bodies are identity with no encoding header and `manual` asserts nothing false. The broken direction is **new client → old gate**, which is unchanged by this PR: prod's gate must ship this before any released client carries the reapplied get-bb#909. Merging this triggers `deploy-connect.yml`, which deploys the prod gate — the correct order. `HOST_DAEMON_PROTOCOL_VERSION` not bumped (stays 68): worker-only change, no daemon command / session payload / WS message altered, tunnel frame format unchanged (`PROTOCOL_VERSION` stays 1). ## Tests - `@bb/connect` 88 ✅ + typecheck - `@bb/tunnel-client` 6 ✅ + typecheck - `bb-plugin-connect` 65 ✅ - `@bb/app` 28 focused (the four suites from get-bb#909) ✅ + typecheck 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Restores #909 (reverted in 17d652c) and fixes the response corruption that forced the revert.
The corruption
#909 lets the tunnel client forward the origin's compressed bytes verbatim. The gate then rebuilt those bytes into new
Responseobjects under workerd's defaultencodeBody: "automatic", which means "this body is identity and I own the encoding" — so workerd droppedcontent-encodingand shipped gzip bytes labelledtext/html. Browsers rendered raw gzip.Two sites relayed pre-encoded bodies that way, and the correct fix is asymmetric:
tunnel-do.tsresp-headmanual— was corruptcache.tshitmanual— was corruptcache.tsmissMarking the miss path
manual(the symmetric-looking fix) actively breaks it: it advertises gzip over plaintext and clients fail withZ_DATA_ERROR. The regression test catches that.Regression test
apps/connect/src/response-encoding.test.tsdrives the realTunnelDOand realserveWithCacheinside workerd (miniflare), with a fake tunnel client relaying gzip frames over a real WebSocket. Node'sResponseignoresencodeBody, so a Node-only test would pass with or without this fix.Mutation-checked: reverting the
tunnel-do.tscall site fails 4 tests, reverting thecache.tshit path fails 1. Two control routes pin workerd's default behaviour so the tests document the bug rather than merely guarding it.Verification on real Cloudflare
Deployed to staging (
bb-connect-staging, version0946eaab,sawyer.vibecodethis.site). Because the tunnelled path needs a client + owner session, the end-to-end check ran against a throwaway workers.dev probe running this sameTunnelDO+cache.ts, driven by the realTunnelSessionclient in front of the real built bb app (383 precompressed.gzassets, served asapps/serverdoes). Probe deleted afterwards.index.html: gzip-only client got 1509 raw bytes (1f8b0800) gunzipping to exactly 4255 bytes of real HTML; browser-style clients got it transcoded to zstd by the edge — which the edge can only do if it correctly understands the body as encoded.x-bb-cache: miss/hitpluscf-cache-status: HIT, every response gunzipping to sha25618cb8f3a…, byte-identical to the origin file.content-encodingdropped, edge zstd-wraps the raw gzip, browser renders^_M-^K^H…astext/html.Accept-Encoding: identity: correct 4255 plain bytes, no encoding header.manualdoes not suppress edge compression for uncompressed origins — port shares and pre-Speed up thread loading over Connect #909 clients keep it.Compatibility
Old client → new gate is safe: a pre-#909 client strips
accept-encodingand dropscontent-encodingfrom resp-head, so its bodies are identity with no encoding header andmanualasserts nothing false.The broken direction is new client → old gate, which is unchanged by this PR: prod's gate must ship this before any released client carries the reapplied #909. Merging this triggers
deploy-connect.yml, which deploys the prod gate — the correct order.HOST_DAEMON_PROTOCOL_VERSIONnot bumped (stays 68): worker-only change, no daemon command / session payload / WS message altered, tunnel frame format unchanged (PROTOCOL_VERSIONstays 1).Tests
@bb/connect88 ✅ + typecheck@bb/tunnel-client6 ✅ + typecheckbb-plugin-connect65 ✅@bb/app28 focused (the four suites from Speed up thread loading over Connect #909) ✅ + typecheck🤖 Generated with Claude Code