JavaScript-Scripting
Chute unterstützt JavaScript-Scripting für erweiterte Anfrage-/Antwortmodifikation, benutzerdefinierte Regelabgleiche, DNS-Auflösung und geplante Aufgaben. Skripte verwenden die JavaScriptCore-Engine von Apple und folgen der Surge-kompatiblen Skript-API.
Skripte werden im Abschnitt [Script] der Konfigurationsdatei definiert.
Konfiguration
[Script]
MyScript = type=http-request, script-path=/path/to/script.js, pattern=^https?://example\.com, requires-body=true, max-size=262144, timeout=10, argument=myArg, debug=true
CronJob = type=cron, script-path=/path/to/cron.js, cron-expression=* * * * *, wake-system=true
Skriptparameter
| Parameter | Erforderlich | Standard | Beschreibung |
|---|---|---|---|
type |
Ja | http-request |
Skript-Trigger-Typ (siehe unten) |
script-path |
Ja | — | Lokaler Dateipfad oder HTTP(S)-URL zum JS-Skript |
pattern |
Nein | (alle) | URL-Regex-Muster zum Filtern, wann das Skript ausgelöst wird |
requires-body |
Nein | auto-detect | Erzwingt, dass das Skript den vollständigen Anfrage-/Antwort-Body erhält |
max-size |
Nein | 131072 (128KB) | Maximale Body-Größe in Bytes für Body-zugreifende Skripte |
timeout |
Nein | 5,0 Sekunden | Ausführungs-Timeout pro Skript |
argument |
Nein | — | Benutzerdefiniertes String-Argument, verfügbar als $argument in JS |
debug |
Nein | false | Kompilierungs-Cache für Debugging überspringen |
cron-expression |
Nein | — | Cron-Zeitplan-Ausdruck (nur für Cron-Typ) |
wake-system |
Nein | false | System aufwecken, um Cron-Skripte auszuführen (nur iOS) |
enable |
Nein | true | Dieses Skript aktivieren oder deaktivieren |
script-update-interval |
Nein | 0 | Sekunden zwischen Remote-Skript-Aktualisierungen (-1 = nie, 0 = nur beim Start) |
Skripttypen
| Typ-String | Enum | Beschreibung |
|---|---|---|
http-request |
HTTP Request | HTTP-Anfragen vor dem Upstream abfangen und ändern |
http-response |
HTTP Response | HTTP-Antworten vor dem Client abfangen und ändern |
http-request-before-send |
HTTP Request Before Send | Anfrage nach vollständiger Body-Erfassung vor dem Senden ändern |
rule |
Rule | Benutzerdefinierte Regelabgleich-Logik |
dns |
DNS | Benutzerdefinierte DNS-Auflösung |
cron |
Cron | Geplante/zeitgesteuerte Skripte |
event |
Event | Systemereignishandler (z.B. network-changed) |
Body-Autoerkennung: Wenn der Skriptquelltext
$request.bodyoder$response.bodyenthält, wird der Body automatisch bis zumax-sizebereitgestellt. Verwenden Sierequires-body=true, um dieses Verhalten zu erzwingen.
JavaScript-API-Referenz
Skripte laufen in einer sandboxed JavaScriptCore-Umgebung mit den folgenden verfügbaren globalen Objekten.
$request (Schreibgeschützt)
Verfügbar in: http-request, http-response, http-request-before-send, rule
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
.url |
String | Vollständige Anfrage-URL |
.method |
String | HTTP-Methode (GET, POST usw.) oder QUERY für DNS |
.headers |
Object | Anfrage-Header als Schlüssel-Wert-Paare |
.body |
String oder null | Anfrage-Body (UTF-8 dekodiert) |
.hostname |
String | Ziel-Hostname |
.destPort |
Number | Zielport |
.processPath |
String | Pfad des anfragenden Prozesses (nur macOS) |
.userAgent |
String | Wert des User-Agent-Headers |
.sourceIP |
String | Quell-IP-Adresse |
.listenPort |
Number | Proxy-Lauschport |
.requestId |
String | Eindeutige Anfrage-ID |
.dnsResult |
String | Aufgelöste IP-Adresse |
.srcPort |
Number | Quellport |
.protocol |
String | Erkanntes Protokoll: http, https, tcp, dns |
$response (Schreibgeschützt)
Verfügbar in: http-response
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
.status |
Number | HTTP-Statuscode |
.headers |
Object | Antwort-Header als Schlüssel-Wert-Paare |
.body |
String oder null | Antwort-Body (UTF-8 dekodiert) |
$done(value) — Abschluss-Handler
Muss genau einmal am Ende des Skripts aufgerufen werden, um den Abschluss zu signalisieren. Die Skriptausführung blockiert, bis $done() aufgerufen wird oder das Timeout abläuft.
$done({}) // Durchleitung — keine Änderungen
$done() // Verbindung abbrechen
$done({matched: true}) // Regelabgleich-Ergebnis (nur Regel-Skripte)
$done({address: "1.2.3.4"}) // DNS-Ergebnis (nur DNS-Skripte)
Rückgabewerte für HTTP-Request-Skripte:
$done({
url: "https://new.example.com/path", // URL umschreiben
headers: {"X-Custom": "value"}, // Header ändern
body: "new request body", // Body ändern
response: { // Synthetische Antwort zurückgeben (Upstream überspringen)
status: 200,
headers: {"Content-Type": "text/html"},
body: "<html>Blocked</html>"
}
})
Wenn response angegeben ist, wird die Anfrage kurzgeschlossen: Chute gibt die synthetische Antwort direkt an den Client zurück, ohne den Upstream-Server zu kontaktieren. Dies ist nützlich zum Blockieren, Mocken von APIs oder Zurückgeben von zwischengespeicherten Inhalten.
Rückgabewerte für HTTP-Response-Skripte:
$done({
status: 200, // Statuscode ändern
headers: {"X-Custom": "value"}, // Antwort-Header ändern
body: "new response body", // Antwort-Body ändern
url: "https://other.example.com" // 302-Weiterleitung auslösen
})
Wenn url angegeben ist, gibt Chute eine 302-Weiterleitung zur angegebenen URL anstelle der ursprünglichen Antwort zurück.
Rückgabewerte für DNS-Skripte:
$done({address: "1.2.3.4"}) // Einzelne IP
$done({addresses: ["1.2.3.4", "5.6.7.8"]}) // Mehrere IPs
$done({address: "10.0.0.1", ttl: 300}) // Mit benutzerdefinierter TTL (Sekunden, Standard 60)
$done({server: "8.8.8.8"}) // An bestimmten DNS-Server weiterleiten
$httpClient — Asynchroner HTTP-Client
Führen Sie HTTP-Anfragen aus Skripten heraus aus. Alle Anfragen werden bei $done() oder Timeout abgebrochen.
$httpClient.get(url, function(error, response, data) {
if (error) {
console.log("Anfrage fehlgeschlagen: " + error)
} else {
console.log("Status: " + response.status)
console.log("Antwort: " + data)
}
})
$httpClient.post(url, {headers: {...}, body: "...", timeout: 5}, callback)
$httpClient.put(url, options, callback)
$httpClient.del(url, options, callback)
$httpClient.head(url, options, callback)
$httpClient.options(url, options, callback)
$httpClient.patch(url, options, callback)
Callback-Signatur: callback(error, response, data)
error: Fehler-String odernullresponse:{status: Number, headers: Object}odernulldata: UTF-8-String-Antwort-Body odernull
$persistentStore — Schlüssel-Wert-Speicher
Persistenter Schlüssel-Wert-Speicher, der Skript- und Prozessneustarts überdauert. Unterstützt durch NSUserDefaults.
$persistentStore.write(data, key) // Einen Wert speichern
$persistentStore.read(key) // Einen Wert abrufen
$persistentStore.remove(key) // Einen Wert entfernen
$notification — Lokale Benachrichtigungen
Lokale Systembenachrichtigungen senden.
$notification.post("Titel", "Untertitel", "Benachrichtigungstext")
$network — Netzwerkinformationen
Schreibgeschützte Netzwerkzustandsinformationen.
$network.dns // Array von DNS-Server-IPs
$network.wifi // {ssid: "WiFiName", bssid: "aa:bb:cc:dd:ee:ff"}
$environment — Laufzeitinformationen
$environment.system // "iOS" oder "macOS"
$environment.appVersion // App-Versionsstring
$utils — Hilfsfunktionen
$utils.geoip("1.2.3.4") // Ländercode (z.B. "US")
$utils.ipasn("1.2.3.4") // ASN-Nummer (z.B. "13335")
$utils.ungzip(data) // Gzip-Daten dekomprimieren
$klne — Proxy-Steuerungs-API
Steuern Sie die Proxy-Laufzeit aus Skripten.
$klne.policyGroups // Alle Richtliniengruppen abrufen
$klne.selectPolicy("Group", "Proxy") // Richtlinie für eine Gruppe wechseln
$klne.getActiveConnections() // Aktive Verbindungen auflisten
$klne.closeConnection("id") // Eine Verbindung schließen
$klne.flushDNS() // DNS-Cache leeren
$klne.startURLTest("Group") // URL-Test für eine Gruppe auslösen
$klne.reloadConfiguration() // Gesamte Konfiguration neu laden
$klne.setOutboundMode("rule") // Modus setzen: "global", "proxy", "direct", "rule"
$klne.setHTTPCaptureEnabled(true) // MITM aktivieren/deaktivieren
$script — Skript-Metadaten
$script.name // Skriptname aus der Konfiguration
$script.type // Skripttyp-String
$script.startTime // Epochen-Zeitstempel
Globale Variablen pro Ausführung
Die folgenden Variablen werden pro Skriptausführung injiziert und sind spezifisch für bestimmte Skripttypen.
$argument — Skript-Argument
Der String-Wert aus dem Parameter argument= in der Skriptkonfiguration. Verfügbar in: http-request, http-response, http-request-before-send, rule, dns, cron.
console.log("Argument: " + $argument)
$domain — DNS-Domain (Nur DNS-Skript)
Der abgefragte Domainname. Nur in dns-Skripten verfügbar.
var domain = $domain // z.B. "example.com"
$cronexp — Cron-Ausdruck (Nur Cron-Skript)
Der Cron-Zeitplan-Ausdruck aus der Skriptkonfiguration. Nur in cron-Skripten verfügbar.
console.log("Zeitplan: " + $cronexp) // z.B. "*/30 * * * *"
$event — Ereignisinformationen (Nur Event-Skript)
Informationen über das auslösende Ereignis. Derzeit wird nur network-changed unterstützt.
console.log("Ereignis: " + $event.name) // "network-changed"
console — Protokollierung
console.log("Debug-Nachricht") // Ausführliche Protokollierung
console.warn("Warnmeldung") // Warnprotokoll
console.error("Fehlermeldung") // Fehlerprotokoll mit [JS-ERROR]-Präfix
setTimeout(fn, seconds) — Timer
Plant die Ausführung einer Funktion nach einer Verzögerung.
setTimeout(function() {
console.log("Verzögerte Ausführung")
}, 2.5) // 2,5 Sekunden
$script(subScriptPath) — Subskript-Loader
Lädt und wertet eine andere JavaScript-Datei aus.
$script("/path/to/helper.js")
$script("https://example.com/remote-script.js")
// Daten zwischen Skripten mit $persistentStore übergeben
Details zu Skripttypen
HTTP-Request-Skript
Wird ausgeführt, wenn Anfrage-Header empfangen werden. Kann URL, Header und Body ändern, bevor die Anfrage weitergeleitet wird.
[Script]
ModifyHeaders = type=http-request, script-path=modify.js, pattern=^https://api\.example\.com
HTTP-Response-Skript
Wird ausgeführt, wenn Antwort-Header empfangen werden. Kann Status, Header und Body ändern, bevor sie an den Client zurückgegeben werden.
[Script]
ModifyResponse = type=http-response, script-path=response.js, pattern=^https://api\.example\.com
HTTP-Request-Before-Send-Skript
Wird ausgeführt, nachdem der vollständige Anfrage-Body erfasst wurde, kurz vor dem Senden an den Upstream. Nützlich zum Ändern von POST/PUT-Anfrage-Bodys.
[Script]
BeforeSend = type=http-request-before-send, script-path=before-send.js, pattern=^https://api\.example\.com, requires-body=true
Regel-Skript
Benutzerdefinierter Regelabgleich. Das Skript muss $done({matched: true}) oder $done({matched: false}) aufrufen.
[Rule]
SCRIPT,MyRuleScript,DIRECT
[Script]
MyRuleScript = type=rule, script-path=rule.js
DNS-Skript
Benutzerdefinierte DNS-Auflösung. Erhält $domain und gibt aufgelöste Adresse(n) zurück.
// dns.js
var domain = $domain
if (domain === "internal.example.com") {
$done({address: "10.0.0.1", ttl: 300})
} else {
$done({}) // Durchleitung zur normalen DNS-Auflösung
}
Cron-Skript
Geplante Ausführung mit Cron-Ausdrücken. Das Mindestintervall beträgt 60 Sekunden.
[Script]
HourlyTask = type=cron, script-path=hourly.js, cron-expression=0 * * * *
Vereinfachte Cron-Syntax wird unterstützt (z.B. */30 * * * * für alle 30 Minuten, durch Kommas getrennte Werte).
Event-Skript
Durch Systemereignisse ausgelöst. Unterstützt derzeit das Ereignis network-changed (wird ausgelöst, wenn sich das Wi-Fi- oder Mobilfunknetz ändert).
[Script]
NetChange = type=event, script-path=network-changed.js
Das Objekt $event ist verfügbar:
$event.name // "network-changed"
Praxisbeispiele
Mobile Geräte umleiten
Ein http-request-Skript, das mobile Benutzer basierend auf dem User-Agent umleitet:
[Script]
MobileRedirect = type=http-request, script-path=mobile-redirect.js, pattern=^https://example\\.com
// mobile-redirect.js
var ua = $request.headers["User-Agent"] || ""
if (/Mobile|Android|iPhone/.test(ua)) {
$done({
response: {
status: 302,
headers: {"Location": "https://m.example.com" + $request.url.replace(/.*example\\.com/, "")},
body: ""
}
})
} else {
$done({})
}
Inhalte in API-Antworten blockieren
Ein http-response-Skript, das Werbung und gesponserte Inhalte aus einer JSON-API-Antwort entfernt:
[Script]
RemoveAds = type=http-response, script-path=remove-ads.js, pattern=^https://api\\.example\\.com/feed, requires-body=true
// remove-ads.js
var body = JSON.parse($response.body)
if (body.ads) {
delete body.ads
}
if (body.recommendations) {
body.recommendations = body.recommendations.filter(function(r) {
return !r.sponsored
})
}
$done({body: JSON.stringify(body)})
Anfrage-Body vor dem Senden ändern
Ein http-request-before-send-Skript, das eine POST-Nutzlast bereinigt:
[Script]
SanitizePayload = type=http-request-before-send, script-path=sanitize.js, pattern=^https://api\\.example\\.com/submit, requires-body=true
// sanitize.js
var body = JSON.parse($request.body)
body.clientSecret = "[REDACTED]"
body.timestamp = Math.floor(Date.now() / 1000)
$done({body: JSON.stringify(body)})
Benutzerdefinierte Regel: Zeitbasiertes Routing
Ein rule-Skript, das je nach Tageszeit einen anderen Proxy auswählt:
[Rule]
SCRIPT,TimeBasedRule,ProxyA
[Script]
TimeBasedRule = type=rule, script-path=time-rule.js
// time-rule.js
var hour = new Date().getHours()
if (hour >= 9 && hour < 18) {
$done({matched: false}) // Während der Arbeitszeit zur nächsten Regel durchfallen
} else {
$done({matched: true}) // ProxyA außerhalb der Arbeitszeit verwenden
}
Benutzerdefiniertes DNS für interne Domains
Ein dns-Skript, das interne Hostnamen zu lokalen IPs auflöst:
[Script]
InternalDNS = type=dns, script-path=internal-dns.js
// internal-dns.js
var internalHosts = {
"gitlab.local": "10.0.0.10",
"registry.local": "10.0.0.11",
"monitor.local": "10.0.0.12"
}
if (internalHosts[$domain]) {
$done({address: internalHosts[$domain], ttl: 3600})
} else {
$done({}) // Durchleitung zur normalen DNS-Auflösung
}
Richtlinie bei Netzwerkwechsel automatisch umschalten
Ein event-Skript, das bei Mobilfunkverbindung zu einer konservativen Richtliniengruppe wechselt:
[Script]
NetSwitch = type=event, script-path=network-switch.js
// network-switch.js
if (!$network.wifi.ssid) {
// Bei Mobilfunk — Gruppe mit niedrigem Datenverbrauch verwenden
$klne.selectPolicy("MainGroup", "LowDataProxy")
console.log("Auf Mobilfunkprofil umgeschaltet")
} else if ($network.wifi.ssid === "Office") {
$klne.selectPolicy("MainGroup", "DIRECT")
console.log("Auf Büroprofil umgeschaltet")
}
Periodische Zustandsprüfung
Ein cron-Skript, das alle 30 Minuten die Proxy-Gesundheit prüft:
[Script]
HealthCheck = type=cron, script-path=health-check.js, cron-expression=*/30 * * * *
// health-check.js
$httpClient.head("https://www.google.com/generate_204", {timeout: 10},
function(error, response, data) {
if (error || response.status !== 204) {
console.error("Zustandsprüfung fehlgeschlagen: " + (error || "Status " + response.status))
$notification.post("Chute-Alarm", "Zustandsprüfung", "Google nicht erreichbar")
} else {
console.log("Zustandsprüfung OK")
}
}
)
$done()
API-Antworten mit externen Daten anreichern
Ein http-response-Skript, das Benutzerdaten durch Aufruf einer sekundären API anreichert:
[Script]
EnrichUsers = type=http-response, script-path=enrich.js, pattern=^https://api\\.example\\.com/users, requires-body=true
// enrich.js
var users = JSON.parse($response.body)
var pending = users.length
if (pending === 0) { $done({}) }
users.forEach(function(user, index) {
$httpClient.get("https://internal-api.example.com/avatar/" + user.id,
function(error, resp, data) {
if (!error && resp.status === 200) {
users[index].avatar = JSON.parse(data).url
}
pending--
if (pending === 0) {
$done({body: JSON.stringify(users)})
}
}
)
})
Ausführungsmodell
- Alle Skripte laufen in einer dedizierten seriellen Warteschlange für Thread-Sicherheit.
- Jede Skriptausführung hat ein pro-Skript-Timeout; wenn
$done()nicht innerhalb des Timeouts aufgerufen wird, wird das Skript als Durchleitung behandelt. - Ein JSContext-Pool (Größe 3) wird für die Leistung verwendet; Kontexte werden über Ausführungen hinweg wiederverwendet.
- Die Skriptkompilierung wird standardmäßig zwischengespeichert; verwenden Sie
debug=true, um den Cache zu umgehen. - Auf iOS/tvOS unterliegen Skripte einer Speicherbegrenzung von 10 MB; auf macOS 512 MB.
- Remote-Skripte (HTTP/HTTPS-Pfade) werden beim Start abgerufen und optional nach
script-update-intervalSekunden erneut abgerufen.
Modulskript-Integration
Skripte können auch in Modul-Dateien (.sgmodule) unter dem Abschnitt [Script] definiert werden.
```