Перехват соединений: XS_TAKEOVER — третий путь
Часть бизнеса не влезает в «вопрос-ответ»: SOCKS-прокси, приватные протоколы после Upgrade, шлюзы длинного опроса. XS_TAKEOVER позволяет RequestProc забрать соединение целиком и вести его самому.
Обзор
25.1 Введение
Границы трёх состояний нарисованы в главе 4: XS_OK возвращает соединение в keep-alive, XS_FALLBACK отдаёт статическому слою. Третье состояние XS_TAKEOVER значит «с этого момента соединение моё» — драйвер выходит из обработки событий, и дальнейший приём-передача, тайм-ауты и закрытие решает логика приложения. Типовые сцены: HTTP Upgrade к приватному протоколу, туннели-прокси, push-шлюзы с длинными межзапросными сеансами.
25.2 Семантика перехвата
- Точка перехвата: с момента возврата TAKEOVER из RequestProc соединение больше не потребляется HTTP-драйвером
- Таблица событий: после перехвата ставится своя обработка (заменять на нелегальное состояние нельзя)
- Контракт утилизации: объект соединения в конце всё равно Destroy-ит XServer — вы им управляете, но не владеете его деструкцией; терминал Close одновременно освобождает lease поколения, который держит соединение
- Байтовая синхронизация: потреблённые/записанные до перехвата байты по-прежнему сверяются драйвером — ползапроса HTTP + TAKEOVER отклоняется как несогласованное состояние
25.3 Модель pull на практике
В func_test есть реальный кейс перехвата-эха; паттерн (асинхронный pull-ввод-вывод):
typedef struct TakeoverCtx {
XS_HostInfo* pHost; /* через потоки — по правилам владения */
xnetstream* tcp; /* или tls: транспорт после перехвата */
} TakeoverCtx;
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
if ( !IsUpgradeTarget(pReq) ) return XS_FALLBACK;
/* 1. сначала записать требуемые протоколом байты ответа (например 101 Switching Protocols) */
/* 2. построить контекст перехвата */
TakeoverCtx* pCtx = xrtCalloc(1, sizeof(TakeoverCtx));
pCtx->tcp = pReq->tcp;
/* 3. повесить асинхронное чтение: данные приходят в колбэк (кейс — отдельный поток + RecvAsync) */
StartTakeoverLoop(pCtx);
return XS_TAKEOVER; /* соединение моё */
}
Что только что произошло: внутри цикла перехвата вы работаете с pCtx->tcp обычными потоковыми API xrt — ровно как в гл. 23. Разница лишь в «происхождении» потока: это было HTTP-соединение, а какой протокол оно говорит дальше — решаете вы.
25.4 Терминальное состояние и утилизация
- Вы обязаны в итоге Close: перехват — обещание; забытое соединение висит вечно (idle_timeout — подстраховка, а не проектный путь)
- Close идёт через семантику закрытия потоковых API; XServer в терминальном колбэке соединения делает Destroy и освобождает lease поколения — поэтому старое поколение может дождаться окончания всех перехваченных соединений до утилизации (reload_matrix проверил этот порядок)
- Контекст перехвата (pCtx) освобождается вместе с Close — тот же реестровый паттерн чистки, что у WS/TCP
25.5 Контракт
- Перехват: до возврата TAKEOVER дописаны байты ответа протокола; после перехвата драйвер соединение не потребляет.
- Терминал: финальный Close за приложением; Destroy и освобождение lease — за XServer на терминале.
- Контекст: жизнь контекста перехвата привязана к терминалу соединения — чистка реестровым паттерном.
Частые ошибки
- Симптом
- Таблица соединений медленно растёт; старые поколения не утилизируются (lease не возвращён).
- Причина
- Обещание перехвата не выполнено — у каждого TAKEOVER обязан быть путь Close (включая ветки ошибок).
- Симптом
- Соединение немедленно разрывается.
- Причина
- Драйвер сверяет «заявленные скриптом байты vs сеть» — TAKEOVER при недопотреблённом запросе есть несогласованное состояние. Сначала дочитайте, потом перехватывайте.
Упражнения
Реализуйте /raw по паттерну 25.3: после перехвата — режим построчного эха (на уровне HTTP будто «протокол обновился»).
Реализуйте метод CONNECT: после перехвата открыть к цели второй поток и качать в обе стороны (двухпоточный форвардинг xrt).
Держите перехваченное соединение активным, подайте reload скрипта и убедитесь: старое поколение ждёт конца перехваченного соединения до утилизации.
Итоги
| Тезис | Вывод |
|---|---|
| Третье состояние | XS_TAKEOVER: драйвер отходит, приложение ведёт соединение целиком |
| Сценарии | смена протокола / туннели / прокси — не повседневный инструмент |
| Дисциплина перехвата | сначала дописать байты ответа протокола; сверка потребления действует |
| Дисциплина терминала | обязателен финальный Close; Destroy и lease — за XServer |
| Итог тома 3 | четыре класса протоколов + маршрутизация/JSON/шаблоны/vhost в руках — в том 4, к горячей перезагрузке |
- справочник xrt · future — модель уведомлений о завершении асинхронного ввод-вывода
- справочник xrt · proxy — реализация SOCKS5 / HTTP CONNECT на уровне библиотеки (для продвинутого упражнения)