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
CronJob = type=cron, script-path=/path/to/cron.js, cron-expression=*/30 * * * *
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. Überschreitet der erfasste Body diese Größe, wird das Skript für diese Anfrage übersprungen (der Body wird nicht gekürzt) |
timeout |
Nein | 5,0 Sekunden | Ausführungs-Timeout pro Skript. Werte über 30 Sekunden werden auf 30 begrenzt |
argument |
Nein | — | Benutzerdefiniertes String-Argument, verfügbar als $argument in JS |
debug |
Nein | false | Reserviert; derzeit ohne Wirkung |
cron-expression |
Nein | — | Cron-Zeitplan-Ausdruck (nur für Cron-Typ) |
wake-system |
Nein | false | Reserviert; wird geparst, hat aber derzeit keine Wirkung |
enable |
Nein | true | Dieses Skript aktivieren oder deaktivieren |
script-update-interval |
Nein | 0 | Aktualisierungsabfrage für Remote-Skripte (siehe Ausführungsmodell) |
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) |
.bodyBytes |
Uint8Array oder null | Rohe Anfrage-Body-Bytes |
.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) |
.bodyBytes |
Uint8Array oder null | Rohe Antwort-Body-Bytes |
.rawBody |
Uint8Array oder null | Alias für .bodyBytes |
$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. url muss zusammen mit mindestens einem von status / headers / body zurückgegeben werden; ein Ergebnis, das nur url enthält, wird als Durchleitung behandelt.
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)
Die Schlüssel
server/serverswerden akzeptiert, aber derzeit ignoriert; die Auflösung fällt auf normales DNS zurück.
$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
$httpClientist inhttp-request-,http-response-,http-request-before-send-,cron- undevent-Skripten verfügbar. Inrule- unddns-Skripten ist es nicht verfügbar.
$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: null}
bssidist immernull;ssidwird nur auf iOS befüllt.Hinweis: In den aktuellen Versionen sind die Eigenschaften
$networkund$environmentaufgrund einer Bridging-Einschränkung für Skripte nicht sichtbar und werden alsundefinedgelesen.$script-Metadaten sind nur in Cron- und Event-Skripten verfügbar.
$environment — Laufzeitinformationen
$environment.system // "iOS" oder "macOS"
$environment.appVersion // KLNEKit-SDK-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 ist in jedem Skripttyp verfügbar.
$klne.getPolicyGroups() // Alle Richtliniengruppen abrufen
$klne.selectPolicy("Group", "Proxy") // Richtlinie für eine Gruppe wechseln
$klne.getActiveConnections() // Aktive Verbindungen auflisten
$klne.closeConnection("id") // Reserviert — derzeit ohne Funktion
$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
getPolicyGroups() gibt die auswählbaren Richtliniengruppen zurück:
{
count: 1, // Number — Anzahl der Gruppen
policyGroups: [{
name: "MainGroup", // String — Gruppenname
type: 0, // Number — 0 select, 1 url-test, 2 fallback, 3 ssid, 5 load-balance
policyNames: ["A", "B"], // Array of String — Kandidaten-Richtlinien
selectedIndex: 0, // Number — Index der aktiven Richtlinie (fehlt, wenn nicht ermittelbar)
selectedPolicy: "A" // String — Name der aktiven Richtlinie (fehlt, wenn nicht ermittelbar)
}]
}
getActiveConnections() gibt ein Array zurück, das die aktuellen Verbindungen beschreibt:
[{
id: "0x600002f01230", // String — opaker Verbindungsbezeichner
host: "example.com", // String — Zielhost (fehlt, wenn unbekannt)
port: 443 // Number — Zielport
}]
Die übrigen Methoden nehmen einfache Argumente entgegen und geben nichts zurück:
selectPolicy(group, policy)— machtpolicy(einen Namen aus denpolicyNamesder Gruppe) zur aktiven Richtlinie vongroup. Ein unbekannter Gruppen- oder Richtlinienname wird mit einer Warnung im Protokoll ignoriert.closeConnection(id)— reserviert. Der aktuelle Kernel kann einzelne Verbindungen nicht schließen; ein Aufruf protokolliert nur eine Warnung und schließt nichts.flushDNS()— leert den DNS-Cache.startURLTest(group)— startet einen asynchronen Latenztest für eineurl-test- oderfallback-Gruppe; andere Gruppentypen und unbekannte Namen werden mit einer Warnung ignoriert.reloadConfiguration()— lädt die aktuelle Konfiguration neu.setOutboundMode(mode)—"global"und"proxy"leiten beide den gesamten Verkehr über den Proxy,"direct"sendet den gesamten Verkehr direkt, und jeder andere Wert wählt den Regelmodus.setHTTPCaptureEnabled(enabled)— aktiviert oder deaktiviert die HTTPS-Entschlüsselung (MitM) zur Laufzeit; nimmt einen Boolean entgegen.
$script — Skript-Metadaten
$script.name // Skriptname aus der Konfiguration
$script.type // Skripttyp-String
$script.startTime // Monotoner Zeitstempel (Sekunden seit System-Boot-Referenz, nicht Epoche)
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, event.
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") // Warnprotokoll, als JavaScript-Fehler gekennzeichnet
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
$scriptImport(subScriptPath) — Subskript-Loader
Lädt und wertet eine andere JavaScript-Datei aus. Es werden nur lokale Dateipfade unterstützt; http(s)://- und file://-URLs werden abgelehnt. ($script ist für das Skript-Metadatenobjekt reserviert.)
$scriptImport("/path/to/helper.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]
HalfHourlyTask = type=cron, script-path=task.js, cron-expression=*/30 * * * *
Nur das Minutenfeld des Ausdrucks wird berücksichtigt: */N läuft alle N Minuten. Jeder andere Ausdruck wird alle 60 Sekunden ausgelöst, und das Skript muss die aktuelle Zeit selbst prüfen, um zu entscheiden, ob es handeln soll.
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 nach einem Netzwerkwechsel die Konnektivität prüft und die Richtliniengruppe entsprechend umschaltet:
[Script]
NetSwitch = type=event, script-path=network-switch.js
// network-switch.js
$httpClient.head("https://www.google.com/generate_204", {timeout: 5},
function(error, response, data) {
if (error) {
// Probe im neuen Netzwerk fehlgeschlagen — zur Backup-Gruppe wechseln
$klne.selectPolicy("MainGroup", "BackupProxy")
console.log("Probe failed after " + $event.name)
} else {
$klne.selectPolicy("MainGroup", "MainProxy")
}
$done({})
})
Hinweis: Das Auslesen der Wi-Fi-SSID über
$networkwäre hier die natürliche Lösung, aber$networkist für Skripte noch nicht sichtbar (siehe den Hinweis unter $network); in den aktuellen Versionen funktioniert stattdessen das Probing über$httpClient.
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({})
}
)
Rufen Sie
$done({})innerhalb des Callbacks auf — der Abschluss des Skripts bricht alle ausstehenden$httpClient-Anfragen ab, sodass ein synchrones$done()am Skriptende die Zustandsprüfung abbrechen würde, bevor die Antwort eintrifft.
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 Pool von 3 vorgewärmten JSContexts wird vorgehalten; ein Kontext wird nach jeder Ausführung verworfen und durch einen frischen ersetzt, sodass globale Variablen niemals zwischen Läufen durchsickern.
- Auf iOS/tvOS unterliegen Skripte einer Speicherbegrenzung von 10 MB; auf macOS 512 MB.
- Remote-Skripte (HTTP/HTTPS-Pfade) werden bei jeder Ausführung abgerufen. Wenn
script-update-intervalungleich null ist, fragt Chute die URL zusätzlich alle 10 Minuten mit einer bedingten HEAD-Anfrage ab;0(der Standard) deaktiviert diese Abfrage.
Modulskript-Integration
Skripte können auch in Modul-Dateien (.sgmodule) unter dem Abschnitt [Script] definiert werden.
Diese Seite ist eine Übersetzung der englischen Version. Bei Abweichungen ist die englische Version maßgeblich.