Имитация и внедрение сбоев
Наблюдение за трафиком отвечает на вопрос, что приложение делает. Эта страница — о другой половине: заставить сеть ответить то, чего она никогда бы не ответила, и посмотреть, как приложение с этим справится. Бэкенд, которого ещё нет; эндпоинт, отдающий 500; ответ, приходящий через восемь секунд; API, который попросту исчез.
Всё описанное работает на любой платформе, где работает Chute, а всё, что касается HTTPS, требует сначала включить расшифровку для этого хоста: зашифрованный запрос, который Chute не может прочитать, он не может и отработать за сервер.
Что Chute может имитировать, а что нет
Chute вмешивается на уровне соединения и HTTP-сообщения. Формирователя трафика у него нет, поэтому:
| Может | Вернуть заготовленное тело, вернуть выбранный код состояния, добавить фиксированную задержку перед запросом или ответом, наотрез отказать в соединении, вернуть клиентов с HTTP/3 на TCP, отправить запрос не тому хосту, который был запрошен |
| Не может | Ограничить полосу, терять или переставлять пакеты, добавлять джиттер, ухудшать соединение по ходу дела или воспроизводить конкретный RTT на транспортном уровне |
Секции [Throttle] в конфигурации нет, ограничения скорости нет нигде. Если нужен медленный канал, а не медленный ответ, это дело сетевого кондиционера (Network Link Conditioner от Apple или маршрутизатор), а не Chute.
Выбрать механизм
| Что имитировать | Чем | Где описано |
|---|---|---|
| Тело ответа, которого ещё нет | [Map Local] |
Имитация ответов |
| Ровно 503 | [URL Rewrite] … reject |
Перезапись URL |
| Пустой 200, пустая картинка, пустой объект JSON | reject-200, reject-img, reject-dict |
Перезапись URL |
| Любой другой статус — 401, 429, 500 | Сценарий http-request |
Сценарии JS |
| Задержка | Сценарий http-request или http-response |
Сценарии JS |
| Эндпоинт, до которого просто не достучаться | Правило REJECT |
Встроенные политики |
| Клиент, который не откатывается на TCP | block-quic |
Прочие параметры |
| Другой бэкенд за тем же URL | [Host] или режим header у [URL Rewrite] |
Локальное сопоставление 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="
Сработает это или нет, решают три вещи:
- Регулярное выражение должно совпасть со всем URL, а не с его частью. Заканчивайте шаблон на
.*, если только вам не нужен URL вовсе без строки запроса. data=читает то устройство, на котором работает Chute. На Mac это удобно: правите файл — и следующий запрос видит изменение. На телефоне или Apple TV путь с вашего Mac не значит ничего; там используйтеbase64=либо отдавайте файл по HTTP и применяйте перезапись URL.- Статус всегда
200 OK. У[Map Local]нет способа его задать, а после ответа соединение закрывается. Для любого другого статуса берите сценарий — см. ниже.
Тело поддерживает шаблонные переменные {{ "{{url}}" }}, {{ "{{host}}" }}, {{ "{{path}}" }}, {{ "{{method}}" }} и {{ "{{ua}}" }}, чего достаточно, чтобы сделать имитацию, отражающую то, о чём её спросили.
Код ошибки
Для 503 сценарий не нужен: перезапись URL в режиме reject возвращает HTTP/1.1 503:
[URL Rewrite]
^https://api\.example\.com/v1/orders.* _ reject
Соседние режимы покрывают остальные формы «ничего полезного»: reject-200 (200 с пустым телом), reject-img (GIF 1×1), 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 — вернуть настоящий ответ на восемь секунд позже
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-DROP, REJECT-TINYGIF и REJECT-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, только когда поток идёт на прокси; on отклоняет везде, включая DIRECT. Для трафика, приходящего через TUN, Chute отвечает на отклонённый QUIC-поток сообщением ICMP Port Unreachable, чтобы клиент откатился сразу, а не ждал таймаута.
Отправить запрос в другое место
Два способа на двух уровнях:
[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 откатывается к ответу 307 на новый URL.
Убедиться, что оно сработало
Правило, которое ни разу не совпало, выглядит ровно так же, как правило, которое совпало и ничего не сделало, — именно так эта страница целиком и подводит.
- Перезаписи и имитации: страница Правила в Веб-консоли перечисляет каждое правило URL Rewrite, Header Rewrite, Body Rewrite и Map Local, сработавшее за этот запуск, со счётчиком. Нет в списке — значит ни разу не совпало. Те же данные лежат в
rewrite_hitsуGET /api/rules. - По соединению: откройте соединение в консоли или в Dashboard и прочтите строки Применённые перезаписи, где правило названо его собственными словами.
- Сценариев в этой таблице нет. Свидетельство сценария — его собственный вывод: строки
console.logпопадают в журнал и читаются на странице Журналы консоли или черезGET /api/logs.
Убрать за собой
Правила, добавленные из консоли, из Dashboard или через POST /api/rewrites/:family, живут в работающем ядре и исчезают при следующем перезапуске — идеально для опыта и очень плохо для того, на что вы полагаетесь. Правила в файле конфигурации переживают перезапуски, что делает файл хорошим местом для имитации и очень плохой вещью, чтобы о ней забыть: строка [Map Local], оставленная в конфигурации, будет отвечать на запросы и через несколько недель, а выглядеть это будет в точности как сломанный сервер.
Эта страница — перевод английской версии. При расхождениях приоритет имеет английская версия.