Контекст
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-стор для обновлений и уведомление в интерфейсе.
Реализация самообновления:
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 рабочих дней. Спасибо, что читали.