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

4.7 KiB
Raw Blame History

Fix: stream range-parallel downloads larger than Apps Script's 50 MiB cap (#1042 + PR #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 + PR #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:

  • Bufferedtotal ≤ 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.