Глава 14 / 51
Том 3 · Разработка протоколов

Основы HTTP: структура-представление и трёхзначный возврат

RequestProc получает не объект-обёртку, а набор «можно сразу действовать» нативных указателей xrt. Глава дорисовывает карту этого представления — следующие четыре главы работают на ней.

Обзор

Позиционирование: картографическая глава тома 3. Гл. 15 (ответы), гл. 16 (тела) и гл. 17 (маршрутизация) раскрывают разные стороны представления; здесь сначала строится целостная картина и два железных правила (заимствование, трёхзначность).

14.1 Введение

Для HTTP-бизнеса нужны три вещи: знать, что за запрос (метод, путь, заголовки, тело), знать, куда писать ответ (транспорт) и знать, кто забирает управление после вас. Большинство фреймворков мнут всё это в объект Request/Response, меняя абстракцию на удобство; XServer выдаёт три вещи раздельно — шесть полей XS_HttpReq и есть их адреса.

14.2 XS_HttpReq по полям

XS_HttpReq (xsbase.h, дословно)источник: src/sdk/xsbase.h
typedef struct {
    xnetstream*       tcp;    /* ровно один из tcp/tls не пуст: голый транспорт */
    xtlsstream*       tls;    /*   применим весь потоковый/отправочный API xrt  */
    const xhttp1head* head;   /* вкл. MethodCode; представление валидно в колбэке */
    xhttp1body*       body;   /* автомат кадрирования: фикс / chunked / close     */
    const XS_HostInfo*  host;
    XS_ServerInfo*    server;
} XS_HttpReq;
ПолеНазначениеИспользование здесь
tcp / tlsвыход ответаотправка байтов ответа (гл. 15)
headличность запросаMethodCode, путь, представления полей (14.3)
bodyтело запросаинкрементальное чтение (гл. 16)
host / serverпринадлежностьтопология, ручки Custom

Почему нет поля Response? — потому что ответ не объект, а поток байтов, который вы пишете в транспорт. В этом предельность «нуля обёрток»: нет скрытого буфера ответа — значит, нет и вопросов класса «когда фреймворк делает flush».

14.3 Чтение заголовков запроса

Метод, путь и поля
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
    /* метод: битовая маска — одновременно и метод, и элемент множества (ref-enums) */
    if ( pReq->head->MethodCode == XHTTP_METHOD_GET ) { ... }

    /* представления пути и полей: xstrview = указатель + длина, валидны в колбэке */
    /* конкретные API доступа (поиск полей / разбор пути) — в xrt · http1 */
    return XS_FALLBACK;
}

Два железных правила чтения головы: первое — представления полей head действительны только в стеке данного колбэка — XServer копирует заголовок запроса на фазе PinHead, чтобы помешать переиспользованию ring-буфера, но после смены поколения/конца запроса они всё равно истекают; второе — не сравнивайте пути C-строковыми функциями: у представлений есть длина, сравнивайте по длине (инструменты xrtStrView).

14.4 Момент вызова и потоки

соединение установлено → заголовок запроса разобран → RequestProc (worker-поток, конкурентно) │ ┌────────────────────┼────────────────────┐ ▼ XS_OK ▼ XS_FALLBACK ▼ XS_TAKEOVER драйвер вычитывает тело GET/HEAD → статика приложение забирает соединение цикл keep-alive остальное → 404 (гл. 25)
  • По разу на запрос; на keep-alive-соединении вызывается на каждый запрос
  • Конкурентность workers — RequestProc обязан быть реентерабельным (без изменяемых межзапросных статиков или с собственной блокировкой)
  • Между драйвером и скриптом действует перекрёстная сверка потреблённых байтов: расхождение заявленного с фактическим в сети рвёт соединение — скрипт рассматривается как недоверенная граница

14.5 Контракт

  • Заимствование: все шесть полей действительны внутри колбэка; значения можно копировать, указатели нельзя.
  • Трёхзначность: перед XS_OK ответ дописан; один записанный байт запрещает FALLBACK; после TAKEOVER драйвер отходит.
  • Конкурентность: RequestProc исполняется конкурентно на workers — реентерабельность есть жёсткое требование.

Частые ошибки

Сравнение представлений пути через strcmp
Симптом
Случайные ошибки определения пути, особенно со строкой запроса или буфером без нуля в конце.
Причина
xstrview — не C-строка: NUL-терминатора может не быть, авторитетна длина.
✗ Ошибка
if ( strcmp(pPath, "/api") == 0 )
✓ Правильно
/* сравнение по длине представления (инструменты xrtStrView) */
Межзапросные изменяемые статики в RequestProc
Симптом
Под конкурентностью сбивающиеся счётчики, случайные падения.
Причина
Забыта конкурентность workers — статические переменные читаются и пишутся несколькими запросами одновременно.

Упражнения

БазовыйТриаж методов

Напишите RequestProc: GET печатает "GET", POST печатает "POST", остальное возвращает XS_FALLBACK.

Приёмка: три метода проверены; лог и поведение отката соответствуют.
ПродвинутыйКонкурентно-безопасный счётчик

Считайте обработанные запросы (подсказка: атомики, семейство xrtAtomic).

Приёмка: после 10 000 запросов счётчик ровно 10 000 — без потерь и разрывов.
ВызовЭксперимент со временем жизни представлений

Сохраните указатель представления head в глобальную и прочитайте на следующем запросе — пронаблюдайте и объясните; затем перепишите на копирование внутри колбэка.

Приёмка: падение или порча объяснимы по модели поколений/заимствования главы 3.

Итоги

ТезисВывод
Структура-представлениеtcp/tls (выход) + head (личность) + body (тело) + host/server (принадлежность)
Нет объекта Responseответ — поток байтов в транспорт (гл. 15)
Правила чтения головыпредставления валидны в колбэке; сравнение по длине, не как C-строки
Конкурентностьреентерабельность на workers; сверка потребления с сетью
Что дальше по xrt