Files
MasterHttpRelayVPN-RUST/assets/apps_script
dazzling-no-more f4f23c3173 fix(code.gs): wrap _doSingle normal-relay fetch in try/catch (#1047, #1049)
Fixes #1047. `_doSingle`'s normal-relay path (cache disabled or cache miss on
non-cachable request) ran `UrlFetchApp.fetch` → `getContent` → `base64Encode`
with no error wrapper. Any throw — most commonly when the response body
approaches Apps Script's ~50 MB ceiling and `base64Encode` blows the V8 heap,
also URL-too-long / payload-too-large / quota exhaustion / 6-minute execution
timeout — propagated unhandled, and Apps Script served its default
`<title>Web App</title>` HTML error page in place of the JSON envelope.

The Rust client (`parse_relay_json` in `domain_fronter.rs`) then failed to
find JSON and surfaced the cryptic `bad response: no json in: <!DOCTYPE html>...`
with no signal as to the actual cause.

The reporter's symptom — a single failing host (`shc-dist.lostsig.co`,
sonichacking.org) serving large ROM-hack binaries — matches this exactly.
Every other download worked because they were all under the body-size
ceiling.

## Fix

Wrap the normal-relay block in `_doSingle` with
`try { ... } catch (err) { return _json({ e: "fetch failed: " + String(err) }); }`.
Mirrors the per-item try/catch already present in `_doBatch`. Turns the
silent HTML crash into a structured `FronterError::Relay("fetch failed: …")`
on the client side that pinpoints the real underlying error.

Cache path intentionally untouched:
- `_fetchAndCache` already wraps its own fetch in try/catch and returns
  `null` on any failure (so `_doSingle` falls through cleanly to the
  normal relay).
- The cached-read path is bounded to ≤ `CACHE_MAX_BODY_BYTES` (35 KB)
  so it cannot trip the size limits that caused this bug.

## Verified locally on top of v1.9.22

- `node --check assets/apps_script/Code.gs`: clean 
- `cargo test --lib --release`: 209/209  (sanity — no Rust change)

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>
2026-05-12 01:55:10 +03:00
..

Apps Script source

Three deploy-ready Apps Script files live here. They all speak the same {k, m, u, h, b, ct, r} wire protocol with mhrv-rs, so the client just points its script_id at whichever deployment you want — no mode change required.

Variants and origins

  • Code.gs — standard relay. Verbatim mirror of upstream. Apps Script does the outbound fetch itself. This is the default choice for most users.

  • CodeFull.gs — superset of Code.gs that additionally proxies raw-TCP / UDP via tunnel-node (used by mode: "full"). Maintained in this repo — written for this Rust port and not present upstream. Deploy this if you want full-tunnel mode; details in the file's header comment.

  • Code.cfw.gs — Apps Script becomes a thin auth+forward layer; the actual outbound fetch happens on a Cloudflare Worker you also deploy (assets/cloudflare/). Derivative work — not unmodified upstream. The pattern of forwarding through a Cloudflare Worker came from denuitt1/mhr-cfw; this file inherits hardening from Code.gs (decoy-on-bad-auth, fail-closed sentinels) and adds chunked batch forwarding (Promise.all on the Worker side, ceil(N/40) GAS calls per batch) that the upstream mhr-cfw does not have. Faster per-call latency, worse YouTube long-form, no fix for Cloudflare anti-bot. Read assets/cloudflare/README.md before choosing this one.

What you must edit before deploying

For every variant: change AUTH_KEY from its placeholder to a strong secret, and use that same string in your mhrv-rs config's auth_key. Code.cfw.gs additionally requires setting WORKER_URL to your deployed Cloudflare Worker URL; CodeFull.gs additionally requires TUNNEL_SERVER_URL and TUNNEL_AUTH_KEY for the tunnel-node leg.