Add Code.gs relay script and update README with full documentation

This commit is contained in:
Abolfazl
2026-04-20 19:03:46 +03:30
parent df3c2c5c16
commit 5dbee179e1
2 changed files with 198 additions and 22 deletions
+128
View File
@@ -0,0 +1,128 @@
/**
* DomainFront Relay — Google Apps Script
*
* TWO modes:
* 1. Single: POST { k, m, u, h, b, ct, r } → { s, h, b }
* 2. Batch: POST { k, q: [{m,u,h,b,ct,r}, ...] } → { q: [{s,h,b}, ...] }
* Uses UrlFetchApp.fetchAll() — all URLs fetched IN PARALLEL.
*
* DEPLOYMENT:
* 1. Go to https://script.google.com → New project
* 2. Delete the default code, paste THIS entire file
* 3. Click Deploy → New deployment
* 4. Type: Web app | Execute as: Me | Who has access: Anyone
* 5. Copy the Deployment ID into config.json as "script_id"
*
* CHANGE THE AUTH KEY BELOW TO YOUR OWN SECRET!
*/
const AUTH_KEY = "CHANGE_ME_TO_A_STRONG_SECRET";
const SKIP_HEADERS = {
host: 1, connection: 1, "content-length": 1,
"transfer-encoding": 1, "proxy-connection": 1, "proxy-authorization": 1,
};
function doPost(e) {
try {
var req = JSON.parse(e.postData.contents);
if (req.k !== AUTH_KEY) return _json({ e: "unauthorized" });
// Batch mode: { k, q: [...] }
if (Array.isArray(req.q)) return _doBatch(req.q);
// Single mode
return _doSingle(req);
} catch (err) {
return _json({ e: String(err) });
}
}
function _doSingle(req) {
if (!req.u || typeof req.u !== "string" || !req.u.match(/^https?:\/\//i)) {
return _json({ e: "bad url" });
}
var opts = _buildOpts(req);
var resp = UrlFetchApp.fetch(req.u, opts);
return _json({
s: resp.getResponseCode(),
h: resp.getHeaders(),
b: Utilities.base64Encode(resp.getContent()),
});
}
function _doBatch(items) {
var fetchArgs = [];
var errorMap = {};
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item.u || typeof item.u !== "string" || !item.u.match(/^https?:\/\//i)) {
errorMap[i] = "bad url";
continue;
}
var opts = _buildOpts(item);
opts.url = item.u;
fetchArgs.push({ _i: i, _o: opts });
}
// fetchAll() processes all requests in parallel inside Google
var responses = [];
if (fetchArgs.length > 0) {
responses = UrlFetchApp.fetchAll(fetchArgs.map(function(x) { return x._o; }));
}
var results = [];
var rIdx = 0;
for (var i = 0; i < items.length; i++) {
if (errorMap.hasOwnProperty(i)) {
results.push({ e: errorMap[i] });
} else {
var resp = responses[rIdx++];
results.push({
s: resp.getResponseCode(),
h: resp.getHeaders(),
b: Utilities.base64Encode(resp.getContent()),
});
}
}
return _json({ q: results });
}
function _buildOpts(req) {
var opts = {
method: (req.m || "GET").toLowerCase(),
muteHttpExceptions: true,
followRedirects: req.r !== false,
validateHttpsCertificates: true,
};
if (req.h && typeof req.h === "object") {
var headers = {};
for (var k in req.h) {
if (req.h.hasOwnProperty(k) && !SKIP_HEADERS[k.toLowerCase()]) {
headers[k] = req.h[k];
}
}
opts.headers = headers;
}
if (req.b) {
opts.payload = Utilities.base64Decode(req.b);
if (req.ct) opts.contentType = req.ct;
}
return opts;
}
function doGet(e) {
return HtmlService.createHtmlOutput(
"<!DOCTYPE html><html><head><title>My App</title></head>" +
'<body style="font-family:sans-serif;max-width:600px;margin:40px auto">' +
"<h1>Welcome</h1><p>This application is running normally.</p>" +
"</body></html>"
);
}
function _json(obj) {
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(
ContentService.MimeType.JSON
);
}
+70 -22
View File
@@ -1,7 +1,11 @@
# DomainFront Tunnel (MasterHttpRelayVPN Testing Python!) # MasterHttpRelayVPN
[![GitHub](https://img.shields.io/badge/GitHub-MasterHttpRelayVPN-blue?logo=github)](https://github.com/masterking32/MasterHttpRelayVPN)
A local HTTP proxy that bypasses DPI (Deep Packet Inspection) censorship using **domain fronting**. The proxy tunnels all browser traffic through a CDN — the TLS SNI shows an allowed domain (e.g. `www.google.com`) while the encrypted HTTP Host header routes to your relay endpoint. A local HTTP proxy that bypasses DPI (Deep Packet Inspection) censorship using **domain fronting**. The proxy tunnels all browser traffic through a CDN — the TLS SNI shows an allowed domain (e.g. `www.google.com`) while the encrypted HTTP Host header routes to your relay endpoint.
> **Branch:** `python_testing` — Python implementation
## How It Works ## How It Works
``` ```
@@ -27,8 +31,8 @@ The DPI/firewall only sees the SNI field in the TLS handshake, which shows an in
```bash ```bash
# Clone the repository # Clone the repository
git clone https://github.com/masterking32/MasterHttpRelayVPN.git git clone -b python_testing https://github.com/masterking32/MasterHttpRelayVPN.git
cd domainfront-tunnel cd MasterHttpRelayVPN
# (Optional) Create a virtual environment # (Optional) Create a virtual environment
python -m venv venv python -m venv venv
@@ -41,7 +45,55 @@ pip install -r requirements.txt
> **Note:** Python 3.10+ is required. Core functionality has no external dependencies. The optional packages (`cryptography`, `h2`) enable MITM interception and HTTP/2 multiplexing for the `apps_script` mode. > **Note:** Python 3.10+ is required. Core functionality has no external dependencies. The optional packages (`cryptography`, `h2`) enable MITM interception and HTTP/2 multiplexing for the `apps_script` mode.
### 2. Configure ### 2. Deploy the Relay (Code.gs — Google Apps Script)
Before running the local proxy, you need a relay endpoint. The easiest (free) option is Google Apps Script using the included `Code.gs` file.
1. Go to [Google Apps Script](https://script.google.com/) and create a **New project**.
2. Delete the default code in the editor.
3. Copy the entire contents of the [`Code.gs`](Code.gs) file from this repository and paste it into the Apps Script editor.
4. **Change the `AUTH_KEY`** at the top of the script to your own strong secret:
```javascript
const AUTH_KEY = "your-strong-secret-key";
```
5. Click **Deploy → New deployment**.
6. Set the deployment type to **Web app**:
- **Execute as:** Me
- **Who has access:** Anyone
7. Click **Deploy** and copy the **Deployment ID** (not the URL).
8. Paste the Deployment ID into your `config.json` as the `script_id` value.
> **Important:** The `AUTH_KEY` in `Code.gs` must match the `auth_key` in your `config.json` exactly.
#### How Code.gs Works
`Code.gs` is a Google Apps Script relay that receives HTTP requests from the local proxy (via domain fronting) and forwards them to the actual target websites. It supports two modes:
| Mode | Request Format | Description |
|------|---------------|-------------|
| **Single** | `{ k, m, u, h, b, ct, r }` | Fetches one URL and returns `{ s, h, b }` (status, headers, body) |
| **Batch** | `{ k, q: [{m,u,h,b,ct,r}, ...] }` | Fetches multiple URLs **in parallel** using `UrlFetchApp.fetchAll()` and returns `{ q: [{s,h,b}, ...] }` |
**Request fields:**
- `k` — Auth key (must match `AUTH_KEY`)
- `m` — HTTP method (`GET`, `POST`, etc.)
- `u` — Target URL
- `h` — Request headers (object)
- `b` — Request body (base64-encoded)
- `ct` — Content-Type
- `r` — Follow redirects (`true`/`false`)
**Response fields:**
- `s` — HTTP status code
- `h` — Response headers
- `b` — Response body (base64-encoded)
- `e` — Error message (if any)
#### Updating Code.gs
When you update `Code.gs`, you must create a **new deployment** in Apps Script (Deploy → New deployment) and update the `script_id` in your `config.json` with the new Deployment ID. Editing the code alone does not update a live deployment.
### 3. Configure the Local Proxy
```bash ```bash
cp config.example.json config.json cp config.example.json config.json
@@ -63,13 +115,13 @@ Edit `config.json` with your values:
} }
``` ```
### 3. Run ### 4. Run
```bash ```bash
python main.py python main.py
``` ```
### 4. Configure Your Browser ### 5. Configure Your Browser
Set your browser's HTTP proxy to `127.0.0.1:8085` (or whatever `listen_host`:`listen_port` you configured). Set your browser's HTTP proxy to `127.0.0.1:8085` (or whatever `listen_host`:`listen_port` you configured).
@@ -82,13 +134,13 @@ For `apps_script` mode, you also need to install the generated CA certificate (`
| Field | Description | | Field | Description |
|-------|-------------| |-------|-------------|
| `mode` | One of: `apps_script`, `google_fronting`, `domain_fronting`, `custom_domain` | | `mode` | One of: `apps_script`, `google_fronting`, `domain_fronting`, `custom_domain` |
| `auth_key` | Shared secret between the proxy and your relay endpoint | | `auth_key` | Shared secret between the proxy and your relay endpoint (must match `AUTH_KEY` in `Code.gs`) |
### Mode-Specific Fields ### Mode-Specific Fields
| Field | Modes | Description | | Field | Modes | Description |
|-------|-------|-------------| |-------|-------|-------------|
| `script_id` | `apps_script` | Your deployed Apps Script ID (or array of IDs for load balancing) | | `script_id` | `apps_script` | Your deployed Apps Script Deployment ID (or array of IDs for load balancing) |
| `worker_host` | `domain_fronting`, `google_fronting` | Your Worker/Cloud Run hostname | | `worker_host` | `domain_fronting`, `google_fronting` | Your Worker/Cloud Run hostname |
| `custom_domain` | `custom_domain` | Your custom domain on Cloudflare | | `custom_domain` | `custom_domain` | Your custom domain on Cloudflare |
| `front_domain` | `domain_fronting`, `google_fronting`, `apps_script` | The domain shown in TLS SNI (default: `www.google.com`) | | `front_domain` | `domain_fronting`, `google_fronting`, `apps_script` | The domain shown in TLS SNI (default: `www.google.com`) |
@@ -151,19 +203,9 @@ python main.py --log-level DEBUG
DFT_AUTH_KEY=my-secret DFT_PORT=9090 python main.py DFT_AUTH_KEY=my-secret DFT_PORT=9090 python main.py
``` ```
## Apps Script Setup ## Multiple Script IDs (Load Balancing)
1. Go to [Google Apps Script](https://script.google.com/) and create a new project. For higher throughput, deploy multiple copies of `Code.gs` to separate Apps Script projects and use an array:
2. Paste your relay script code into `Code.gs`.
3. Deploy as a **Web App**:
- Execute as: **Me**
- Who has access: **Anyone**
4. Copy the **Deployment ID** and paste it into `config.json` as `script_id`.
5. Set a strong `auth_key` in both the Apps Script and `config.json`.
### Multiple Script IDs (Load Balancing)
For higher throughput, deploy multiple copies and use an array:
```json ```json
{ {
@@ -175,6 +217,8 @@ For higher throughput, deploy multiple copies and use an array:
} }
``` ```
The proxy will distribute requests across all IDs in round-robin fashion.
## Architecture ## Architecture
``` ```
@@ -186,7 +230,7 @@ For higher throughput, deploy multiple copies and use an array:
MITM (optional) Host: relay Return response MITM (optional) Host: relay Return response
``` ```
### Key Components ### Project Structure
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
@@ -196,12 +240,15 @@ For higher throughput, deploy multiple copies and use an array:
| `h2_transport.py` | HTTP/2 multiplexed transport (optional, for performance) | | `h2_transport.py` | HTTP/2 multiplexed transport (optional, for performance) |
| `mitm.py` | MITM certificate manager for HTTPS interception | | `mitm.py` | MITM certificate manager for HTTPS interception |
| `ws.py` | WebSocket frame encoder/decoder (RFC 6455) | | `ws.py` | WebSocket frame encoder/decoder (RFC 6455) |
| `Code.gs` | Google Apps Script relay — deploy this to Apps Script as your relay endpoint |
| `config.example.json` | Example configuration file — copy to `config.json` |
| `requirements.txt` | Python dependencies |
## Performance Features ## Performance Features
- **HTTP/2 multiplexing**: Single TLS connection handles 100+ concurrent requests - **HTTP/2 multiplexing**: Single TLS connection handles 100+ concurrent requests
- **Connection pooling**: Pre-warmed TLS connection pool with automatic maintenance - **Connection pooling**: Pre-warmed TLS connection pool with automatic maintenance
- **Request batching**: Groups concurrent requests into single relay calls - **Request batching**: Groups concurrent requests into single relay calls (uses batch mode in `Code.gs`)
- **Request coalescing**: Deduplicates identical concurrent GET requests - **Request coalescing**: Deduplicates identical concurrent GET requests
- **Parallel range downloads**: Splits large downloads into concurrent chunks - **Parallel range downloads**: Splits large downloads into concurrent chunks
- **Response caching**: LRU cache for static assets (configurable, 50 MB default) - **Response caching**: LRU cache for static assets (configurable, 50 MB default)
@@ -209,6 +256,7 @@ For higher throughput, deploy multiple copies and use an array:
## Security Notes ## Security Notes
- **Never commit `config.json`** — it contains your `auth_key`. The `.gitignore` excludes it. - **Never commit `config.json`** — it contains your `auth_key`. The `.gitignore` excludes it.
- **Change the default `AUTH_KEY` in `Code.gs`** before deploying — the default value is not secure.
- The `ca/` directory contains your generated CA private key. Keep it secure. - The `ca/` directory contains your generated CA private key. Keep it secure.
- Use a strong, unique `auth_key` to prevent unauthorized use of your relay. - Use a strong, unique `auth_key` to prevent unauthorized use of your relay.
- Set `listen_host` to `127.0.0.1` (not `0.0.0.0`) unless you need LAN access. - Set `listen_host` to `127.0.0.1` (not `0.0.0.0`) unless you need LAN access.