モックと障害注入

トラフィックを眺めれば、アプリが何をしているかは分かります。このページはもう半分の話です。ネットワークに、本来返さないはずの答えを返させて、アプリがそれにどう反応するかを見る。まだ作られていないバックエンド、500 を返すエンドポイント、8 秒かかるレスポンス、そっくり消えてしまった API。

ここに書かれたことは Chute が動くすべてのプラットフォームで成り立ちます。HTTPS に触れるものはすべて、そのホストに対して先に復号を有効にしておく必要があります — Chute が読めない暗号化リクエストには、Chute も答えようがありません。

Chute にできること・できないこと

Chute が介入するのは接続と HTTP メッセージです。トラフィックシェーパーは持っていません:

できる 固定のボディを返す、指定のステータスコードを返す、リクエストやレスポンスの前に固定の遅延を入れる、接続をきっぱり拒否する、HTTP/3 のクライアントを TCP に戻させる、リクエストを本来とは別のホストへ送る
できない 帯域を制限する、パケットを落とす・並べ替える、ジッタを加える、接続を途中から劣化させる、トランスポート層で特定の RTT を再現する

設定に [Throttle] セクションはありませんし、速度制限もどこにもありません。遅いレスポンスではなく遅いリンクが必要なら、それは Chute ではなくネットワークコンディショナー(Apple の Network Link Conditioner、またはルーター)の仕事です。

仕組みを選ぶ

再現したいもの 使うもの 参照先
まだ存在しないレスポンスボディ [Map Local] モックレスポンス
ちょうど 503 [URL Rewrite]reject URL 書き換え
空の 200、空白画像、空の JSON オブジェクト reject-200reject-imgreject-dict URL 書き換え
それ以外のステータス — 401、429、500 http-request スクリプト JS スクリプト
遅延 http-request または http-response スクリプト JS スクリプト
そもそも到達できないエンドポイント REJECT ルール 組み込みポリシー
TCP にフォールバックしないクライアント block-quic その他のオプション
同じ URL の裏側にある別のバックエンド [Host]、または [URL Rewrite] の header モード ローカル DNS マッピング

固定のレスポンスボディ

[Map Local] は、実際のサーバーに尋ねずに、ファイルまたはインラインの base64 でマッチしたリクエストに応答します:

[Map Local]
^https://api\.example\.com/v1/profile.* data="/Users/me/mocks/profile.json"
^https://api\.example\.com/v1/flags.* base64="eyJiZXRhIjogdHJ1ZX0="

これが効くかどうかは 3 点で決まります:

  • 正規表現は URL 全体にマッチしなければなりません。一部ではありません。クエリ文字列がまったく無い URL だけを狙うのでなければ、パターンは .* で終わらせてください。
  • data= は Chute を動かしているデバイスが読みます。 Mac では便利です — ファイルを直せば次のリクエストから反映されます。スマートフォンや Apple TV では、あなたの Mac 上のパスには何の意味もありません。そこでは base64= を使うか、ファイルを HTTP で配信して URL 書き換えを使ってください。
  • ステータスは常に 200 OK です。 [Map Local] にはステータスを設定する手段が無く、応答後に接続は閉じられます。それ以外のステータスにはスクリプトを使ってください — 次の節を参照。

ボディは {{ "{{url}}" }}{{ "{{host}}" }}{{ "{{path}}" }}{{ "{{method}}" }}{{ "{{ua}}" }} のテンプレート変数に対応しており、問い合わせ内容をそのまま返すモックを作るには十分です。

エラーステータス

503 ならスクリプトは不要です。reject モードの URL 書き換えが HTTP/1.1 503 を返します:

[URL Rewrite]
^https://api\.example\.com/v1/orders.* _ reject

同じ仲間が「使える中身が無い」の他のかたちをカバーします:reject-200(空ボディの 200)、reject-img(1×1 の GIF)、reject-dict(JSON の {}、200)。いずれも HTTPS に対しては、そのホストが復号されているときにだけ効きます。

それ以外の任意のステータスコードには、http-request スクリプトでリクエストを短絡させます:

[Script]
Fail429 = type=http-request, script-path=/Users/me/mocks/fail429.js, pattern=^https://api\.example\.com/v1/orders
// fail429.js — サーバーに接続せずに応答する
$done({
    response: {
        status: 429,
        headers: {
            "Content-Type": "application/json",
            "Retry-After": "30"
        },
        body: JSON.stringify({ error: "rate_limited" })
    }
})

スクリプトの pattern は書き換えファミリーとは違い、URL のどこにでもマッチします — ^https://api\.example\.com/v1/orders のような前置きで十分で、末尾の .* は要りません。

HTTP/1.1 の経路では、ステータスコードが何であれステータス行のリーズンフレーズは OK と書かれます(HTTP/1.1 429 OK)。クライアントが読むのは数値でフレーズではないので見た目だけの問題ですが、生のキャプチャではそう見えます。

