Mocks und Fehlerinjektion
Verkehr zu beobachten beantwortet, was eine App tut. Diese Seite handelt von der anderen Hälfte: das Netzwerk etwas antworten zu lassen, was es nie geantwortet hätte, um zu sehen, was die App damit anfängt. Ein Backend, das es noch nicht gibt, ein Endpunkt, der 500 liefert, eine Antwort, die acht Sekunden braucht, eine API, die einfach verschwunden ist.
Alles hier funktioniert auf jeder Plattform, auf der Chute läuft, und alles, was HTTPS berührt, verlangt zuerst Entschlüsselung für diesen Host — eine verschlüsselte Anfrage, die Chute nicht lesen kann, kann es auch nicht beantworten.
Was Chute simulieren kann und was nicht
Chute greift an der Verbindung und an der HTTP-Nachricht ein. Einen Traffic-Shaper hat es nicht:
| Kann | Einen vorgefertigten Body liefern, einen gewählten Statuscode liefern, vor Anfrage oder Antwort eine feste Verzögerung einlegen, eine Verbindung rundheraus verweigern, HTTP/3-Clients zurück auf TCP zwingen, eine Anfrage an einen anderen Host schicken als den verlangten |
| Kann nicht | Bandbreite begrenzen, Pakete verwerfen oder umsortieren, Jitter hinzufügen, eine Verbindung mittendrin verschlechtern oder eine bestimmte RTT auf Transportebene nachbilden |
Es gibt keinen [Throttle]-Abschnitt und nirgends eine Geschwindigkeitsbegrenzung. Wenn Sie eine langsame Leitung brauchen statt einer langsamen Antwort, gehört das zu einem Netzwerk-Conditioner (Apples Network Link Conditioner oder ein Router), nicht zu Chute.
Einen Mechanismus wählen
| Zu simulieren | Mittel | Wo |
|---|---|---|
| Ein Antwort-Body, den es noch nicht gibt | [Map Local] |
Mock-Antwort |
| Genau 503 | [URL Rewrite] … reject |
URL-Umschreibung |
| Ein leeres 200, ein leeres Bild, ein leeres JSON-Objekt oder -Array | reject-200, reject-img / reject-tinygif, reject-dict, reject-array |
URL-Umschreibung |
| Jeder andere Status — 401, 429, 500 | [Map Local] mit status-code= oder ein http-request-Skript |
Mock-Antwort, JavaScript-Scripting |
| Latenz | Ein http-request- oder http-response-Skript |
JavaScript-Scripting |
| Ein Endpunkt, der schlicht nicht erreichbar ist | Eine REJECT-Regel |
Integrierte Richtlinie |
| Eine Anfrage, die hängt, bis der Client aufgibt | Eine REJECT-DROP-Regel |
Integrierte Richtlinie |
| Ein Client, der nicht auf TCP zurückfällt | block-quic |
Verschiedene Optionen |
| Ein anderes Backend hinter derselben URL | [Host] oder [URL Rewrite] im header-Modus |
Lokale DNS-Zuordnung |
Ein vorgefertigter Antwort-Body
[Map Local] beantwortet eine passende Anfrage aus einer Datei oder aus eingebetteten Daten. Bei einem entschlüsselten HTTP/2-Stream wird der echte Server gar nicht gefragt; bei HTTP/1.1 geht die Anfrage trotzdem hinaus, und nur die Antwort wird ersetzt:
[Map Local]
^https://api\.example\.com/v1/profile.* data="/Users/me/mocks/profile.json"
^https://api\.example\.com/v1/flags.* base64="eyJiZXRhIjogdHJ1ZX0="
Drei Dinge entscheiden, ob das funktioniert:
- Der reguläre Ausdruck passt an beliebiger Stelle der URL, wie bei der URL-Umschreibung:
^https://api\.example\.com/v1/profiledeckt den Pfad und jeden folgenden Query-String bereits ab, ein abschließendes.*ist nicht nötig. data=liest das Gerät, auf dem Chute läuft. Auf einem Mac ist das praktisch — Datei ändern, und die nächste Anfrage sieht die Änderung. Auf einem Telefon oder einem Apple TV bedeutet ein Pfad von Ihrem Mac nichts; nehmen Sie dortbase64=, oder liefern Sie die Datei über HTTP aus und nutzen Sie die URL-Umschreibung.- Der Status ist
200 OK, sofern Sie nichtstatus-code=setzen, und nach der Antwort wird die Verbindung geschlossen. Antwort-Header gehören inheader="Key: value|…".
Der Body unterstützt die Vorlagenvariablen {{ "{{url}}" }}, {{ "{{host}}" }}, {{ "{{path}}" }}, {{ "{{method}}" }} und {{ "{{ua}}" }}, was für einen Mock reicht, der zurückspiegelt, wonach gefragt wurde.
Ein Fehlerstatus
Für 503 braucht es kein Skript — eine URL-Umschreibung im reject-Modus liefert HTTP/1.1 503:
[URL Rewrite]
^https://api\.example\.com/v1/orders.* _ reject
Die verwandten Modi decken die übrigen Formen von „nichts Brauchbares“ ab: reject-200 (200 mit leerem Body), reject-img und reject-tinygif (ein 1×1-GIF), reject-dict ({} als JSON, 200), reject-array ([] als JSON, 200). Alle greifen bei HTTPS nur, wenn dieser Host gerade entschlüsselt wird.
Für jeden anderen Statuscode ist eine Mock-Regel mit status-code= der kürzeste Weg:
[Map Local]
^https://api\.example\.com/v1/orders data-type=text data="rate limited" status-code=429 header="Retry-After: 30"
Muss die Antwort von der Anfrage abhängen, beantwortet stattdessen ein http-request-Skript die Anfrage direkt, ohne den Server zu kontaktieren:
[Script]
Fail429 = type=http-request, script-path=/Users/me/mocks/fail429.js, pattern=^https://api\.example\.com/v1/orders
// fail429.js — antworten, ohne den Server zu kontaktieren
$done({
response: {
status: 429,
headers: {
"Content-Type": "application/json",
"Retry-After": "30"
},
body: JSON.stringify({ error: "rate_limited" })
}
})
Das pattern eines Skripts passt an beliebiger Stelle der URL, wie bei den Umschreibungsfamilien — ein Präfix wie ^https://api\.example\.com/v1/orders genügt, ein abschließendes .* ist nicht nötig.
Auf HTTP/1.1 trägt die Statuszeile die Standard-Statusmeldung (Reason Phrase) des Codes (
HTTP/1.1 429 Too Many Requests); ein Code, dem das Register keinen Namen gibt, etwa 599, erhält keine (HTTP/1.1 599).
Latenz
Ein Skript, das einen Timer anmeldet, bleibt am Leben, bis es $done() aufruft — der Timer ist also eine Verzögerung. (Ein Skript, das nichts anmeldet, wartet nicht: Es lässt die Nachricht durch, sobald es zurückkehrt.)
[Script]
SlowAPI = type=http-response, script-path=/Users/me/mocks/slow.js, pattern=^https://api\.example\.com/v1/, timeout=15
// slow.js — die echte Antwort acht Sekunden zu spät zurückgeben
setTimeout(function () {
$done({})
}, 8)
Das Budget ist das timeout des Skripts selbst: standardmäßig 5 Sekunden, alles über 30 wird auf 30 gekappt. Ein Skript, das bei Ablauf des Timeouts kein $done() gerufen hat, gilt als durchgereicht (Passthrough) — die Nachricht läuft unverändert weiter —, eine Verzögerung länger als das Timeout scheitert also nicht laut, sie verzögert nur nicht mehr. Setzen Sie timeout über die gewünschte Verzögerung, wie im Beispiel.
type=http-request verzögert, bevor der Server kontaktiert wird (die App sieht einen langsamen Roundtrip), type=http-response danach (der Server war schnell, die App wartet trotzdem).
Ein Endpunkt, der einfach weg ist
Ein Mock ersetzt eine Antwort; eine REJECT-Regel verweigert die Verbindung. Sie wirkt auf Verbindungsebene, deckt damit jedes Protokoll ab statt nur HTTP, und braucht keine Entschlüsselung:
[Rule]
DOMAIN-SUFFIX,api.example.com,REJECT
Die REJECT-Varianten antworten unterschiedlich, und das ist auch hier nützlich. REJECT-DROP antwortet nie, der Client wartet also, bis er in ein Timeout läuft — so sieht ein toter Server aus. REJECT-200, REJECT-DICT, REJECT-ARRAY, REJECT-TINYGIF / REJECT-IMG und REJECT-VIDEO beantworten eine unverschlüsselte oder entschlüsselte HTTP/1-Anfrage mit einem synthetischen 200 (leer, {}, [], ein 1×1-GIF, ein leeres MP4). REJECT, REJECT-TINYGIF und REJECT-IMG werden für 30 Sekunden zu REJECT-DROP, sobald ein Host mehr als 50-mal in 30 Sekunden abgelehnt wurde; REJECT-NO-DROP und die anderen Varianten nie. Siehe Integrierte Richtlinie. Für HTTP-Anfragen ersetzt show-error-page-for-reject = true die nackte Ablehnung durch eine lesbare Fehlerseite, sodass im Browser sofort klar ist, dass die Blockade Ihre war.
So prüfen Sie auch, ob es überhaupt einen Ausweichpfad gibt — verweigern Sie den primären Host und sehen Sie, ob die App nach dem sekundären greift oder nur einen Ladekreis zeigt.
Einen Client von HTTP/3 herunterholen
QUIC läuft über UDP und Chute kann es nicht entschlüsseln, eine App auf HTTP/3 ist für jeden Mechanismus dieser Seite also unsichtbar. Verweigert man ihre QUIC-Flüsse, versuchen es kompatible Clients erneut über TCP, wo all das funktioniert:
[General]
block-quic = on
auto verweigert QUIC nur, wenn der Fluss zu einem Proxy geht; on verweigert überall, DIRECT eingeschlossen. Für Verkehr, der durch TUN kommt, antwortet Chute auf einen verweigerten QUIC-Fluss mit ICMP Port Unreachable, sodass der Client sofort zurückfällt, statt ein Timeout abzuwarten.
Die Anfrage woandershin schicken
Zwei Wege auf zwei Ebenen:
[Host]
api.example.com = 10.0.0.5
Eine [Host]-Zuordnung beantwortet die DNS-Auflösung mit einer Adresse Ihrer Wahl — einer Staging-Maschine, oder einer Adresse, die nirgendwohin führt, wenn Sie eine Verbindung wollen, die in ein Timeout läuft statt abgewiesen zu werden. Sie gilt für jedes Protokoll und braucht keine Entschlüsselung. Leeren Sie danach den DNS-Cache.
[URL Rewrite]
^https://api\.example\.com/v1/(.*) https://staging.example.com/v1/$1 header
Der header-Modus schreibt die Anfrage an Ort und Stelle um und passt den Host-Header entsprechend an. Das ist HTTP-Ebene und braucht bei HTTPS die Entschlüsselung. Der Client erfährt von der Umschreibung nichts, solange die Anfrage ihren Host behält. Eine entschlüsselte HTTPS-Anfrage ist an den Host gebunden, an den sie gesendet wurde, daher wird eine Regel, die einen anderen Host nennt — wie die obige —, mit einem 307 auf die neue URL beantwortet, dem der Client selbst folgt; genauso antwortet Chute überall sonst, wo sich das Ziel nicht an Ort und Stelle umschreiben lässt.
Bestätigen, dass es gegriffen hat
Eine Regel, die nie passt, sieht genauso aus wie eine, die passte und nichts tat — das ist der typische Fehlerfall für alles auf dieser Seite.
- Umschreibungen und Mocks: Die Seite Regeln der Web-Konsole listet jede Regel für URL-Umschreibung, Header-Umschreibung, Body-Umschreibung und Mock-Antwort, die in diesem Lauf gegriffen hat, mit Zähler. Nicht in der Liste heißt nie gepasst. Dieselben Daten liegen in
rewrite_hitsvonGET /api/rules. - Pro Verbindung: Öffnen Sie die Verbindung in der Konsole und lesen Sie ihre Zeilen Angewendete Umschreibungen, die die Regel in ihren eigenen Worten nennen. In Dashboard zählt der Tab Allgemein der Verbindung sie als Ausgelöst unter Regeln und nennt sie in der Liste Ausgelöste Regeln.
- Skripte tauchen in dieser Tabelle nicht auf. Der Beleg eines Skripts ist seine eigene Ausgabe:
console.log-Zeilen landen im Protokoll, lesbar auf der Seite Protokolle der Konsole oder mitGET /api/logs. Sie werden auf der Stufe verbose geschrieben, heben Sie also zuerstloglevelaufverbosean —PUT /api/loglevelerledigt das ohne Neuladen;console.warnundconsole.errorerscheinen auf der Standardstufe. Jede Zeile wird auf 256 Zeichen gekürzt, Zugangsdaten werden maskiert.
Aufräumen
Regeln, die aus der Konsole, aus Dashboard oder über POST /api/rewrites/:family hinzugefügt wurden, leben in der laufenden Engine und sind beim nächsten Neustart weg — ideal für ein Experiment und ein schlechter Ort für etwas, auf das Sie sich verlassen. Regeln in der Konfigurationsdatei überstehen Neustarts, was sie zu einem guten Ort für einen Mock macht und zu etwas, das man auf keinen Fall vergessen darf: Eine [Map Local]-Zeile, die in einer Konfiguration stehen bleibt, beantwortet Wochen später immer noch Anfragen — und sieht dabei genau aus wie ein kaputter Server.
Diese Seite ist eine Übersetzung der englischen Version. Bei Abweichungen ist die englische Version maßgeblich.