""" Central location for tunable constants used across the project. Values here are chosen for safe defaults; individual entries may be overridden from `config.json` where noted. """ from __future__ import annotations # ── Version ─────────────────────────────────────────────────────────────── __version__ = "1.1.0" # ── Size caps ───────────────────────────────────────────────────────────── MAX_REQUEST_BODY_BYTES = 100 * 1024 * 1024 # 100 MB — inbound browser body MAX_RESPONSE_BODY_BYTES = 200 * 1024 * 1024 # 200 MB — chunked response cap MAX_HEADER_BYTES = 64 * 1024 # 64 KB # ── Timeouts (seconds) ──────────────────────────────────────────────────── CLIENT_IDLE_TIMEOUT = 120 RELAY_TIMEOUT = 25 TLS_CONNECT_TIMEOUT = 15 TCP_CONNECT_TIMEOUT = 10 # ── Google IP Scanner settings ────────────────────────────────────────────── GOOGLE_SCANNER_TIMEOUT = 4 # Timeout per IP probe (seconds) GOOGLE_SCANNER_CONCURRENCY = 8 # Parallel probes # Candidate Google frontend IPs for scanning (multiple ASNs and regions) CANDIDATE_IPS: tuple[str, ...] = ( "216.239.32.120", "216.239.34.120", "216.239.36.120", "216.239.38.120", "142.250.80.142", "142.250.80.138", "142.250.179.110", "142.250.185.110", "142.250.184.206", "142.250.190.238", "142.250.191.78", "172.217.1.206", "172.217.14.206", "172.217.16.142", "172.217.22.174", "172.217.164.110", "172.217.168.206", "172.217.169.206", "34.107.221.82", "142.251.32.110", "142.251.33.110", "142.251.46.206", "142.251.46.238", "142.250.80.170", "142.250.72.206", "142.250.64.206", "142.250.72.110", ) # ── Response cache ──────────────────────────────────────────────────────── CACHE_MAX_MB = 50 CACHE_TTL_STATIC_LONG = 3600 # images / fonts CACHE_TTL_STATIC_MED = 1800 # css / js CACHE_TTL_MAX = 86400 # hard cap on any explicit max-age # ── Connection pool (HTTP/1.1 to Apps Script) ───────────────────────────── POOL_MAX = 50 POOL_MIN_IDLE = 15 CONN_TTL = 45.0 SEMAPHORE_MAX = 50 WARM_POOL_COUNT = 30 # ── Batch windows ───────────────────────────────────────────────────────── BATCH_WINDOW_MICRO = 0.015 # 15 ms: captures short asset bursts BATCH_WINDOW_MACRO = 0.120 # 120 ms: balances batching and latency BATCH_MAX = 64 # enough headroom for browser fan-out waves # ── Fan-out relay (parallel Apps Script instances) ──────────────────────── # How long to ignore a script ID after it fails or is unreasonably slow. SCRIPT_BLACKLIST_TTL = 600.0 # 10 minutes # ── SNI rotation pool ───────────────────────────────────────────────────── # Google-owned SNIs that share the same edge IPs as www.google.com. # When `front_domain` is a Google property, we rotate through this pool on # each new outbound TLS handshake so DPI systems don't see a constant # "always www.google.com" pattern from the client. # Looks like that only mail and google.com not have a shaped DPI, the rest are 16kb shape blocked. # from my own benchmarks . Google and mail have 658 kb ps but the rest have 16 kb ps. FRONT_SNI_POOL_GOOGLE: tuple[str, ...] = ( "mail.google.com", "accounts.google.com", "www.google.com", # "drive.google.com", # "docs.google.com", # "calendar.google.com", # "maps.google.com", # "chat.google.com", # "translate.google.com", # "play.google.com", # "lens.google.com", # "scholar.google.com", # "chromewebstore.google.com", ) # ── Per-host stats ──────────────────────────────────────────────────────── STATS_LOG_INTERVAL = 300.0 # seconds — how often to log per-host totals STATS_LOG_TOP_N = 10 # how many hosts to include in the log # ── Direct Google tunnel allow / exclude ────────────────────────────────── # Google web-apps whose real origin must go through the Apps Script relay # because direct SNI tunneling to them does not work reliably behind DPI. GOOGLE_DIRECT_EXACT_EXCLUDE = frozenset({ "gemini.google.com", "aistudio.google.com", "notebooklm.google.com", "labs.google.com", "meet.google.com", "accounts.google.com", "ogs.google.com", "mail.google.com", "calendar.google.com", "drive.google.com", "docs.google.com", "chat.google.com", "photos.google.com", "maps.google.com", "myaccount.google.com", "contacts.google.com", "classroom.google.com", "keep.google.com", "play.google.com", "translate.google.com", "assistant.google.com", "lens.google.com", }) GOOGLE_DIRECT_SUFFIX_EXCLUDE: tuple[str, ...] = ( ".meet.google.com", ) # Hosts that are known to work better when tunneled directly. GOOGLE_DIRECT_ALLOW_EXACT = frozenset({ "www.google.com", "google.com", "safebrowsing.google.com", }) GOOGLE_DIRECT_ALLOW_SUFFIXES: tuple[str, ...] = () # ── Google-owned domain detection ───────────────────────────────────────── GOOGLE_OWNED_SUFFIXES: tuple[str, ...] = ( ".google.com", ".google.co", ".googleapis.com", ".gstatic.com", ".googleusercontent.com", ) GOOGLE_OWNED_EXACT = frozenset({ "google.com", "gstatic.com", "googleapis.com", }) # ── SNI-rewrite suffixes ────────────────────────────────────────────────── # Google-owned properties whose real SNI is DPI-blocked but are served by # the same edge IP as `front_domain`. Routed through the configured # `google_ip` with SNI rewritten. SNI_REWRITE_SUFFIXES: tuple[str, ...] = ( "youtube.com", "youtu.be", "youtube-nocookie.com", "ytimg.com", "ggpht.com", "gvt1.com", "gvt2.com", "doubleclick.net", "googlesyndication.com", "googleadservices.com", "google-analytics.com", "googletagmanager.com", "googletagservices.com", "fonts.googleapis.com", "script.google.com", ) # ── Response-logging trace hosts ────────────────────────────────────────── TRACE_HOST_SUFFIXES: tuple[str, ...] = ( "chatgpt.com", "openai.com", "gemini.google.com", "google.com", "cloudflare.com", "challenges.cloudflare.com", "turnstile", ) # ── File-extension heuristics ───────────────────────────────────────────── STATIC_EXTS: tuple[str, ...] = ( ".css", ".js", ".mjs", ".woff", ".woff2", ".ttf", ".eot", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ico", ".mp3", ".mp4", ".webm", ".wasm", ".avif", ".json", ".map", ) LARGE_FILE_EXTS = frozenset({ ".bin", ".zip", ".tar", ".gz", ".bz2", ".xz", ".7z", ".rar", ".exe", ".msi", ".dmg", ".deb", ".rpm", ".apk", ".iso", ".img", ".mp4", ".mkv", ".avi", ".mov", ".webm", ".mp3", ".flac", ".wav", ".aac", ".pdf", ".doc", ".docx", ".ppt", ".pptx", ".wasm", }) # ── Stateful-request hints ──────────────────────────────────────────────── STATEFUL_HEADER_NAMES: tuple[str, ...] = ( "cookie", "authorization", "proxy-authorization", "origin", "referer", "if-none-match", "if-modified-since", "cache-control", "pragma", ) UNCACHEABLE_HEADER_NAMES: tuple[str, ...] = ( "cookie", "authorization", "proxy-authorization", "range", "if-none-match", "if-modified-since", "cache-control", "pragma", )