遅延

スクリプトは $done() を呼ぶまでメッセージを止めるので、タイマーがそのまま遅延になります:

[Script]
SlowAPI = type=http-response, script-path=/Users/me/mocks/slow.js, pattern=^https://api\.example\.com/v1/, timeout=15
// slow.js — 本物のレスポンスを 8 秒遅れて返す
setTimeout(function () {
    $done({})
}, 8)

予算はスクリプト自身の timeout です:既定は 5 秒、30 を超える値は 30 に丸められます。タイムアウト時に $done() を呼んでいないスクリプトはパススルーとして扱われ — メッセージはそのまま進みます — なので、タイムアウトより長い遅延は派手に失敗するのではなく、単に遅延しなくなります。上の例のように、timeout は欲しい遅延より大きく設定してください。

サーバーに接続する前に遅らせるなら type=http-request(アプリからは往復が遅く見えます)、あとで遅らせるなら type=http-response(サーバーは速かったのにアプリは待たされます)を使います。

そっくり消えたエンドポイント

モックはレスポンスを差し替えますが、REJECT ルールは接続を拒みます。接続のレベルで働くので HTTP だけでなく任意のプロトコルを覆い、復号も不要です:

[Rule]
DOMAIN-SUFFIX,api.example.com,REJECT

REJECT-DROPREJECT-TINYGIFREJECT-NO-DROP は互換のために受け付けられ、いずれも通常の REJECT として振る舞います。HTTP リクエストについては show-error-page-for-reject = true が素っ気ない拒否を読めるエラーページに置き換えるので、ブラウザではそれが自分のブロックだと一目で分かります。

フォールバック経路がそもそも存在するかを確かめる方法でもあります — 主系のホストを拒否して、アプリが副系に手を伸ばすのか、ただ回り続けるのかを見ます。

クライアントを HTTP/3 から降ろす

QUIC は UDP の上を走り、Chute はそれを復号できないので、HTTP/3 のアプリはこのページのどの仕組みからも見えません。その QUIC フローを拒否すると、対応するクライアントは TCP で再試行し、そこではすべてが機能します:

[General]
block-quic = on

auto はフローがプロキシに向かうときだけ QUIC を拒否し、onDIRECT を含めどこでも拒否します。TUN 経由で入ってくるトラフィックについては、Chute は拒否した QUIC フローに ICMP ポート到達不能で応答するので、クライアントはタイムアウトを待たずにすぐフォールバックします。

リクエストを別の場所へ送る

2 つのレイヤーに 2 つの方法があります:

[Host]
api.example.com = 10.0.0.5

[Host] のマッピングは DNS の問い合わせに好きなアドレスで答えます — ステージング機でもよいですし、「拒否される接続」ではなく「タイムアウトする接続」が欲しいならどこにも通じないアドレスでも構いません。すべてのプロトコルに効き、復号も不要です。変更したら DNS キャッシュを消去してください。

[URL Rewrite]
^https://api\.example\.com/v1/(.*) https://staging.example.com/v1/$1 header

header モードはリクエストをその場で書き換え、Host ヘッダーも直すので、クライアントはリダイレクトされたことを知りません。こちらは HTTP レベルなので、HTTPS には復号が必要です。宛先をその場で書き換えられない場合、Chute は新しい URL への 307 で応答するようフォールバックします。

本当に効いたかを確かめる

一度もマッチしなかったルールは、マッチしたけれど何もしなかったルールとまったく同じに見えます — このページ全体の失敗のしかたがこれです。

  • 書き換えとモックWeb コンソールルールページが、この実行で効いた URL 書き換え・Header 書き換え・Body 書き換え・Map Local のルールを回数付きで一覧します。そこに無いルールは一度もマッチしていません。同じデータは GET /api/rulesrewrite_hits にあります。
  • 接続ごと:コンソールか Dashboard でその接続を開き、適用された書き換えの行を読んでください。ルール自身の言葉でルールを示します。
  • スクリプトはその表に出ません。 スクリプトの証拠はスクリプト自身の出力です。console.log の行はログに残り、コンソールのログページや GET /api/logs で読めます。

後片付け

コンソール、Dashboard、あるいは POST /api/rewrites/:family から追加したルールは動作中のカーネルの中にあり、次の再起動で消えます — 実験には理想的ですが、頼りにするものを置く場所としては最悪です。設定ファイルの中のルールは再起動を越えて残るので、モックを置くにはよい場所であり、同時に忘れてはならないものでもあります。設定に残された [Map Local] の 1 行は数週間後もリクエストに答え続け、しかも壊れたサーバーとまったく同じに見えます。

S. Smart Rabbit LLC © All Rights Reserved            updated 2026-09-05 00:42:56

本ページは英語版からの翻訳です。内容に相違がある場合は、英語版が優先されます。

results matching ""

    No results matching ""