Контекст
30 января. Последний день активной разработки. fxTunnel работает, оптимизирован, протестирован, защищён. Но есть одна фича, которую хочу с самого начала: инспекция трафика. Знаете Charles Proxy? Или вкладку Network в DevTools? Вот то же самое, но встроенное в туннель. Каждый HTTP-запрос, который проходит через fxTunnel, видим в реальном времени. Заголовки, тело, статус-код, время. Без установки дополнительного софта. Для разработчика, который показывает заказчику демо через туннель — очень полезно. Видишь, что заказчик делает на сайте в реальном времени.
От документа к коду
Это первая фича, которую я начал с документа, а не с кода. Написал дизайн: что хочу, как должно работать, какие паттерны. И только потом — реализация. Архитектура:
HTTP-запрос → Сервер перехватывает → Сохраняет в RingBuffer →
→ SSE-стрим → Веб-панель / GUI в реальном времени
Ключевые решения: - RingBuffer — не хочу бесконечно копить запросы в памяти - Fan-out подписки — несколько клиентов могут смотреть трафик одновременно - SSE (Server-Sent Events) — для реального времени в браузере
RingBuffer
Массив фиксированного размера. Заполнился — новые элементы перезаписывают старые. Как кассета, которая перематывается в начало. Почему не обычный массив: 1. Предсказуемое потребление памяти — буфер на 100 записей всегда занимает одинаково 2. O(1) добавление — никаких аллокаций 3. Безопасно для concurrent — с правильными блокировками Fan-out: новая запись → все подписчики получают уведомление через каналы. Подписался на трафик туннеля — получаешь каждый запрос в реальном времени.
HTTP-перехват
Сервер уже проксирует HTTP-трафик. Инспекция — дополнительный слой: перед отправкой запроса сохраняем его, после получения ответа — дополняем.
→ Входящий HTTP-запрос
→ Сохраняем: метод, URL, заголовки, тело
→ Проксируем через туннель (как обычно)
→ Получаем ответ
→ Дополняем: статус-код, заголовки, тело, время
→ Кладём в RingBuffer → Уведомляем подписчиков
Каждая запись содержит: метод, URL, заголовки запроса и ответа, тела (с лимитом), статус-код, время. 90 строк в HTTP-роутере.
SSE vs WebSocket
Для реального времени два варианта: WebSocket и SSE (Server-Sent Events). WebSocket — двунаправленный канал. Клиент и сервер обмениваются сообщениями. SSE — однонаправленный. Только сервер → клиент. Но: - Работает через обычный HTTP (легче проксировать) - Автоматический реконнект в браузере (встроено!) - Проще реализовать (раза в три меньше кода) - Двунаправленность нам и не нужна — мы только показываем трафик Выбрал SSE. На сервере:
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
ch := buffer.Subscribe()
defer buffer.Unsubscribe(ch)
for {
select {
case exchange := <-ch:
data, _ := json.Marshal(exchange)
fmt.Fprintf(w, "data: %s\n\n", data)
flusher.Flush()
case <-r.Context().Done():
return
}
}
На фронте:
const source = new EventSource('/api/inspect/stream?tunnel_id=xxx')
source.onmessage = (event) => {
const exchange = JSON.parse(event.data)
// Показываем в UI
}
Реальное время без библиотек, без сложности WebSocket. Просто работает.
UI
Добавил интерфейс инспекции в обоих клиентах: - Веб-панель: таблица запросов, клик — детали. Обновляется в реальном времени через SSE. - GUI: то же самое в десктопном приложении, через тот же SSE-стрим.
Грабли
Большие ответы. HTTP-ответы могут быть огромными (файлы, видео). Буферизовать всё в память нельзя. Ограничил размер тела — показываем что поместилось, для остального показываем размер.
SSE и аутентификация. EventSource в браузере не поддерживает кастомные заголовки. Нельзя передать JWT через Authorization. Передаю токен через query-параметр. Не идеально (токен в логах), но для внутреннего инструмента — приемлемо.
Конкурентный доступ. Несколько горутин пишут (каждый запрос), несколько читают (подписчики). sync.RWMutex решает: много читателей одновременно, один писатель.
Итог
30 января — инспекция трафика: - RingBuffer с fan-out подписками - HTTP-перехват с минимальным overhead - SSE-стриминг в реальном времени - UI в веб-панели и GUI
a6bb61b feat(inspect): add CapturedExchange data model
b577e7e feat(inspect): add Manager for per-tunnel buffers
5023c3b feat(inspect): capture HTTP traffic
efa4863 feat(api): add inspect handlers (list, detail, clear, SSE stream)
dffe5c2 feat(web): add traffic inspection UI
da2a494 feat(gui): add traffic inspection view
Фича, которая превращает fxTunnel из просто туннеля в инструмент разработки.
В следующей части: финальная. Итоги, статистика и что бы я сделал иначе.