mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 06:44:35 +03:00
fix: split YouTube domains in youtube_via_relay (#275)
Pre-#275, youtube_via_relay=true routed every YouTube-related host through Apps Script — including ytimg.com (thumbnails) and any googlevideo.com chunk request the player issued. Two problems: 1. ytimg.com via Apps Script is wasted quota — image CDN, no Restricted Mode logic to bypass. 2. googlevideo.com wasn't even in SNI_REWRITE_SUFFIXES, so video chunks hit the relay regardless of the flag. A single chunk timeout aborted the whole video on Firefox; long videos risked the Apps Script 6-min execution cap mid-playback. Fix: split YouTube into "API/HTML hosts" (where Restricted Mode lives, gated by the flag) and "asset CDNs" (always direct). The new YOUTUBE_RELAY_HOSTS list is youtube.com, youtu.be, youtube-nocookie.com, youtubei.googleapis.com — those go through relay when the flag is on. ytimg.com, googlevideo.com (added), ggpht.com all stay on SNI rewrite. The matches_sni_rewrite logic was also restructured: the carve-out now runs FIRST before the SNI suffix match, so the broad googleapis.com entry can't override the narrower youtubei.googleapis.com decision. Reported with detailed analysis by @amirabbas117. Will ship in v1.7.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+114
-28
@@ -47,6 +47,18 @@ const SNI_REWRITE_SUFFIXES: &[&str] = &[
|
|||||||
"youtu.be",
|
"youtu.be",
|
||||||
"youtube-nocookie.com",
|
"youtube-nocookie.com",
|
||||||
"ytimg.com",
|
"ytimg.com",
|
||||||
|
// YouTube video CDN. Issue #275: a user reported that with
|
||||||
|
// `youtube_via_relay = true`, every video chunk was traversing the
|
||||||
|
// Apps Script relay and a single chunk timeout aborted playback —
|
||||||
|
// because `googlevideo.com` was not on this list, all chunks
|
||||||
|
// fell through to the relay path. Adding it here keeps video
|
||||||
|
// bytes on the direct GFE tunnel even when the relay flag is on
|
||||||
|
// (the YOUTUBE_RELAY_HOSTS carve-out below excludes only the API
|
||||||
|
// / HTML surfaces, not the CDN). The chunks are unauthenticated
|
||||||
|
// bytes anyway — there's no Restricted Mode logic on the CDN, so
|
||||||
|
// routing them through Apps Script gains nothing and costs the
|
||||||
|
// 6-minute Apps Script execution cap on long videos.
|
||||||
|
"googlevideo.com",
|
||||||
// Google Video Transport CDN — YouTube video chunks, Chrome
|
// Google Video Transport CDN — YouTube video chunks, Chrome
|
||||||
// auto-updates, Google Play Store downloads. The single biggest
|
// auto-updates, Google Play Store downloads. The single biggest
|
||||||
// gap vs the upstream Python port: without these in the list
|
// gap vs the upstream Python port: without these in the list
|
||||||
@@ -72,27 +84,62 @@ const SNI_REWRITE_SUFFIXES: &[&str] = &[
|
|||||||
"blogger.com",
|
"blogger.com",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// YouTube-family suffixes. Extracted so `youtube_via_relay` config can
|
/// YouTube hosts that should be routed through the Apps Script relay
|
||||||
/// pull them out of the SNI-rewrite dispatch at runtime.
|
/// when `youtube_via_relay` is enabled — the API + HTML surfaces where
|
||||||
const YOUTUBE_SNI_SUFFIXES: &[&str] = &[
|
/// Restricted Mode is actually enforced (via the SNI=www.google.com
|
||||||
|
/// edge looking at the request). Issue #102 / #275.
|
||||||
|
///
|
||||||
|
/// Deliberately narrower than the YouTube section of
|
||||||
|
/// `SNI_REWRITE_SUFFIXES`:
|
||||||
|
/// - `youtube.com` / `youtu.be` / `youtube-nocookie.com`: HTML pages
|
||||||
|
/// and player frames. These trigger Restricted Mode if served via
|
||||||
|
/// the SNI rewrite, so when the flag is on we relay them.
|
||||||
|
/// - `youtubei.googleapis.com`: the YouTube data API the player
|
||||||
|
/// queries for video metadata + manifest. Restricted Mode also
|
||||||
|
/// gates video availability here. Without this entry, the JSON
|
||||||
|
/// RPC layer would still hit the SNI-rewrite tunnel via the
|
||||||
|
/// broader `googleapis.com` suffix — the user-visible symptom of
|
||||||
|
/// that miss is "youtube_via_relay flips on but Restricted Mode
|
||||||
|
/// stays sticky on some videos."
|
||||||
|
///
|
||||||
|
/// **NOT** in this list (intentional, was a regression in #275):
|
||||||
|
/// - `ytimg.com`: thumbnails. No Restricted Mode logic on a static
|
||||||
|
/// image CDN; routing through Apps Script makes thumbnails slow
|
||||||
|
/// for zero gain.
|
||||||
|
/// - `googlevideo.com`: video chunk CDN. Routing through Apps Script
|
||||||
|
/// means every chunk eats Apps Script quota *and* risks the 6-min
|
||||||
|
/// execution cap aborting long videos mid-playback.
|
||||||
|
/// - `ggpht.com`: channel/profile images, same reasoning as ytimg.
|
||||||
|
const YOUTUBE_RELAY_HOSTS: &[&str] = &[
|
||||||
"youtube.com",
|
"youtube.com",
|
||||||
"youtu.be",
|
"youtu.be",
|
||||||
"youtube-nocookie.com",
|
"youtube-nocookie.com",
|
||||||
"ytimg.com",
|
"youtubei.googleapis.com",
|
||||||
];
|
];
|
||||||
|
|
||||||
fn matches_sni_rewrite(host: &str, youtube_via_relay: bool) -> bool {
|
fn matches_sni_rewrite(host: &str, youtube_via_relay: bool) -> bool {
|
||||||
let h = host.to_ascii_lowercase();
|
let h = host.to_ascii_lowercase();
|
||||||
let h = h.trim_end_matches('.');
|
let h = h.trim_end_matches('.');
|
||||||
|
|
||||||
|
// YouTube relay carve-out runs FIRST so it wins over the broad
|
||||||
|
// `googleapis.com` suffix that would otherwise pull
|
||||||
|
// `youtubei.googleapis.com` into the SNI-rewrite path. The earlier
|
||||||
|
// implementation iterated SNI_REWRITE_SUFFIXES with a filter, which
|
||||||
|
// works for sibling entries (e.g. `youtube.com` in both lists) but
|
||||||
|
// not for nested ones (`youtubei.googleapis.com` matches the broad
|
||||||
|
// `googleapis.com` even when its specific entry is filtered out).
|
||||||
|
// The short-circuit here is unconditional — we don't need to check
|
||||||
|
// SNI rewrite once we've decided this host goes to the relay.
|
||||||
|
if youtube_via_relay {
|
||||||
|
for s in YOUTUBE_RELAY_HOSTS {
|
||||||
|
if h == *s || h.ends_with(&format!(".{}", s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SNI_REWRITE_SUFFIXES
|
SNI_REWRITE_SUFFIXES
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| {
|
|
||||||
// If the user opted into youtube_via_relay, skip YouTube
|
|
||||||
// suffixes so they fall through to the Apps Script relay
|
|
||||||
// path. See config.rs `youtube_via_relay` docs for the
|
|
||||||
// trade-off. Issue #102.
|
|
||||||
!(youtube_via_relay && YOUTUBE_SNI_SUFFIXES.contains(s))
|
|
||||||
})
|
|
||||||
.any(|s| h == *s || h.ends_with(&format!(".{}", s)))
|
.any(|s| h == *s || h.ends_with(&format!(".{}", s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2587,36 +2634,75 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn youtube_via_relay_routes_youtube_through_relay_path() {
|
fn youtube_via_relay_routes_youtube_through_relay_path() {
|
||||||
// Issue #102. When youtube_via_relay=true, YouTube suffixes
|
// Issue #102 + #275. When youtube_via_relay=true:
|
||||||
// must NOT match the SNI-rewrite path, so traffic falls
|
// - YouTube API + HTML hosts (where Restricted Mode lives)
|
||||||
// through to Apps Script relay. Other Google suffixes are
|
// opt out of SNI rewrite so they go through the relay.
|
||||||
// unaffected.
|
// - YouTube image / video / channel-asset CDNs STAY on SNI
|
||||||
|
// rewrite — Restricted Mode isn't enforced on those, and
|
||||||
|
// routing video chunks through Apps Script burns quota
|
||||||
|
// and risks the 6-min execution cap. Pre-#275 ytimg.com
|
||||||
|
// was incorrectly carved out alongside the API surfaces.
|
||||||
|
// - Non-YouTube Google suffixes are unaffected by the flag.
|
||||||
let hosts = std::collections::HashMap::new();
|
let hosts = std::collections::HashMap::new();
|
||||||
|
|
||||||
// Default behaviour: everything in the pool rewrites.
|
// Default behaviour (flag off): everything in the SNI pool
|
||||||
assert!(should_use_sni_rewrite(
|
// rewrites including all YouTube assets.
|
||||||
&hosts,
|
assert!(should_use_sni_rewrite(&hosts, "www.youtube.com", 443, false));
|
||||||
"www.youtube.com",
|
|
||||||
443,
|
|
||||||
false
|
|
||||||
));
|
|
||||||
assert!(should_use_sni_rewrite(&hosts, "i.ytimg.com", 443, false));
|
assert!(should_use_sni_rewrite(&hosts, "i.ytimg.com", 443, false));
|
||||||
assert!(should_use_sni_rewrite(&hosts, "youtu.be", 443, false));
|
assert!(should_use_sni_rewrite(&hosts, "youtu.be", 443, false));
|
||||||
assert!(should_use_sni_rewrite(&hosts, "www.google.com", 443, false));
|
assert!(should_use_sni_rewrite(&hosts, "www.google.com", 443, false));
|
||||||
|
assert!(should_use_sni_rewrite(
|
||||||
|
&hosts,
|
||||||
|
"rr1---sn-abc.googlevideo.com",
|
||||||
|
443,
|
||||||
|
false
|
||||||
|
));
|
||||||
|
assert!(should_use_sni_rewrite(
|
||||||
|
&hosts,
|
||||||
|
"youtubei.googleapis.com",
|
||||||
|
443,
|
||||||
|
false
|
||||||
|
));
|
||||||
|
|
||||||
// With the toggle on: YouTube opts out, Google stays.
|
// Flag on: only the API + HTML hosts opt out.
|
||||||
|
assert!(!should_use_sni_rewrite(&hosts, "www.youtube.com", 443, true));
|
||||||
|
assert!(!should_use_sni_rewrite(&hosts, "youtu.be", 443, true));
|
||||||
assert!(!should_use_sni_rewrite(
|
assert!(!should_use_sni_rewrite(
|
||||||
&hosts,
|
&hosts,
|
||||||
"www.youtube.com",
|
"www.youtube-nocookie.com",
|
||||||
443,
|
443,
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
assert!(!should_use_sni_rewrite(&hosts, "i.ytimg.com", 443, true));
|
assert!(!should_use_sni_rewrite(
|
||||||
assert!(!should_use_sni_rewrite(&hosts, "youtu.be", 443, true));
|
&hosts,
|
||||||
assert!(should_use_sni_rewrite(&hosts, "www.google.com", 443, true));
|
"youtubei.googleapis.com",
|
||||||
|
443,
|
||||||
|
true
|
||||||
|
));
|
||||||
|
|
||||||
|
// Flag on: video / image / channel-asset CDNs STAY on SNI
|
||||||
|
// rewrite. The pre-#275 implementation broke playback by
|
||||||
|
// routing googlevideo.com through Apps Script (it wasn't even
|
||||||
|
// in the SNI list before #275, so it always went via relay)
|
||||||
|
// and routed ytimg.com through the relay too.
|
||||||
|
assert!(should_use_sni_rewrite(&hosts, "i.ytimg.com", 443, true));
|
||||||
assert!(should_use_sni_rewrite(
|
assert!(should_use_sni_rewrite(
|
||||||
&hosts,
|
&hosts,
|
||||||
"fonts.gstatic.com",
|
"rr1---sn-abc.googlevideo.com",
|
||||||
|
443,
|
||||||
|
true
|
||||||
|
));
|
||||||
|
assert!(should_use_sni_rewrite(&hosts, "yt3.ggpht.com", 443, true));
|
||||||
|
|
||||||
|
// Flag on: non-YouTube Google suffixes are unaffected. Note
|
||||||
|
// youtubei.googleapis.com (above) is the *carve-out* — the
|
||||||
|
// broader googleapis.com suffix is NOT carved out, so e.g.
|
||||||
|
// Drive / Calendar / etc. continue to SNI-rewrite.
|
||||||
|
assert!(should_use_sni_rewrite(&hosts, "www.google.com", 443, true));
|
||||||
|
assert!(should_use_sni_rewrite(&hosts, "fonts.gstatic.com", 443, true));
|
||||||
|
assert!(should_use_sni_rewrite(
|
||||||
|
&hosts,
|
||||||
|
"drive.googleapis.com",
|
||||||
443,
|
443,
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
|
|||||||
Reference in New Issue
Block a user