mirror of
https://github.com/masterking32/MasterHttpRelayVPN.git
synced 2026-05-17 21:24:37 +03:00
refactor: update configuration keys and improve documentation for HTTP proxy settings
This commit is contained in:
@@ -155,14 +155,12 @@ It'll prompt for your Deployment ID, generate a random `auth_key`, and write
|
||||
2. Open `config.json` in any text editor and fill in your values:
|
||||
```json
|
||||
{
|
||||
"mode": "apps_script",
|
||||
"google_ip": "216.239.38.120",
|
||||
"front_domain": "www.google.com",
|
||||
"script_id": "PASTE_YOUR_DEPLOYMENT_ID_HERE",
|
||||
"auth_key": "your-secret-password-here",
|
||||
"listen_host": "127.0.0.1",
|
||||
"listen_port": 8085,
|
||||
"socks5_enabled": true,
|
||||
"http_port": 8085,
|
||||
"socks5_port": 1080,
|
||||
"log_level": "INFO",
|
||||
"verify_ssl": true
|
||||
@@ -301,7 +299,7 @@ By default, the proxy only listens on `127.0.0.1` (localhost), meaning only your
|
||||
{
|
||||
"lan_sharing": true,
|
||||
"listen_host": "0.0.0.0",
|
||||
"listen_port": 8085
|
||||
"http_port": 8085
|
||||
}
|
||||
```
|
||||
|
||||
@@ -326,7 +324,7 @@ This project is centered on the **Apps Script** relay (free, no VPS needed). For
|
||||
| `auth_key` | Password shared between your computer and the relay |
|
||||
| `script_id` | Your Google Apps Script Deployment ID |
|
||||
| `listen_host` | Where to listen (`127.0.0.1` = only this computer, `0.0.0.0` = all interfaces for LAN sharing) |
|
||||
| `listen_port` | Which port to listen on (default: `8085`) |
|
||||
| `http_port` | Which HTTP proxy port to listen on (default: `8085`) |
|
||||
| `lan_sharing` | Enable LAN sharing to allow other devices on your network to use the proxy (`false` by default) |
|
||||
| `log_level` | How much detail to show: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
||||
|
||||
@@ -340,21 +338,46 @@ This project is centered on the **Apps Script** relay (free, no VPS needed). For
|
||||
| `relay_timeout` | `25` | Total timeout for one relayed request before it fails |
|
||||
| `tls_connect_timeout` | `15` | Timeout for the proxy's TLS connection to the fronted Google/CDN endpoint |
|
||||
| `tcp_connect_timeout` | `10` | Timeout for direct TCP tunnels and outbound SNI-rewrite connects |
|
||||
| `max_response_body_bytes` | `209715200` | Hard cap for a single relay response body after buffering/decoding |
|
||||
| `script_ids` | — | Multiple Script IDs for load balancing (array) |
|
||||
| `chunked_download_extensions` | see [config.example.json](config.example.json) | File extensions that should use parallel range downloading. Supports `".*"` to probe all GET downloads. |
|
||||
| `chunked_download_min_size` | `5242880` | Minimum total file size (5 MB) before range-parallel download stays enabled |
|
||||
| `chunked_download_chunk_size` | `524288` | Per-range chunk size used by parallel downloads |
|
||||
| `chunked_download_max_parallel` | `8` | Maximum simultaneous range requests for one download |
|
||||
| `chunked_download_max_chunks` | `256` | Soft upper bound for total chunk requests; chunk size is raised automatically for very large files |
|
||||
| `hosts` | `{}` | Manual DNS override map (`hostname` or `.suffix` -> IP). Example: `{ "example.org": "93.184.216.34", ".internal.lan": "192.168.1.10" }`. |
|
||||
| `block_hosts` | `[]` | Hosts that must never be tunneled (return HTTP 403). Supports exact names (`ads.example.com`) or leading-dot suffixes (`.doubleclick.net`). |
|
||||
| `direct_hosts` | `[]` | Hosts that must always go direct (no MITM and no relay/domain-fronting). Supports exact names and leading-dot suffixes. |
|
||||
| `bypass_hosts` | `["localhost", ".local", ".lan", ".home.arpa"]` | Hosts that go direct (no MITM, no relay). Useful for LAN resources or sites that break under MITM. |
|
||||
| `direct_google_exclude` | see [config.example.json](config.example.json) | Google apps that must use the MITM relay path instead of the fast direct tunnel. |
|
||||
| `hosts` | `{}` | Manual DNS override: map a hostname to a specific IP. |
|
||||
| `youtube_via_relay` | `false` | Route YouTube (`youtube.com`, `youtu.be`, `youtube-nocookie.com`) through the Apps Script relay instead of the SNI-rewrite path. The SNI-rewrite path uses Google's frontend IP which enforces SafeSearch and can cause **"Video Unavailable"** errors. Setting this to `true` fixes playback at the cost of using more Apps Script executions and slightly higher latency. |
|
||||
| `exit_node.provider` | `cloudflare` | Selected exit-node backend: `cloudflare`, `deno`, `vps`, or `custom`. |
|
||||
| `exit_node.url` | `""` | Beginner-friendly single URL for the selected provider. |
|
||||
|
||||
Practical host-policy example:
|
||||
|
||||
```json
|
||||
{
|
||||
"block_hosts": [
|
||||
"ads.example.com",
|
||||
".doubleclick.net"
|
||||
],
|
||||
"direct_hosts": [
|
||||
"chat.openai.com",
|
||||
".openai.com"
|
||||
],
|
||||
"hosts": {
|
||||
"example.org": "93.184.216.34",
|
||||
".internal.lan": "192.168.1.10"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `block_hosts`: deny requests entirely (`403`) for exact names or full suffix trees.
|
||||
- `direct_hosts`: force plain direct tunnel only (no MITM, no relay fronting).
|
||||
- `hosts`: force DNS mapping before any real lookup (useful for testing/split-DNS workarounds).
|
||||
|
||||
Note: the relay response body cap is now a code constant (`MAX_RESPONSE_BODY_BYTES`) in [src/core/constants.py](src/core/constants.py), not a user config key.
|
||||
|
||||
### Optional Dependencies
|
||||
|
||||
Install everything from [`requirements.txt`](requirements.txt). All listed packages are optional — the proxy runs with no third-party dependencies in basic modes, but without them you lose features:
|
||||
@@ -395,7 +418,6 @@ If you change `Code.gs`, you must **create a new deployment** in Google Apps Scr
|
||||
python3 main.py # Normal start
|
||||
python3 main.py -p 9090 # Use HTTP port 9090 instead
|
||||
python3 main.py --socks5-port 1081 # Use SOCKS5 port 1081
|
||||
python3 main.py --disable-socks5 # Disable SOCKS5 listener
|
||||
python3 main.py --log-level DEBUG # Show detailed logs
|
||||
python3 main.py -c /path/to/config.json # Use a different config file
|
||||
python3 main.py --install-cert # Install MITM CA certificate and exit
|
||||
|
||||
+30
-7
@@ -116,14 +116,12 @@ cp config.example.json config.json
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "apps_script",
|
||||
"google_ip": "216.239.38.120",
|
||||
"front_domain": "www.google.com",
|
||||
"script_id": "PASTE_YOUR_DEPLOYMENT_ID_HERE",
|
||||
"auth_key": "your-secret-password-here",
|
||||
"listen_host": "127.0.0.1",
|
||||
"listen_port": 8085,
|
||||
"socks5_enabled": true,
|
||||
"http_port": 8085,
|
||||
"socks5_port": 1080,
|
||||
"log_level": "INFO",
|
||||
"verify_ssl": true
|
||||
@@ -254,7 +252,7 @@ json
|
||||
{
|
||||
"lan_sharing": true,
|
||||
"listen_host": "0.0.0.0",
|
||||
"listen_port": 8085
|
||||
"http_port": 8085
|
||||
}
|
||||
|
||||
**هشدار امنیتی:** وقتی اشتراکگذاری در شبکه محلی فعال باشد، هر کسی در شبکه محلی شما میتواند از پروکسی شما استفاده کند. اطمینان حاصل کنید که شبکه شما مورد اعتماد است و اقدامات امنیتی بیشتری را در نظر بگیرید.
|
||||
@@ -270,7 +268,7 @@ json
|
||||
| `auth_key` | رمز مشترک بین کامپیوتر شما و رله |
|
||||
| `script_id` | شناسه Deployment مربوط به Google Apps Script شما |
|
||||
| `listen_host` | محل گوش دادن (`127.0.0.1` = فقط همین کامپیوتر، `0.0.0.0` = همه اینترفیسها برای اشتراکگذاری LAN) |
|
||||
| `listen_port` | پورتی که پروکسی روی آن اجرا میشود (پیشفرض: `8085`) |
|
||||
| `http_port` | پورت HTTP پروکسی (پیشفرض: `8085`) |
|
||||
| `lan_sharing` | فعالسازی اشتراکگذاری LAN تا دستگاههای دیگر در شبکه شما بتوانند از پروکسی استفاده کنند (بهصورت پیشفرض `false`) |
|
||||
| `log_level` | میزان جزئیات لاگ: `DEBUG`، `INFO`، `WARNING`، `ERROR` |
|
||||
|
||||
@@ -284,20 +282,46 @@ json
|
||||
| `relay_timeout` | `25` | مهلت کل برای هر درخواست relay قبل از fail شدن |
|
||||
| `tls_connect_timeout` | `15` | مهلت اتصال TLS پروکسی به endpoint fronted روی Google/CDN |
|
||||
| `tcp_connect_timeout` | `10` | مهلت اتصال برای tunnel مستقیم و SNI-rewrite |
|
||||
| `max_response_body_bytes` | `209715200` | سقف نهایی برای اندازه body هر پاسخ relay بعد از buffer/decode |
|
||||
| `script_ids` | - | چند Deployment ID برای load balancing |
|
||||
| `chunked_download_extensions` | مطابق [config.example.json](config.example.json) | پسوند فایلهایی که باید از دانلود range-parallel استفاده کنند. از `".*"` هم برای probe همه دانلودهای GET پشتیبانی میشود. |
|
||||
| `chunked_download_min_size` | `5242880` | حداقل اندازه کل فایل (۵ مگابایت) برای فعال ماندن دانلود موازی |
|
||||
| `chunked_download_chunk_size` | `524288` | اندازه هر chunk در دانلود موازی |
|
||||
| `chunked_download_max_parallel` | `8` | حداکثر تعداد range request همزمان برای یک دانلود |
|
||||
| `chunked_download_max_chunks` | `256` | سقف نرم برای تعداد کل chunk request ها؛ برای فایلهای خیلی بزرگ اندازه chunk بهصورت خودکار بیشتر میشود |
|
||||
| `hosts` | `{}` | نگاشت دستی DNS (`hostname` یا `.suffix` به IP). مثال: `{ "example.org": "93.184.216.34", ".internal.lan": "192.168.1.10" }`. |
|
||||
| `block_hosts` | `[]` | هاستهایی که هرگز نباید tunnel شوند (پاسخ 403). نام دقیق (`ads.example.com`) یا پسوند با نقطهی ابتدایی (`.doubleclick.net`). |
|
||||
| `direct_hosts` | `[]` | دامنههایی که همیشه باید مستقیم بروند (بدون MITM و بدون relay/domain-fronting). از نام دقیق یا پسوند نقطهدار پشتیبانی میکند. |
|
||||
| `bypass_hosts` | `["localhost", ".local", ".lan", ".home.arpa"]` | هاستهایی که مستقیم میروند (بدون MITM و بدون رله). برای منابع داخلی شبکه یا سایتهایی که با MITM مشکل دارند. |
|
||||
| `direct_google_exclude` | مراجعه به [config.example.json](config.example.json) | اپهای Google که باید از مسیر MITM برای رله استفاده کنند بهجای tunnel مستقیم. |
|
||||
| `youtube_via_relay` | `false` | مسیردهی YouTube (`youtube.com`، `youtu.be`، `youtube-nocookie.com`) از طریق رله Apps Script بهجای مسیر SNI-rewrite. مسیر SNI-rewrite از IP فرانتاند Google عبور میکند که SafeSearch را اجباری میکند و میتواند باعث خطای **«ویدیو در دسترس نیست»** شود. با فعال کردن این گزینه، پخش ویدیو درست میشود اما تعداد اجراهای Apps Script بیشتر و تأخیر اندکی بالاتر میرود. |
|
||||
| `exit_node.provider` | `cloudflare` | backend انتخابشده برای exit node: `cloudflare`، `deno`، `vps` یا `custom`. |
|
||||
| `exit_node.url` | `""` | آدرس ساده و اصلی برای provider انتخابشده. |
|
||||
|
||||
نمونه کاربردی برای policy ها:
|
||||
|
||||
```json
|
||||
{
|
||||
"block_hosts": [
|
||||
"ads.example.com",
|
||||
".doubleclick.net"
|
||||
],
|
||||
"direct_hosts": [
|
||||
"chat.openai.com",
|
||||
".openai.com"
|
||||
],
|
||||
"hosts": {
|
||||
"example.org": "93.184.216.34",
|
||||
".internal.lan": "192.168.1.10"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `block_hosts`: این دامنهها کامل مسدود میشوند (پاسخ `403`).
|
||||
- `direct_hosts`: این دامنهها همیشه مستقیم میروند (بدون MITM و بدون relay fronting).
|
||||
- `hosts`: قبل از DNS واقعی، نگاشت دستی اعمال میشود (برای تست یا split-DNS workaround).
|
||||
|
||||
نکته: سقف اندازه پاسخ relay حالا یک مقدار ثابت کدی (`MAX_RESPONSE_BODY_BYTES`) در [src/core/constants.py](src/core/constants.py) است و دیگر گزینهی کاربری config نیست.
|
||||
|
||||
### وابستگیهای اختیاری
|
||||
|
||||
همه وابستگیهای [`requirements.txt`](requirements.txt) اختیاری هستند — در حالت پایه بدون هیچکدام کار میکند، ولی با نصب آنها امکانات بیشتری در دسترس است:
|
||||
@@ -337,7 +361,6 @@ json
|
||||
python3 main.py
|
||||
python3 main.py -p 9090
|
||||
python3 main.py --socks5-port 1081
|
||||
python3 main.py --disable-socks5
|
||||
python3 main.py --log-level DEBUG
|
||||
python3 main.py -c /path/to/config.json
|
||||
python3 main.py --install-cert # نصب گواهی CA و خروج
|
||||
|
||||
+16
-75
@@ -1,90 +1,30 @@
|
||||
{
|
||||
"mode": "apps_script",
|
||||
"google_ip": "216.239.38.120",
|
||||
"front_domain": "www.google.com",
|
||||
"script_id": "YOUR_APPS_SCRIPT_DEPLOYMENT_ID",
|
||||
"auth_key": "CHANGE_ME_TO_A_STRONG_SECRET",
|
||||
"listen_host": "127.0.0.1",
|
||||
"socks5_enabled": true,
|
||||
"listen_port": 8085,
|
||||
"http_port": 8085,
|
||||
"socks5_port": 1080,
|
||||
"log_level": "INFO",
|
||||
"verify_ssl": true,
|
||||
"lan_sharing": true,
|
||||
"lan_sharing": false,
|
||||
"relay_timeout": 25,
|
||||
"tls_connect_timeout": 15,
|
||||
"tcp_connect_timeout": 10,
|
||||
"max_response_body_bytes": 209715200,
|
||||
"parallel_relay": 1,
|
||||
"chunked_download_extensions": [
|
||||
".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"
|
||||
"block_hosts": [
|
||||
"ads.example.com",
|
||||
".doubleclick.net"
|
||||
],
|
||||
"chunked_download_min_size": 5242880,
|
||||
"chunked_download_chunk_size": 524288,
|
||||
"chunked_download_max_parallel": 8,
|
||||
"chunked_download_max_chunks": 256,
|
||||
"block_hosts": [],
|
||||
"bypass_hosts": [
|
||||
"localhost",
|
||||
".local",
|
||||
".lan",
|
||||
".home.arpa"
|
||||
],
|
||||
"direct_google_exclude": [
|
||||
"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",
|
||||
"maps.google.com",
|
||||
"play.google.com",
|
||||
"translate.google.com",
|
||||
"assistant.google.com",
|
||||
"lens.google.com"
|
||||
],
|
||||
"direct_google_allow": [
|
||||
"www.google.com",
|
||||
"safebrowsing.google.com"
|
||||
"direct_hosts": [
|
||||
"rubika.ir",
|
||||
"soft98.ir"
|
||||
],
|
||||
"youtube_via_relay": false,
|
||||
"hosts": {},
|
||||
"hosts": {
|
||||
"example.org": "93.184.216.34",
|
||||
".internal.lan": "192.168.1.10"
|
||||
},
|
||||
"exit_node": {
|
||||
"enabled": false,
|
||||
"provider": "cloudflare",
|
||||
@@ -92,8 +32,9 @@
|
||||
"psk": "",
|
||||
"mode": "full",
|
||||
"hosts": [
|
||||
"example.com",
|
||||
"example.org"
|
||||
".chatgpt.com",
|
||||
".openai.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"log_level": "INFO"
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ def parse_args():
|
||||
"-p", "--port",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Override listen port (env: DFT_PORT)",
|
||||
help="Override HTTP proxy port (env: DFT_HTTP_PORT, legacy: DFT_PORT)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--host",
|
||||
@@ -66,7 +66,7 @@ def parse_args():
|
||||
parser.add_argument(
|
||||
"--disable-socks5",
|
||||
action="store_true",
|
||||
help="Disable the built-in SOCKS5 listener.",
|
||||
help="Deprecated: SOCKS5 listener is always enabled.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
@@ -170,9 +170,15 @@ def main():
|
||||
|
||||
# CLI argument overrides
|
||||
if args.port is not None:
|
||||
config["listen_port"] = args.port
|
||||
config["http_port"] = args.port
|
||||
elif os.environ.get("DFT_HTTP_PORT"):
|
||||
config["http_port"] = int(os.environ["DFT_HTTP_PORT"])
|
||||
elif os.environ.get("DFT_PORT"):
|
||||
config["listen_port"] = int(os.environ["DFT_PORT"])
|
||||
config["http_port"] = int(os.environ["DFT_PORT"])
|
||||
|
||||
# Backward compatibility for older config files.
|
||||
if "http_port" not in config:
|
||||
config["http_port"] = int(config.get("listen_port", 8080))
|
||||
|
||||
if args.host is not None:
|
||||
config["listen_host"] = args.host
|
||||
@@ -185,7 +191,12 @@ def main():
|
||||
config["socks5_port"] = int(os.environ["DFT_SOCKS5_PORT"])
|
||||
|
||||
if args.disable_socks5:
|
||||
config["socks5_enabled"] = False
|
||||
logging.getLogger("Main").warning(
|
||||
"--disable-socks5 is deprecated and ignored: SOCKS5 is always enabled."
|
||||
)
|
||||
|
||||
# Keep runtime behavior fixed regardless of user config values.
|
||||
config["socks5_enabled"] = True
|
||||
|
||||
if args.log_level is not None:
|
||||
config["log_level"] = args.log_level
|
||||
@@ -273,8 +284,8 @@ def main():
|
||||
# print concrete IPv4 addresses users can use on other devices.
|
||||
lan_mode = lan_sharing or listen_host in ("0.0.0.0", "::")
|
||||
if lan_mode:
|
||||
socks_port = config.get("socks5_port", 1080) if config.get("socks5_enabled", True) else None
|
||||
log_lan_access(config.get("listen_port", 8080), socks_port)
|
||||
socks_port = config.get("socks5_port", 1080)
|
||||
log_lan_access(config.get("http_port", config.get("listen_port", 8080)), socks_port)
|
||||
|
||||
try:
|
||||
asyncio.run(_run(config))
|
||||
|
||||
@@ -77,12 +77,10 @@ def load_base_config() -> dict:
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"mode": "apps_script",
|
||||
"google_ip": "216.239.38.120",
|
||||
"front_domain": "www.google.com",
|
||||
"listen_host": "127.0.0.1",
|
||||
"listen_port": 8085,
|
||||
"socks5_enabled": True,
|
||||
"http_port": 8085,
|
||||
"socks5_port": 1080,
|
||||
"log_level": "INFO",
|
||||
"verify_ssl": True,
|
||||
@@ -90,11 +88,7 @@ def load_base_config() -> dict:
|
||||
"relay_timeout": 25,
|
||||
"tls_connect_timeout": 15,
|
||||
"tcp_connect_timeout": 10,
|
||||
"max_response_body_bytes": 200 * 1024 * 1024,
|
||||
"chunked_download_min_size": 5 * 1024 * 1024,
|
||||
"chunked_download_chunk_size": 512 * 1024,
|
||||
"chunked_download_max_parallel": 8,
|
||||
"chunked_download_max_chunks": 256,
|
||||
"direct_hosts": [],
|
||||
"hosts": {},
|
||||
}
|
||||
|
||||
@@ -137,20 +131,21 @@ def configure_network(cfg: dict) -> dict:
|
||||
default_host = "0.0.0.0"
|
||||
cfg["listen_host"] = prompt("Listen host", default=default_host)
|
||||
|
||||
port = prompt("HTTP proxy port", default=str(cfg.get("listen_port", 8085)))
|
||||
port = prompt(
|
||||
"HTTP proxy port",
|
||||
default=str(cfg.get("http_port", cfg.get("listen_port", 8085))),
|
||||
)
|
||||
try:
|
||||
cfg["listen_port"] = int(port)
|
||||
cfg["http_port"] = int(port)
|
||||
except ValueError:
|
||||
cfg["listen_port"] = 8085
|
||||
cfg["http_port"] = 8085
|
||||
|
||||
socks5 = prompt_yes_no("Enable SOCKS5 proxy?", default=bool(cfg.get("socks5_enabled", True)))
|
||||
cfg["socks5_enabled"] = socks5
|
||||
if socks5:
|
||||
sport = prompt("SOCKS5 port", default=str(cfg.get("socks5_port", 1080)))
|
||||
try:
|
||||
cfg["socks5_port"] = int(sport)
|
||||
except ValueError:
|
||||
cfg["socks5_port"] = 1080
|
||||
# SOCKS5 is always enabled at runtime; only port is configurable.
|
||||
sport = prompt("SOCKS5 port", default=str(cfg.get("socks5_port", 1080)))
|
||||
try:
|
||||
cfg["socks5_port"] = int(sport)
|
||||
except ValueError:
|
||||
cfg["socks5_port"] = 1080
|
||||
return cfg
|
||||
|
||||
|
||||
@@ -175,7 +170,6 @@ def main() -> int:
|
||||
return 0
|
||||
|
||||
cfg = load_base_config()
|
||||
cfg["mode"] = "apps_script"
|
||||
|
||||
suggested_key = random_auth_key()
|
||||
print()
|
||||
|
||||
@@ -107,6 +107,17 @@ FRONT_SNI_POOL_GOOGLE: tuple[str, ...] = (
|
||||
)
|
||||
|
||||
|
||||
# ── Bypass hosts (direct, no MITM/relay) ────────────────────────────────
|
||||
# Applied when bypass_hosts is omitted from config.json.
|
||||
# Advanced users can override this list in config.json under "bypass_hosts".
|
||||
DEFAULT_BYPASS_HOSTS: tuple[str, ...] = (
|
||||
"localhost",
|
||||
".local",
|
||||
".lan",
|
||||
".home.arpa",
|
||||
)
|
||||
|
||||
|
||||
# ── 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
|
||||
|
||||
@@ -22,6 +22,7 @@ except Exception: # optional dependency fallback
|
||||
from core.constants import (
|
||||
CACHE_MAX_MB,
|
||||
CLIENT_IDLE_TIMEOUT,
|
||||
DEFAULT_BYPASS_HOSTS,
|
||||
GOOGLE_DIRECT_ALLOW_EXACT,
|
||||
GOOGLE_DIRECT_ALLOW_SUFFIXES,
|
||||
GOOGLE_DIRECT_EXACT_EXCLUDE,
|
||||
@@ -73,14 +74,15 @@ class ProxyServer:
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self.host = config.get("listen_host", "127.0.0.1")
|
||||
self.port = config.get("listen_port", 8080)
|
||||
self.socks_enabled = config.get("socks5_enabled", True)
|
||||
# Prefer the new key (http_port) but keep listen_port for old configs.
|
||||
self.port = config.get("http_port", config.get("listen_port", 8080))
|
||||
self.socks_enabled = True
|
||||
self.socks_host = config.get("socks5_host", self.host)
|
||||
self.socks_port = config.get("socks5_port", 1080)
|
||||
if self.socks_enabled and self.socks_host == self.host \
|
||||
and int(self.socks_port) == int(self.port):
|
||||
raise ValueError(
|
||||
f"listen_port and socks5_port must differ on the same host "
|
||||
f"http_port and socks5_port must differ on the same host "
|
||||
f"(both set to {self.port} on {self.host}). "
|
||||
f"Change one of them in config.json."
|
||||
)
|
||||
@@ -137,12 +139,19 @@ class ProxyServer:
|
||||
}
|
||||
|
||||
# ── Per-host policy ────────────────────────────────────────
|
||||
# block_hosts — refuse traffic entirely (close or 403)
|
||||
# bypass_hosts — route directly (no MITM, no relay)
|
||||
# block_hosts — refuse traffic entirely (close or 403)
|
||||
# direct_hosts — route directly (no MITM, no relay)
|
||||
# bypass_hosts — legacy alias kept for backward compatibility
|
||||
# Both accept exact hostnames and leading-dot suffix patterns,
|
||||
# e.g. ".local" matches any *.local domain.
|
||||
self._block_hosts = load_host_rules(config.get("block_hosts", []))
|
||||
self._bypass_hosts = load_host_rules(config.get("bypass_hosts", []))
|
||||
direct_hosts = config.get("direct_hosts", [])
|
||||
bypass_hosts = config.get("bypass_hosts")
|
||||
if bypass_hosts is None:
|
||||
bypass_hosts = list(DEFAULT_BYPASS_HOSTS)
|
||||
self._bypass_hosts = load_host_rules(
|
||||
list(bypass_hosts) + list(direct_hosts)
|
||||
)
|
||||
|
||||
# Route YouTube through the relay when requested; the Google frontend
|
||||
# IP can enforce SafeSearch on the SNI-rewrite path.
|
||||
@@ -428,7 +437,7 @@ class ProxyServer:
|
||||
return
|
||||
|
||||
if self._is_bypassed(host):
|
||||
log.info("Bypass tunnel → %s:%d (matches bypass_hosts)", host, port)
|
||||
log.info("Direct tunnel → %s:%d (matches direct_hosts/bypass_hosts)", host, port)
|
||||
await self._do_direct_tunnel(host, port, reader, writer)
|
||||
return
|
||||
|
||||
|
||||
@@ -137,10 +137,9 @@ class DomainFronter:
|
||||
config, "tls_connect_timeout", TLS_CONNECT_TIMEOUT, minimum=1.0,
|
||||
)
|
||||
self._sni_probe_timeout = min(self._tls_connect_timeout, 4.0)
|
||||
self._max_response_body_bytes = self._cfg_int(
|
||||
config, "max_response_body_bytes", MAX_RESPONSE_BODY_BYTES,
|
||||
minimum=1024,
|
||||
)
|
||||
# Keep response cap as a code-level constant to avoid exposing an
|
||||
# advanced memory-safety knob in end-user config.
|
||||
self._max_response_body_bytes = MAX_RESPONSE_BODY_BYTES
|
||||
|
||||
# Connection pool — TTL-based, pre-warmed, with concurrency control
|
||||
self._pool: list[tuple[asyncio.StreamReader, asyncio.StreamWriter, float]] = []
|
||||
@@ -1418,7 +1417,7 @@ class DomainFronter:
|
||||
502,
|
||||
"Relay response exceeds cap "
|
||||
f"({self._max_response_body_bytes} bytes). "
|
||||
"Increase max_response_body_bytes if your system has enough RAM.",
|
||||
"Increase MAX_RESPONSE_BODY_BYTES in src/core/constants.py if your system has enough RAM.",
|
||||
)
|
||||
if min_size > 0 and total_size < min_size:
|
||||
return self._rewrite_206_to_200(first_resp)
|
||||
|
||||
@@ -224,7 +224,7 @@ def parse_relay_json(data: dict, max_body_bytes: int) -> bytes:
|
||||
return error_response(
|
||||
502,
|
||||
f"Relay response exceeds cap ({max_body_bytes} bytes). "
|
||||
"Increase max_response_body_bytes if your system has enough RAM.",
|
||||
"Increase MAX_RESPONSE_BODY_BYTES in src/core/constants.py if your system has enough RAM.",
|
||||
)
|
||||
|
||||
status_text = {
|
||||
|
||||
Reference in New Issue
Block a user