Files
MasterHttpRelayVPN-RUST/docs/changelog/v1.9.23.md
T
therealaleph ca24ebdc1e chore(release): v1.9.23 — stream large range-parallel downloads (#1042, #1085)
Bumps Cargo.toml v1.9.22 → v1.9.23. Ships @dazzling-no-more's PR #1085
which converts relay_parallel_range into a writer-based API that streams
files >50 MiB chunk-by-chunk instead of trying to buffer the whole
response and hitting Apps Script's body ceiling. Four-way dispatch
(Buffered / Stream / FallbackSingleGet / RejectTooLarge) with O(1)
memory range planning + a 16 GiB hostile-origin guard. 209 → 227 lib
tests (+18 new). Unblocks GitHub releases / large CDN binaries through
apps_script mode without needing Full mode or external mirrors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:55:10 +03:00

26 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
**Fix: stream range-parallel downloads larger than Apps Script's 50 MiB cap** ([#1042](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/1042) + [PR #1085](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/1085) by @dazzling-no-more). دانلودهای range-capable بزرگ‌تر از ~۵۰ MiB از طریق Apps Script relay با \`504 Relay timeout — Apps Script unresponsive\` fail می‌شد. v2rayN DMG 104 MiB در reported logs canonical repro بود. روت کاز: \`relay_parallel_range\` در 64 MiB ceiling داشت و برای بالاتر به single \`relay()\` fallback می‌کرد که از 50 MiB Apps Script ceiling عبور می‌کرد، Apps Script script رو mid-execution می‌کشت، و 25s timeout. Fix: \`relay_parallel_range\` به writer-based API تبدیل شد که large files رو chunk-by-chunk (هر chunk ≤256 KiB، خوب زیر 50 MiB cap) به client socket stream می‌کنه. ۴-way dispatch: Buffered (≤40 MiB)، Stream (40 MiB-16 GiB)، FallbackSingleGet (wrapper 40-64 MiB)، RejectTooLarge (>16 GiB، quota guard). Lazy range planning با \`saturating_*\` — O(1) memory حتی برای \`u64::MAX\` total (قبل ~6 GB Vec allocation می‌داد). MITM HTTPS + plain HTTP call sites + CORS-aware \`transform_head\` همه updated. ۲۰۹ → **۲۲۷ lib test** (+۱۸ new: dispatch enum، lazy planning، head assembly، head transform، streaming writer، flush behavior، CORS-into-streaming integration).
---
**Fix: stream range-parallel downloads larger than Apps Script's 50 MiB cap** ([#1042](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/1042) + [PR #1085](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/1085) by @dazzling-no-more).
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 fixes @Paymanonline's #1077 report).
**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-v1.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. Falls back to `self.relay()`, matching the pre-v1.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. New `inject_cors_into_head` (head-only variant of `inject_cors_response_headers`) lets the streaming path rewrite ACL headers before the body has been assembled.
209 → **227 lib tests** (+18 new: `RangeDispatch` enum coverage, lazy range planning under `u64::MAX`, `assemble_200_head` correctness, `transform_head` closure invocation, streaming writer chunk-by-chunk semantics, head-then-flush-before-body ordering, CORS-into-streaming cross-module integration).
**User impact**: GitHub release downloads, large CDN binaries, ROM-hack distributions, anything in the 50 MiB 16 GiB range now downloads successfully through apps_script mode. Previously these required Full mode, an Iran-mirror proxy (#1077), or a friend-with-VPS workaround.