モックと障害注入

トラフィックを眺めれば、アプリが何をしているかは分かります。このページはもう半分の話です。ネットワークに、本来返さないはずの答えを返させて、アプリがそれにどう反応するかを見る。まだ作られていないバックエンド、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-200、reject-img / reject-tinygif、reject-dict、reject-array URL書き換え
それ以外のステータス — 401、429、500 status-code= 付きの [Map Local]、または http-request スクリプト モックレスポンス、JSスクリプト
遅延 http-request または http-response スクリプト JSスクリプト
そもそも到達できないエンドポイント REJECT ルール 組み込みポリシー
クライアントが諦めるまで応答しないリクエスト REJECT-DROP ルール 組み込みポリシー
TCPにフォールバックしないクライアント block-quic その他オプション
同じURLの裏側にある別のバックエンド [Host]、または [URL Rewrite] の header モード ローカルDNSマッピング

固定のレスポンスボディ

[Map Local] は、ファイルまたはインラインのデータでマッチしたリクエストに応答します。復号したHTTP/2ストリームでは実際のサーバーには一切尋ねません。HTTP/1.1 ではリクエスト自体は送信され、レスポンスだけが置き換えられます:

[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書き換えと同じです。^https://api\.example\.com/v1/profile だけでそのパスと後に続くクエリ文字列を含むので、末尾の .* は要りません。
  • data= はChuteを動かしているデバイスが読みます。 Macでは便利です — ファイルを直せば次のリクエストから反映されます。スマートフォンやApple TVでは、あなたのMac上のパスには何の意味もありません。そこでは base64= を使うか、ファイルをHTTPで配信してURL書き換えを使ってください。
  • ステータスは status-code= を指定しない限り 200 OK です。 応答後に接続は閉じられます。レスポンスヘッダーは header="Key: value|…" に書きます。

ボディは {{ "{{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 と reject-tinygif(1×1 のGIF)、reject-dict(JSONの {}、200)、reject-array(JSONの []、200)。いずれもHTTPSに対しては、そのホストが復号されているときにだけ効きます。

それ以外の任意のステータスコードには、status-code= 付きのモックレスポンスルールがいちばん手短です:

[Map Local]
^https://api\.example\.com/v1/orders data-type=text data="rate limited" status-code=429 header="Retry-After: 30"

応答をリクエストに応じて変える必要がある場合は、代わりに 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では、ステータス行にそのコードの標準のリーズンフレーズが付きます(HTTP/1.1 429 Too Many Requests)。レジストリに名前のないコード(599など)にはフレーズが付きません(HTTP/1.1 599)。

遅延

タイマーを登録したスクリプトは $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の派生は応答のしかたがそれぞれ異なり、ここでも役立ちます。REJECT-DROP は一切応答しないので、クライアントはタイムアウトするまで待ちます — サーバーが死んでいるときの見え方です。REJECT-200、REJECT-DICT、REJECT-ARRAY、REJECT-TINYGIF / REJECT-IMG、REJECT-VIDEO は、平文または復号済みのHTTP/1リクエストに合成した 200(空、{}、[]、1×1 のGIF、空白のMP4)を返します。同じホストが 30 秒間に 50 回を超えて拒否されると、REJECT、REJECT-TINYGIF、REJECT-IMG は以後 30 秒間 REJECT-DROP になります。REJECT-NO-DROP とその他の派生はそうなりません。組み込みポリシーを参照してください。HTTPリクエストについては show-error-page-for-reject = true が素っ気ない拒否を読めるエラーページに置き換えるので、ブラウザではそれが自分のブロックだと一目で分かります。

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

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

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

[General]
block-quic = on

auto はフローがプロキシに向かうときだけQUICを拒否し、on は DIRECT を含めどこでも拒否します。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には復号が必要です。リクエストのホストが変わらない限り、クライアントは書き換えに気づきません。復号されたHTTPSリクエストは送信先のホストに結び付いているため、上の例のように別のホストを指定するルールには新しいURLへの 307 で応答し、クライアントが自分でそれに従います。宛先をその場で書き換えられないその他の場合も、Chuteは同じように応答します。

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

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

  • 書き換えとモック: Webコンソールのルールページが、この実行で効いたURL書き換え・ヘッダー書き換え・ボディ書き換え・モックレスポンスのルールを回数付きで一覧します。そこに無いルールは一度もマッチしていません。同じデータは GET /api/rules の rewrite_hits にあります。
  • 接続ごと: コンソールでその接続を開き、適用された書き換えの行を読んでください。ルール自身の言葉でルールを示します。Dashboardでは、その接続の一般タブがルールの下の適用にその数を示し、適用されたルールの一覧に名前を挙げます。
  • スクリプトはその表に出ません。 スクリプトの証拠はスクリプト自身の出力です。console.log の行はログに残り、コンソールのログページや GET /api/logs で読めます。これらは verbose レベルで書かれるため、まず loglevel を verbose に上げてください — PUT /api/loglevel なら再読み込みなしで変更できます。console.warn と console.error はデフォルトのレベルで表示されます。各行は 256 文字で切り詰められ、認証情報はマスクされます。

後片付け

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

S. Smart Rabbit LLC © All Rights Reserved            updated 2026-09-30 15:11:19

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

results matching ""

    No results matching ""