TunnelHunt

Как повторить вебхук: отладка HTTP-запросов через Replay

4 мин чтения

Получите тестовый вебхук, найдите ошибку и повторите запрос после исправления. Рабочий пример на Python, диагностика подписей и ограничения Replay.

Как повторить вебхук: отладка HTTP-запросов через Replay

Повтор вебхука нужен, когда запрос уже пришёл, но обработчик завершился ошибкой. Вместо нового события у отправителя можно отправить сохранённый HTTP-запрос ещё раз. В TunnelHunt это делает кнопка Replay в журнале запросов.

Ниже воспроизведём ошибку на небольшом обработчике, исправим её и повторим событие. Пример работает с искусственными данными и не вызывает платёжные операции.

1. Запустите обработчик с воспроизводимой ошибкой

Сохраните как webhook_demo.py. Нужен Python 3, сторонние пакеты не требуются:

import json
from http.server import BaseHTTPRequestHandler, HTTPServer

REQUIRE_CUSTOMER = True

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        if self.path != '/webhook':
            self.send_error(404)
            return
        size = int(self.headers.get('Content-Length', '0'))
        if size > 65536:
            self.send_error(413)
            return
        try:
            event = json.loads(self.rfile.read(size))
            if not isinstance(event, dict):
                raise ValueError('Expected a JSON object')
        except (ValueError, UnicodeDecodeError):
            self.send_error(400)
            return
        # Искусственная ошибка: старый обработчик требует лишнее поле.
        status = 500 if REQUIRE_CUSTOMER and 'customer' not in event else 200
        body = json.dumps({'event_id': event.get('id'), 'status': status}).encode()
        self.send_response(status)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)

HTTPServer(('127.0.0.1', 3000), Handler).serve_forever()
python3 webhook_demo.py

В другом терминале откройте туннель. Чтобы найти его логи в своём кабинете, используйте SSH-ключ, связанный с вашим аккаунтом:

ssh -p 2222 -R 80:127.0.0.1:3000 tunnelhunt.ru

2. Отправьте тестовое событие

Замените домен в команде на адрес из терминала:

curl -i https://YOUR-SUBDOMAIN.tunnelhunt.ru/webhook \
  -H 'Content-Type: application/json' \
  --data '{"id":"demo-001","type":"order.created"}'

Ожидается 500: в теле нет customer, а обработчик пока требует это поле. Запрос с тем же телом к http://127.0.0.1:3000/webhook даст такой же результат.

3. Найдите запрос в журнале

Откройте панель TunnelHunt, выберите свой туннель и журнал HTTP-запросов. Найдите POST /webhook по времени отправки.

Проверьте метод, путь, Content-Type, сохранённое тело и статус. Если записи нет, сначала убедитесь, что смотрите правильный туннель и используете ключ своего аккаунта. Доступность и срок хранения логов зависят от условий тарифа.

4. Исправьте код и нажмите Replay

Измените REQUIRE_CUSTOMER = True на False. Остановите только Python-процесс через Ctrl+C и запустите его снова. SSH-соединение оставьте работающим, чтобы адрес сохранился.

Нажмите Replay для исходного запроса. В локальном журнале должен появиться новый POST /webhook со статусом 200. Так вы проверите исправление на том же событии demo-001.

Без панели можно повторить команду curl из шага 2. Разница в том, что Replay использует доступную сохранённую копию, а в curl вы самостоятельно задаёте метод, тело и заголовки.

Почему повтор не всегда равен новой доставке

Replay повторяет HTTP-обращение, но не создаёт новое событие в платёжной системе или Telegram. Состояние отправителя остаётся прежним, а состояние вашей базы могло уже измениться.

  • Подпись и время. Если подпись ограничена по времени, сохранённый запрос может больше не пройти проверку. Не отключайте проверку ради Replay; используйте свежую тестовую доставку или изолированный тест проверки подписи.
  • Идемпотентность. Обработанный event_id может быть отклонён как дубль. Это ожидаемое поведение. Проверяйте, что повтор не создаёт вторую оплату, отправку или запись.
  • IP-адрес. Повтор приходит через инфраструктуру туннеля, а не из сети исходного провайдера. Проверку IP нельзя тестировать таким повтором как полную имитацию реальной доставки.
  • Размер тела. В текущей реализации в журнал сохраняются первые 2 МиБ тела запроса. Для большего payload сохранённая копия может быть неполной; сравните с исходником перед повтором.
  • Транспорт. Служебные заголовки вроде Host, Connection и Content-Length при отправке формируются заново. Это не побайтовое воспроизведение TCP-сессии.

Что означает ошибка после повтора

Результат Где искать причину
404 Путь обработчика изменился или выбран другой туннель
400 Формат тела, кодировка или обязательные поля
401 / 403 Токен, подпись, срок действия или политика доступа
409 либо ответ о дубликате Событие уже обработано
500 Исключение в локальном обработчике; смотрите его журнал
502 Локальное приложение или SSH-соединение недоступны

Для рабочего проекта замените демонстрационный обработчик своим тестовым endpoint. Примеры интеграций: Telegram, платёжные вебхуки и n8n.

Готовы запустить свой первый туннель?

Начните использовать TunnelHunt бесплатно уже сегодня и откройте доступ к локальному серверу всего одной командой.

Зарегистрироваться бесплатно →

По теме