Skip to content

Commit aa16abc

Browse files
dazzling-no-moreweb-flowclaude
authored
fix(relay): stream range-parallel downloads larger than Apps Script's 50 MiB cap (#1042, #1085)
Fixes #1042 — range-capable downloads larger than ~50 MiB through the Apps Script relay returned `504 Relay timeout — Apps Script unresponsive` instead of the file. The 104 MiB v2rayN DMG in the reported logs was the canonical repro (also matches @Paymanonline's report in #1077, closed prior as "architectural limit, use mirrors" — this PR makes it actually work via streaming). ## Root cause `relay_parallel_range` capped the stitched response at 64 MiB and fell back to a single `relay()` for anything larger. Single-GET routes through Apps Script's ~50 MiB response ceiling, so Apps Script killed the script mid-execution and we hung for the full 25s relay timeout before returning 504. ## Fix Convert `relay_parallel_range` into a writer-based API that streams large files chunk-by-chunk to the client socket. Each chunk is still one ≤256 KiB Apps Script call (well under the 50 MiB cap); only the host-side buffering changes. Backward-compatible `Vec<u8>` wrapper preserves the pre-1.9.23 API surface for external library consumers. Three-way dispatch via `RangeDispatch { Buffered, Stream, FallbackSingleGet, RejectTooLarge }` and the pure `dispatch_range_response(total, streaming_allowed)` predicate: - **`Buffered`** — `total ≤ APPS_SCRIPT_BODY_MAX_BYTES` (40 MiB) on either surface. Existing stitch + single-GET fallback path; fully recovers on chunk failure. - **`Stream`** — writer API above 40 MiB. Streams; chunk failure flushes the committed prefix and returns `Err` so the `Content-Length` mismatch tells download clients to resume via `Range`. - **`FallbackSingleGet`** — wrapper above 64 MiB. Matches pre-1.9.23 cliff for external library consumers stuck on the old API. - **`RejectTooLarge`** — writer API above 16 GiB. Refuses with 502; bounds worst-case Apps Script quota drain from a hostile origin advertising an absurd `Content-Range` total. ## Memory bounds Lazy `plan_remaining_ranges` (via `std::iter::from_fn` + `saturating_*`): range planning is `O(1)` memory regardless of advertised total. Even a `u64::MAX` total no longer drives a ~6 GB `Vec<(u64, u64)>` allocation. ## CORS interaction MITM HTTPS and plain-HTTP call sites updated to use `relay_parallel_range_to` with a CORS-aware `transform_head` closure. Extracted `inject_cors_into_head` (head-only variant of `inject_cors_response_headers`) so the streaming path can rewrite ACL headers before the body has been assembled. ## Verified locally on top of v1.9.22 - `cargo test --lib --release`: 227/227 ✅ (was 209; +18 new — 15 stated in PR body + 3 incidental from the helper extractions) - `cargo build --release --features ui --bin mhrv-rs-ui`: clean ✅ Manual repro of the 104 MiB v2rayN DMG download is unchecked in the PR test plan — the unit tests cover the dispatch + streaming + flush contracts thoroughly. The architectural reasoning is sound and the new test count (+18) is concrete. Reviewed via Anthropic Claude. Co-Authored-By: dazzling-no-more <noreply@github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f4f23c3 commit aa16abc

2 files changed

Lines changed: 1537 additions & 113 deletions

File tree

0 commit comments

Comments
 (0)