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:
|
2. Open `config.json` in any text editor and fill in your values:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mode": "apps_script",
|
|
||||||
"google_ip": "216.239.38.120",
|
"google_ip": "216.239.38.120",
|
||||||
"front_domain": "www.google.com",
|
"front_domain": "www.google.com",
|
||||||
"script_id": "PASTE_YOUR_DEPLOYMENT_ID_HERE",
|
"script_id": "PASTE_YOUR_DEPLOYMENT_ID_HERE",
|
||||||
"auth_key": "your-secret-password-here",
|
"auth_key": "your-secret-password-here",
|
||||||
"listen_host": "127.0.0.1",
|
"listen_host": "127.0.0.1",
|
||||||
"listen_port": 8085,
|
"http_port": 8085,
|
||||||
"socks5_enabled": true,
|
|
||||||
"socks5_port": 1080,
|
"socks5_port": 1080,
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
"verify_ssl": true
|
"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,
|
"lan_sharing": true,
|
||||||
"listen_host": "0.0.0.0",
|
"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 |
|
| `auth_key` | Password shared between your computer and the relay |
|
||||||
| `script_id` | Your Google Apps Script Deployment ID |
|
| `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_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) |
|
| `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` |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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) |
|
| `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_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_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_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_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 |
|
| `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`). |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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.provider` | `cloudflare` | Selected exit-node backend: `cloudflare`, `deno`, `vps`, or `custom`. |
|
||||||
| `exit_node.url` | `""` | Beginner-friendly single URL for the selected provider. |
|
| `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
|
### 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:
|
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 # Normal start
|
||||||
python3 main.py -p 9090 # Use HTTP port 9090 instead
|
python3 main.py -p 9090 # Use HTTP port 9090 instead
|
||||||
python3 main.py --socks5-port 1081 # Use SOCKS5 port 1081
|
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 --log-level DEBUG # Show detailed logs
|
||||||
python3 main.py -c /path/to/config.json # Use a different config file
|
python3 main.py -c /path/to/config.json # Use a different config file
|
||||||
python3 main.py --install-cert # Install MITM CA certificate and exit
|
python3 main.py --install-cert # Install MITM CA certificate and exit
|
||||||
|
|||||||
+30
-7
@@ -116,14 +116,12 @@ cp config.example.json config.json
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mode": "apps_script",
|
|
||||||
"google_ip": "216.239.38.120",
|
"google_ip": "216.239.38.120",
|
||||||
"front_domain": "www.google.com",
|
"front_domain": "www.google.com",
|
||||||
"script_id": "PASTE_YOUR_DEPLOYMENT_ID_HERE",
|
"script_id": "PASTE_YOUR_DEPLOYMENT_ID_HERE",
|
||||||
"auth_key": "your-secret-password-here",
|
"auth_key": "your-secret-password-here",
|
||||||
"listen_host": "127.0.0.1",
|
"listen_host": "127.0.0.1",
|
||||||
"listen_port": 8085,
|
"http_port": 8085,
|
||||||
"socks5_enabled": true,
|
|
||||||
"socks5_port": 1080,
|
"socks5_port": 1080,
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
"verify_ssl": true
|
"verify_ssl": true
|
||||||
@@ -254,7 +252,7 @@ json
|
|||||||
{
|
{
|
||||||
"lan_sharing": true,
|
"lan_sharing": true,
|
||||||
"listen_host": "0.0.0.0",
|
"listen_host": "0.0.0.0",
|
||||||
"listen_port": 8085
|
"http_port": 8085
|
||||||
}
|
}
|
||||||
|
|
||||||
**هشدار امنیتی:** وقتی اشتراکگذاری در شبکه محلی فعال باشد، هر کسی در شبکه محلی شما میتواند از پروکسی شما استفاده کند. اطمینان حاصل کنید که شبکه شما مورد اعتماد است و اقدامات امنیتی بیشتری را در نظر بگیرید.
|
**هشدار امنیتی:** وقتی اشتراکگذاری در شبکه محلی فعال باشد، هر کسی در شبکه محلی شما میتواند از پروکسی شما استفاده کند. اطمینان حاصل کنید که شبکه شما مورد اعتماد است و اقدامات امنیتی بیشتری را در نظر بگیرید.
|
||||||
@@ -270,7 +268,7 @@ json
|
|||||||
| `auth_key` | رمز مشترک بین کامپیوتر شما و رله |
|
| `auth_key` | رمز مشترک بین کامپیوتر شما و رله |
|
||||||
| `script_id` | شناسه Deployment مربوط به Google Apps Script شما |
|
| `script_id` | شناسه Deployment مربوط به Google Apps Script شما |
|
||||||
| `listen_host` | محل گوش دادن (`127.0.0.1` = فقط همین کامپیوتر، `0.0.0.0` = همه اینترفیسها برای اشتراکگذاری LAN) |
|
| `listen_host` | محل گوش دادن (`127.0.0.1` = فقط همین کامپیوتر، `0.0.0.0` = همه اینترفیسها برای اشتراکگذاری LAN) |
|
||||||
| `listen_port` | پورتی که پروکسی روی آن اجرا میشود (پیشفرض: `8085`) |
|
| `http_port` | پورت HTTP پروکسی (پیشفرض: `8085`) |
|
||||||
| `lan_sharing` | فعالسازی اشتراکگذاری LAN تا دستگاههای دیگر در شبکه شما بتوانند از پروکسی استفاده کنند (بهصورت پیشفرض `false`) |
|
| `lan_sharing` | فعالسازی اشتراکگذاری LAN تا دستگاههای دیگر در شبکه شما بتوانند از پروکسی استفاده کنند (بهصورت پیشفرض `false`) |
|
||||||
| `log_level` | میزان جزئیات لاگ: `DEBUG`، `INFO`، `WARNING`، `ERROR` |
|
| `log_level` | میزان جزئیات لاگ: `DEBUG`، `INFO`، `WARNING`، `ERROR` |
|
||||||
|
|
||||||
@@ -284,20 +282,46 @@ json
|
|||||||
| `relay_timeout` | `25` | مهلت کل برای هر درخواست relay قبل از fail شدن |
|
| `relay_timeout` | `25` | مهلت کل برای هر درخواست relay قبل از fail شدن |
|
||||||
| `tls_connect_timeout` | `15` | مهلت اتصال TLS پروکسی به endpoint fronted روی Google/CDN |
|
| `tls_connect_timeout` | `15` | مهلت اتصال TLS پروکسی به endpoint fronted روی Google/CDN |
|
||||||
| `tcp_connect_timeout` | `10` | مهلت اتصال برای tunnel مستقیم و SNI-rewrite |
|
| `tcp_connect_timeout` | `10` | مهلت اتصال برای tunnel مستقیم و SNI-rewrite |
|
||||||
| `max_response_body_bytes` | `209715200` | سقف نهایی برای اندازه body هر پاسخ relay بعد از buffer/decode |
|
|
||||||
| `script_ids` | - | چند Deployment ID برای load balancing |
|
| `script_ids` | - | چند Deployment ID برای load balancing |
|
||||||
| `chunked_download_extensions` | مطابق [config.example.json](config.example.json) | پسوند فایلهایی که باید از دانلود range-parallel استفاده کنند. از `".*"` هم برای probe همه دانلودهای GET پشتیبانی میشود. |
|
| `chunked_download_extensions` | مطابق [config.example.json](config.example.json) | پسوند فایلهایی که باید از دانلود range-parallel استفاده کنند. از `".*"` هم برای probe همه دانلودهای GET پشتیبانی میشود. |
|
||||||
| `chunked_download_min_size` | `5242880` | حداقل اندازه کل فایل (۵ مگابایت) برای فعال ماندن دانلود موازی |
|
| `chunked_download_min_size` | `5242880` | حداقل اندازه کل فایل (۵ مگابایت) برای فعال ماندن دانلود موازی |
|
||||||
| `chunked_download_chunk_size` | `524288` | اندازه هر chunk در دانلود موازی |
|
| `chunked_download_chunk_size` | `524288` | اندازه هر chunk در دانلود موازی |
|
||||||
| `chunked_download_max_parallel` | `8` | حداکثر تعداد range request همزمان برای یک دانلود |
|
| `chunked_download_max_parallel` | `8` | حداکثر تعداد range request همزمان برای یک دانلود |
|
||||||
| `chunked_download_max_chunks` | `256` | سقف نرم برای تعداد کل chunk request ها؛ برای فایلهای خیلی بزرگ اندازه chunk بهصورت خودکار بیشتر میشود |
|
| `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`). |
|
| `block_hosts` | `[]` | هاستهایی که هرگز نباید tunnel شوند (پاسخ 403). نام دقیق (`ads.example.com`) یا پسوند با نقطهی ابتدایی (`.doubleclick.net`). |
|
||||||
|
| `direct_hosts` | `[]` | دامنههایی که همیشه باید مستقیم بروند (بدون MITM و بدون relay/domain-fronting). از نام دقیق یا پسوند نقطهدار پشتیبانی میکند. |
|
||||||
| `bypass_hosts` | `["localhost", ".local", ".lan", ".home.arpa"]` | هاستهایی که مستقیم میروند (بدون MITM و بدون رله). برای منابع داخلی شبکه یا سایتهایی که با MITM مشکل دارند. |
|
| `bypass_hosts` | `["localhost", ".local", ".lan", ".home.arpa"]` | هاستهایی که مستقیم میروند (بدون MITM و بدون رله). برای منابع داخلی شبکه یا سایتهایی که با MITM مشکل دارند. |
|
||||||
| `direct_google_exclude` | مراجعه به [config.example.json](config.example.json) | اپهای Google که باید از مسیر MITM برای رله استفاده کنند بهجای tunnel مستقیم. |
|
| `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 بیشتر و تأخیر اندکی بالاتر میرود. |
|
| `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.provider` | `cloudflare` | backend انتخابشده برای exit node: `cloudflare`، `deno`، `vps` یا `custom`. |
|
||||||
| `exit_node.url` | `""` | آدرس ساده و اصلی برای provider انتخابشده. |
|
| `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) اختیاری هستند — در حالت پایه بدون هیچکدام کار میکند، ولی با نصب آنها امکانات بیشتری در دسترس است:
|
همه وابستگیهای [`requirements.txt`](requirements.txt) اختیاری هستند — در حالت پایه بدون هیچکدام کار میکند، ولی با نصب آنها امکانات بیشتری در دسترس است:
|
||||||
@@ -337,7 +361,6 @@ json
|
|||||||
python3 main.py
|
python3 main.py
|
||||||
python3 main.py -p 9090
|
python3 main.py -p 9090
|
||||||
python3 main.py --socks5-port 1081
|
python3 main.py --socks5-port 1081
|
||||||
python3 main.py --disable-socks5
|
|
||||||
python3 main.py --log-level DEBUG
|
python3 main.py --log-level DEBUG
|
||||||
python3 main.py -c /path/to/config.json
|
python3 main.py -c /path/to/config.json
|
||||||
python3 main.py --install-cert # نصب گواهی CA و خروج
|
python3 main.py --install-cert # نصب گواهی CA و خروج
|
||||||
|
|||||||
+16
-75
@@ -1,90 +1,30 @@
|
|||||||
{
|
{
|
||||||
"mode": "apps_script",
|
|
||||||
"google_ip": "216.239.38.120",
|
"google_ip": "216.239.38.120",
|
||||||
"front_domain": "www.google.com",
|
"front_domain": "www.google.com",
|
||||||
"script_id": "YOUR_APPS_SCRIPT_DEPLOYMENT_ID",
|
"script_id": "YOUR_APPS_SCRIPT_DEPLOYMENT_ID",
|
||||||
"auth_key": "CHANGE_ME_TO_A_STRONG_SECRET",
|
"auth_key": "CHANGE_ME_TO_A_STRONG_SECRET",
|
||||||
"listen_host": "127.0.0.1",
|
"listen_host": "127.0.0.1",
|
||||||
"socks5_enabled": true,
|
"http_port": 8085,
|
||||||
"listen_port": 8085,
|
|
||||||
"socks5_port": 1080,
|
"socks5_port": 1080,
|
||||||
"log_level": "INFO",
|
|
||||||
"verify_ssl": true,
|
"verify_ssl": true,
|
||||||
"lan_sharing": true,
|
"lan_sharing": false,
|
||||||
"relay_timeout": 25,
|
"relay_timeout": 25,
|
||||||
"tls_connect_timeout": 15,
|
"tls_connect_timeout": 15,
|
||||||
"tcp_connect_timeout": 10,
|
"tcp_connect_timeout": 10,
|
||||||
"max_response_body_bytes": 209715200,
|
|
||||||
"parallel_relay": 1,
|
"parallel_relay": 1,
|
||||||
"chunked_download_extensions": [
|
"block_hosts": [
|
||||||
".bin",
|
"ads.example.com",
|
||||||
".zip",
|
".doubleclick.net"
|
||||||
".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"
|
|
||||||
],
|
],
|
||||||
"chunked_download_min_size": 5242880,
|
"direct_hosts": [
|
||||||
"chunked_download_chunk_size": 524288,
|
"rubika.ir",
|
||||||
"chunked_download_max_parallel": 8,
|
"soft98.ir"
|
||||||
"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"
|
|
||||||
],
|
],
|
||||||
"youtube_via_relay": false,
|
"youtube_via_relay": false,
|
||||||
"hosts": {},
|
"hosts": {
|
||||||
|
"example.org": "93.184.216.34",
|
||||||
|
".internal.lan": "192.168.1.10"
|
||||||
|
},
|
||||||
"exit_node": {
|
"exit_node": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"provider": "cloudflare",
|
"provider": "cloudflare",
|
||||||
@@ -92,8 +32,9 @@
|
|||||||
"psk": "",
|
"psk": "",
|
||||||
"mode": "full",
|
"mode": "full",
|
||||||
"hosts": [
|
"hosts": [
|
||||||
"example.com",
|
".chatgpt.com",
|
||||||
"example.org"
|
".openai.com"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"log_level": "INFO"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def parse_args():
|
|||||||
"-p", "--port",
|
"-p", "--port",
|
||||||
type=int,
|
type=int,
|
||||||
default=None,
|
default=None,
|
||||||
help="Override listen port (env: DFT_PORT)",
|
help="Override HTTP proxy port (env: DFT_HTTP_PORT, legacy: DFT_PORT)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--host",
|
"--host",
|
||||||
@@ -66,7 +66,7 @@ def parse_args():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--disable-socks5",
|
"--disable-socks5",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Disable the built-in SOCKS5 listener.",
|
help="Deprecated: SOCKS5 listener is always enabled.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--log-level",
|
"--log-level",
|
||||||
@@ -170,9 +170,15 @@ def main():
|
|||||||
|
|
||||||
# CLI argument overrides
|
# CLI argument overrides
|
||||||
if args.port is not None:
|
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"):
|
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:
|
if args.host is not None:
|
||||||
config["listen_host"] = args.host
|
config["listen_host"] = args.host
|
||||||
@@ -185,7 +191,12 @@ def main():
|
|||||||
config["socks5_port"] = int(os.environ["DFT_SOCKS5_PORT"])
|
config["socks5_port"] = int(os.environ["DFT_SOCKS5_PORT"])
|
||||||
|
|
||||||
if args.disable_socks5:
|
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:
|
if args.log_level is not None:
|
||||||
config["log_level"] = args.log_level
|
config["log_level"] = args.log_level
|
||||||
@@ -273,8 +284,8 @@ def main():
|
|||||||
# print concrete IPv4 addresses users can use on other devices.
|
# print concrete IPv4 addresses users can use on other devices.
|
||||||
lan_mode = lan_sharing or listen_host in ("0.0.0.0", "::")
|
lan_mode = lan_sharing or listen_host in ("0.0.0.0", "::")
|
||||||
if lan_mode:
|
if lan_mode:
|
||||||
socks_port = config.get("socks5_port", 1080) if config.get("socks5_enabled", True) else None
|
socks_port = config.get("socks5_port", 1080)
|
||||||
log_lan_access(config.get("listen_port", 8080), socks_port)
|
log_lan_access(config.get("http_port", config.get("listen_port", 8080)), socks_port)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
asyncio.run(_run(config))
|
asyncio.run(_run(config))
|
||||||
|
|||||||
@@ -77,12 +77,10 @@ def load_base_config() -> dict:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return {
|
return {
|
||||||
"mode": "apps_script",
|
|
||||||
"google_ip": "216.239.38.120",
|
"google_ip": "216.239.38.120",
|
||||||
"front_domain": "www.google.com",
|
"front_domain": "www.google.com",
|
||||||
"listen_host": "127.0.0.1",
|
"listen_host": "127.0.0.1",
|
||||||
"listen_port": 8085,
|
"http_port": 8085,
|
||||||
"socks5_enabled": True,
|
|
||||||
"socks5_port": 1080,
|
"socks5_port": 1080,
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
"verify_ssl": True,
|
"verify_ssl": True,
|
||||||
@@ -90,11 +88,7 @@ def load_base_config() -> dict:
|
|||||||
"relay_timeout": 25,
|
"relay_timeout": 25,
|
||||||
"tls_connect_timeout": 15,
|
"tls_connect_timeout": 15,
|
||||||
"tcp_connect_timeout": 10,
|
"tcp_connect_timeout": 10,
|
||||||
"max_response_body_bytes": 200 * 1024 * 1024,
|
"direct_hosts": [],
|
||||||
"chunked_download_min_size": 5 * 1024 * 1024,
|
|
||||||
"chunked_download_chunk_size": 512 * 1024,
|
|
||||||
"chunked_download_max_parallel": 8,
|
|
||||||
"chunked_download_max_chunks": 256,
|
|
||||||
"hosts": {},
|
"hosts": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,20 +131,21 @@ def configure_network(cfg: dict) -> dict:
|
|||||||
default_host = "0.0.0.0"
|
default_host = "0.0.0.0"
|
||||||
cfg["listen_host"] = prompt("Listen host", default=default_host)
|
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:
|
try:
|
||||||
cfg["listen_port"] = int(port)
|
cfg["http_port"] = int(port)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
cfg["listen_port"] = 8085
|
cfg["http_port"] = 8085
|
||||||
|
|
||||||
socks5 = prompt_yes_no("Enable SOCKS5 proxy?", default=bool(cfg.get("socks5_enabled", True)))
|
# SOCKS5 is always enabled at runtime; only port is configurable.
|
||||||
cfg["socks5_enabled"] = socks5
|
sport = prompt("SOCKS5 port", default=str(cfg.get("socks5_port", 1080)))
|
||||||
if socks5:
|
try:
|
||||||
sport = prompt("SOCKS5 port", default=str(cfg.get("socks5_port", 1080)))
|
cfg["socks5_port"] = int(sport)
|
||||||
try:
|
except ValueError:
|
||||||
cfg["socks5_port"] = int(sport)
|
cfg["socks5_port"] = 1080
|
||||||
except ValueError:
|
|
||||||
cfg["socks5_port"] = 1080
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
@@ -175,7 +170,6 @@ def main() -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
cfg = load_base_config()
|
cfg = load_base_config()
|
||||||
cfg["mode"] = "apps_script"
|
|
||||||
|
|
||||||
suggested_key = random_auth_key()
|
suggested_key = random_auth_key()
|
||||||
print()
|
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 ────────────────────────────────────────────────────────
|
# ── Per-host stats ────────────────────────────────────────────────────────
|
||||||
STATS_LOG_INTERVAL = 300.0 # seconds — how often to log per-host totals
|
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
|
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 (
|
from core.constants import (
|
||||||
CACHE_MAX_MB,
|
CACHE_MAX_MB,
|
||||||
CLIENT_IDLE_TIMEOUT,
|
CLIENT_IDLE_TIMEOUT,
|
||||||
|
DEFAULT_BYPASS_HOSTS,
|
||||||
GOOGLE_DIRECT_ALLOW_EXACT,
|
GOOGLE_DIRECT_ALLOW_EXACT,
|
||||||
GOOGLE_DIRECT_ALLOW_SUFFIXES,
|
GOOGLE_DIRECT_ALLOW_SUFFIXES,
|
||||||
GOOGLE_DIRECT_EXACT_EXCLUDE,
|
GOOGLE_DIRECT_EXACT_EXCLUDE,
|
||||||
@@ -73,14 +74,15 @@ class ProxyServer:
|
|||||||
|
|
||||||
def __init__(self, config: dict):
|
def __init__(self, config: dict):
|
||||||
self.host = config.get("listen_host", "127.0.0.1")
|
self.host = config.get("listen_host", "127.0.0.1")
|
||||||
self.port = config.get("listen_port", 8080)
|
# Prefer the new key (http_port) but keep listen_port for old configs.
|
||||||
self.socks_enabled = config.get("socks5_enabled", True)
|
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_host = config.get("socks5_host", self.host)
|
||||||
self.socks_port = config.get("socks5_port", 1080)
|
self.socks_port = config.get("socks5_port", 1080)
|
||||||
if self.socks_enabled and self.socks_host == self.host \
|
if self.socks_enabled and self.socks_host == self.host \
|
||||||
and int(self.socks_port) == int(self.port):
|
and int(self.socks_port) == int(self.port):
|
||||||
raise ValueError(
|
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"(both set to {self.port} on {self.host}). "
|
||||||
f"Change one of them in config.json."
|
f"Change one of them in config.json."
|
||||||
)
|
)
|
||||||
@@ -137,12 +139,19 @@ class ProxyServer:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ── Per-host policy ────────────────────────────────────────
|
# ── Per-host policy ────────────────────────────────────────
|
||||||
# block_hosts — refuse traffic entirely (close or 403)
|
# block_hosts — refuse traffic entirely (close or 403)
|
||||||
# bypass_hosts — route directly (no MITM, no relay)
|
# 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,
|
# Both accept exact hostnames and leading-dot suffix patterns,
|
||||||
# e.g. ".local" matches any *.local domain.
|
# e.g. ".local" matches any *.local domain.
|
||||||
self._block_hosts = load_host_rules(config.get("block_hosts", []))
|
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
|
# Route YouTube through the relay when requested; the Google frontend
|
||||||
# IP can enforce SafeSearch on the SNI-rewrite path.
|
# IP can enforce SafeSearch on the SNI-rewrite path.
|
||||||
@@ -428,7 +437,7 @@ class ProxyServer:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self._is_bypassed(host):
|
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)
|
await self._do_direct_tunnel(host, port, reader, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -137,10 +137,9 @@ class DomainFronter:
|
|||||||
config, "tls_connect_timeout", TLS_CONNECT_TIMEOUT, minimum=1.0,
|
config, "tls_connect_timeout", TLS_CONNECT_TIMEOUT, minimum=1.0,
|
||||||
)
|
)
|
||||||
self._sni_probe_timeout = min(self._tls_connect_timeout, 4.0)
|
self._sni_probe_timeout = min(self._tls_connect_timeout, 4.0)
|
||||||
self._max_response_body_bytes = self._cfg_int(
|
# Keep response cap as a code-level constant to avoid exposing an
|
||||||
config, "max_response_body_bytes", MAX_RESPONSE_BODY_BYTES,
|
# advanced memory-safety knob in end-user config.
|
||||||
minimum=1024,
|
self._max_response_body_bytes = MAX_RESPONSE_BODY_BYTES
|
||||||
)
|
|
||||||
|
|
||||||
# Connection pool — TTL-based, pre-warmed, with concurrency control
|
# Connection pool — TTL-based, pre-warmed, with concurrency control
|
||||||
self._pool: list[tuple[asyncio.StreamReader, asyncio.StreamWriter, float]] = []
|
self._pool: list[tuple[asyncio.StreamReader, asyncio.StreamWriter, float]] = []
|
||||||
@@ -1418,7 +1417,7 @@ class DomainFronter:
|
|||||||
502,
|
502,
|
||||||
"Relay response exceeds cap "
|
"Relay response exceeds cap "
|
||||||
f"({self._max_response_body_bytes} bytes). "
|
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:
|
if min_size > 0 and total_size < min_size:
|
||||||
return self._rewrite_206_to_200(first_resp)
|
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(
|
return error_response(
|
||||||
502,
|
502,
|
||||||
f"Relay response exceeds cap ({max_body_bytes} bytes). "
|
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 = {
|
status_text = {
|
||||||
|
|||||||
Reference in New Issue
Block a user