fxTunnel

· Часть 13 серии «Пишу свой ngrok на Go: дневник разработки»

Пишу свой ngrok на Go: полировка — graceful shutdown, авто-обновление и replay

Проект зрелый: туннели, кастомные домены, инспекция, тесты, безопасность. Но есть набор мелких (и не очень) вещей, которые отделяют «работает» от «работает хорошо».

Контекст

31 января. Проект зрелый: туннели, кастомные домены, инспекция, тесты, безопасность. Но есть набор мелких (и не очень) вещей, которые отделяют «работает» от «работает хорошо». Сегодня — четыре фичи в одном коммите.

Graceful Shutdown

Раньше при остановке сервера все клиенты просто теряли соединение. kill → TCP reset → клиент видит ошибку → переподключается. Если повезёт — переподключится к уже запущенному серверу. Если нет — к серверу, который ещё не стартовал. Теперь: 1. Сервер отправляет ServerShutdown сообщение каждому клиенту 2. Отправляет yamux GoAway — стандартный сигнал «я закрываюсь» 3. Ждёт 2 секунды — пусть текущие запросы завершатся 4. Закрывает сессии Клиент, получив ServerShutdown, знает: это плановая остановка, не авария. Задерживает реконнект на пару секунд — даёт серверу время перезапуститься. Результат: при деплое пользователи не замечают перезапуска. Текущие запросы завершаются, новые идут после рестарта.

Trace ID

Когда через туннель проходят сотни запросов, и что-то идёт не так — как найти проблемный? По времени? Неудобно. По URL? Таких может быть много. Trace ID — уникальный идентификатор запроса, который проходит через всю цепочку: - API-middleware генерирует request_id и добавляет в логи - Для туннельных запросов генерируется X-Trace-Id, прокидывается в заголовках - Инспекция трафика сохраняет trace ID — можно найти запрос по нему Открыл лог, нашёл trace ID, нашёл запрос в инспекции — видишь всю картину.

Авто-обновление клиента

Пользователь запустил клиент. На сервере вышла новая версия. Раньше пользователь об этом не узнавал, пока не зайдёт на сайт. Теперь: 1. При подключении клиент фоном спрашивает сервер: «какая у тебя версия?» 2. Сравнивает со своей (semver) 3. Если есть обновление — уведомляет 4. Команда fxtunnel update скачивает новый бинарник и заменяет текущий В GUI — отдельный Pinia-стор для обновлений и уведомление в интерфейсе. Реализация самообновления:

go
func (u *Updater) Apply(info *UpdateInfo) error {
    // Скачиваем новый бинарник во временный файл
    tmpFile, _ := os.CreateTemp("", "fxtunnel-update-*")
    resp, _ := http.Get(info.DownloadURL)
    io.Copy(tmpFile, resp.Body)

    // Атомарная замена: rename текущего бинарника
    currentPath, _ := os.Executable()
    os.Rename(tmpFile.Name(), currentPath)
}

Знакомый паттерн — атомарная замена, как в деплое (статья 3).

Inspect Replay

В статье 9 я сделал инспекцию трафика — видно каждый HTTP-запрос. Но «видно» — это полдела. Хочу: увидел интересный запрос → нажал кнопку → повторил его. Как в Postman, но прямо из инспекции. Новый эндпоинт:

POST /api/tunnels/{id}/inspect/{exchangeId}/replay

Берёт сохранённый запрос (метод, URL, заголовки, тело) и отправляет его заново через туннель. Ответ сохраняется как новый exchange в инспекции. В UI — кнопка «Replay» рядом с каждым запросом. Один клик — запрос повторён. Удобно для дебага: «что-то сломалось» → нашёл запрос → повторил → посмотрел ответ.

Лендинг с интерактивными демо

Отдельный коммит (c4cceee) — 608 строк нового кода на лендинге. Добавил секцию «Advanced Features» с живыми демо: - Custom Domains — анимированная визуализация DNS-верификации. Показывает, как CNAME-запись проверяется и домен активируется. - HTTP Inspector — эмуляция потока запросов в реальном времени. Как выглядит инспекция трафика, не запуская продукт. - Компактные карточки для WebSocket passthrough, Low Latency, Prometheus Metrics. Всё с i18n — русский и английский.

Ещё: защита /metrics

Мелкий, но важный фикс (eb26ae3). Endpoint Prometheus /metrics висел без авторизации. Любой мог посмотреть метрики сервера: количество пользователей, туннелей, нагрузку. Обернул в auth middleware — теперь только авторизованные. И ограничение длины пароля (b0206ab). Без лимита можно отправить мегабайтный пароль — bcrypt будет хэшировать его вечность, а сервер будет занят. Max password length решает.

Итог

31 января: - Graceful shutdown — деплой без потери соединений - Trace ID — сквозная трассировка запросов - Авто-обновление — клиент обновляется одной командой - Inspect replay — повтор запроса из инспекции - Интерактивные демо на лендинге - Защита /metrics и лимит пароля

8aad04a feat: add graceful shutdown, trace ID, auto-update and inspect replay
c4cceee feat(landing): add advanced features section with interactive demos
eb26ae3 fix(security): protect /metrics endpoint with auth middleware
b0206ab fix(api): add max password length, log JSON encoding errors

Это те фичи, которые по отдельности кажутся мелочью, а вместе делают продукт зрелым. Graceful shutdown — не видно, пока не нужно. Trace ID — не нужен, пока не дебажишь. Auto-update — удобство, которое замечаешь, когда его нет.


Серия завершена. 13 статей, 38 700 строк кода, 26 релизов, 7 рабочих дней. Спасибо, что читали.

Все части дневника разработки