Глобальный контракт
Указатели / представления в параметрах колбэка (XS_HttpReq, структуры заголовков, представления сообщений) действительны только в стеке колбэка; значения выносить можно, указатели — сперва скопировать.
ServiceUnit выполняется не более одного раза за время жизни поколения; для кандидата, чья инициализация не завершилась, Unit ошибочно не вызовется.
Все 14 символов экспортируются по мере надобности. host без RequestProc — чисто статический сайт; ws-хост без WsText/WsBinary отвечает 503.
Распределение по потокам: ServiceInit / ServiceSwap — в потоке сборки или управления reload; RequestProc / Ws* / Event* — в рабочих потоках движка (возможна конкурентность); ServiceUnit — в потоке GC поколения. Межпоточное общее состояние синхронизируйте сами.
Колбэки жизненного цикла (3)
ServiceInit жизненный цикл
Вызывается один раз после загрузки скриптов поколения — здесь строят таблицы маршрутов, открывают базы, инициализируют статическое состояние.
Новое поколение видно снаружи только после полного завершения Init; внутри Init данные передачи берутся через xsSwapTake (см. API XServer).
static void ServiceInit(XS_HostInfo* pHost)
{
DB_Init(); /* открыть SQLite, создать таблицы */
Page_Init();
Template_Init();
RouteHTTP_Init(); /* регистрация и компиляция маршрутов */
}
ServiceUnit жизненный цикл
Деструктор при уходе поколения на покой — освобождение в порядке, обратном Init. За всю жизнь выполняется не более одного раза.
Точки запуска две: путь грациозной остановки процесса или возврат последней аренды старого поколения после горячей перезагрузки. У поколения с незавершённым Init вызова Unit не будет.
static void ServiceUnit(XS_HostInfo* pHost)
{
RouteHTTP_Unit(); /* освободить шаблоны и таблицу маршрутов */
Template_Unit();
Page_Unit();
DB_Unit(); /* закрыть базу */
}
ServiceSwap жизненный цикл
Перед сменой поколения экспортирует неразрушающий снимок межпоколенческого состояния; ServiceInit нового поколения забирает его через xsSwapTake.
| Параметр | Направление | Пояснение |
|---|---|---|
pHost | вход | уходящий host старого поколения |
ppShared | выход | принимает снимок xvalue*; владение через хост переходит новому поколению |
Смысл неразрушающее: после экспорта снимка старое поколение может продолжать обслуживать (в крайних случаях фиксацию ещё и отменят) — не закрывайте ресурсы внутри Swap.
static bool ServiceSwap(XS_HostInfo* pHost, xvalue** ppShared)
{
xvalue* pState = xrtValueObject();
xrtValueObjectSetNew(pState, XRT_STR_LITERAL("counter"),
xrtValueInt64(G_iCounter));
*ppShared = pState;
return true;
}
Колбэк HTTP (1)
RequestProc HTTP
Вход каждого HTTP-запроса. Трёхзначный возврат объявляет, «кто отвечает за остаток жизни этого запроса».
| Возврат | Семантика | Что драйвер делает дальше |
|---|---|---|
XS_OK (0) | полный ответ записан | вычитать остаток body, продолжить keep-alive |
XS_FALLBACK (1) | не обработано | GET/HEAD — в статический слой, остальное — 404 |
XS_TAKEOVER (2) | захват всего соединения | драйвер отходит; приложение само ведёт приём-передачу и в конце Close |
XS_HttpReq — структурное представление: ровно одно из tcp/tls не NULL (доступен весь потоковый API xrt), head — разобранные заголовки запроса (с MethodCode), body — автомат фреймизации, host/server — принадлежность. Поля построчно — в справочнике структур.
/* маршрутизационные фреймворки demo все сходятся к RequestProc */
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
if ( RouteHTTP_Dispatch(pReq) ) return XS_OK;
return XS_FALLBACK; /* без совпадения -> wwwroot */
}
Колбэки WebSocket (6)
XServer выполняет проверку рукопожатия и xrtWsStreamAttach; маскировка, пересборка фрагментов, управляющие кадры и permessage-deflate целиком обрабатываются в слое xrt. Отправка — семейством xrtWsStreamText / Binary.
WsOpen WebSocket
Срабатывает при установлении соединения (101 пройден, поток подключён).
static void WsOpen(XS_HostInfo* pHost, xwsstream* pWs)
{
(void)xrtWsStreamText(pWs, xrtStrView("connected"));
}
WsText WebSocket
Срабатывает после пересборки целого текстового сообщения; tText — представление, действительное внутри этого колбэка.
static void WsText(XS_HostInfo* pHost, xwsstream* pWs, xstrview tText)
{
(void)xrtWsStreamText(pWs, tText);
}
WsBinary WebSocket
Срабатывает на целое бинарное сообщение; накопленное превышение ws_message_limit завершает соединение close 1009.
WsPing WebSocket
Пришёл кадр Ping. Автоматический ответ Pong обрабатывает слой xrt — этот колбэк для бизнес-наблюдения (статистика сердцебиений и т.п.).
WsPong WebSocket
Пришёл кадр Pong — место, где измеряется время оборота.
WsClose WebSocket
Закрытие соединения; несёт код состояния и представление причины — до 123 байтов UTF-8.
| Частые iCode | Смысл |
|---|---|
1000 | нормальное закрытие |
1009 | сообщение сверх лимита (ws_message_limit) |
1011 | внутренняя ошибка сервера (например, OOM) |
Колбэки потока TCP (3)
XS_StreamConn — структурное представление: ровно одно из tcp / tls не NULL; используйте отправочный API, соответствующий ненулевой стороне (под tcps запрещено писать в tcp в обход tls). Время жизни соединения принадлежит хосту — приложение не отвечает за Destroy.
EventOpen TCP
Срабатывает после установления потокового соединения (момент отправить приветственный баннер).
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn)
{
if ( pConn->tcp != NULL )
(void)xrtNetStreamSend(pConn->tcp, "hello\n", 6);
}
EventData TCP
Пришли данные. pBuffer — цепочка буферов без копирования (заимствована в колбэке); размер — через xrtNetBufSize, данные потребляются буферными API xrt.
EventClose TCP
Конечное состояние закрытия соединения; включает код сетевого результата и опциональный объект ошибки (заимствованный).
Колбэк UDP (1)
EventDgram UDP
Срабатывает по одному разу на дейтаграмму; состояния соединения нет — каждый пакет берёт аренду текущего поколения через слот прослушивания.
| Поле pMsg | Пояснение |
|---|---|
Remote | адрес источника (перевод в текст — семейством xrtNetAddr) |
Data / Size | нагрузка и длина |
Flags | XNET_UDP_MESSAGE_TRUNCATED и другие |
static void EventDgram(XS_HostInfo* pHost, xnetudp* pUdp,
const xnetudpmessage* pMsg)
{
(void)xrtNetUdpSendTo(pUdp, &pMsg->Remote, "ack", 3);
}