Глава 9 / 51
Том 2 · Конфигурация и сборка

Система ручек Custom: открытый мир непредустановленных полей

Куда попадают поля xs.json, которых хост не предустановил? — Нетронутыми в Custom, на чтение приложению. Этот «открытый мир» заодно вмещает запросные лимиты http-драйвера.

Обзор

Позиционирование: Custom — вторая половина конфигурационной системы XServer: предустановленные поля отвечают «как собирается хост», Custom — «что нужно приложению». После главы вы сможете добавлять свои ручки в конфиг и безопасно читать их из скрипта.

9.1 Введение

Рано или поздно конфигурационная система упирается: предустановленных полей фреймворка не хватает. Обычное решение — бесконечно добавлять поля, и вот раздутая конфигурация и плывущая семантика. Ответ XServer — прочертить ясную границу: предустановленные поля описывают факты сборки хоста (порты, протоколы, сертификаты), все остальные не всплывают, не валидируются, сохраняются как есть — в рантайме они висят деревьями xvalue на server->Custom, host->Custom и xsConfigRoot(), а смысл и проверку определяет приложение.

9.2 Модель чтения Custom

Три входа чтения
/* 1. Custom корневого уровня: xsConfigRoot возвращает retained xvalue; нужен Release */
xvalue* pRoot = xsConfigRoot();
xvalue* pMy = xrtValueObjectGet(pRoot, "my_app");
xrtValueRelease(pRoot);

/* 2/3. Уровень server / host: поля-представления прямо на информационных структурах (заимствованы) */
xvalue* pSrvCustom = pReq->server->Custom;
xvalue* pHostCustom = pReq->host->Custom;

Что только что произошло: корневому уровню нужен Release (ссылку держите вы); Custom уровня server/host — поля-представления: внутри колбэка читать безопасно, а сохранять — только по правилам владения главы 3.

Инструмент строгого разбора

Stream-модуль демо даёт XS_CustomReadUInt: читает из дерева Custom строго неотрицательное целое — неверный тип или отрицательное значение дают отказ, а не угаданное умолчание. Читайте ручки только им, без мягких преобразований.

9.3 Шесть ручек запросных лимитов

Самые важные «жильцы» открытого мира Custom — запросные лимиты, потребляемые драйверами http/ws (пишутся на уровне server; приложение тоже может их читать):

РучкаУмолчаниеПри превышении
header_limit—431 Request Header Fields Too Large
path_limit2048414 URI Too Long
body_limit—413 Content Too Large
idle_timeout—молча закрывает соединение (общее для http/ws/tcp)
ws_message_limit—close-код 1009
ws_protocol—рукопожатие 400 (обязательный подпротокол не совпал)

Почему это Custom, а не предустановленные поля? Потому что они описывают «отношение этого сервиса к запросам» — политика развёртывания, а не факт сборки; и проверка типов происходит на стороне потребителя (ws_protocol, например, валидируется при старте), сохраняя принцип «кто потребляет, тот и проверяет».

9.4 Контракт

  • Владение: xsConfigRoot — retained (в паре с Release); Custom server/host — заимствованные представления.
  • Валидация: хост не проверяет поля Custom — приложение обязано разбирать их строго (паттерн XS_CustomReadUInt) и отказываться от мягких преобразований.
  • Именование: ключи с префиксом приложения (my_app.*), чтобы не сталкиваться с привычными ручками http-драйвера (header_limit и т.п.).

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

Забыт Release у xsConfigRoot
Симптом
Каждое поколение скрипта течёт одной ссылкой xvalue; в отчётах памяти растёт счётчик корневого объекта.
Причина
Задокументированный контракт: возврат retained, обязателен xrtValueRelease — хрестоматийное применение правил владения главы 3.
Мягкий разбор превратил опечатку ручки в 0
Симптом
В конфигурации "body_limt" (опечатка); приложение получает умолчание 0 и тихо ведёт себя неверно.
Причина
Чтение в стиле «нет значения — считай ноль». У Custom нет валидации хостом — опечатку вскрывает только строгое чтение на стороне приложения.
✗ Ошибка
uint32 n = 0;
GetValue(pCustom, "body_limit", &n);
/* нет значения — молча 0 */
✓ Правильно
/* строгое чтение: нет значения — ошибка конфигурации */

Упражнения

БазовыйПрочитать свою ручку

Добавьте в корень xs.json "my_app": {"greeting": "hi"}; в ServiceInit скрипта прочитайте и напечатайте.

Приёмка: в логе напечатано hi; пара к xrtValueRelease оформлена верно.
ПродвинутыйНаблюдение состояний лимитов

Задайте демо body_limit=100, отправьте POST на 99 и 101 байт и зафиксируйте разницу кодов; затем path_limit=50 и длинный путь.

Приёмка: границы срабатывания 413 и 414 точно совпадают с настроенными значениями.
ВызовЗакрепить паттерн ручек

Напишите modules/knobs.h: декларативное описание ручек приложения (ключ / тип / умолчание / обязательность), строгая проверка всех при Init и единый отчёт обо всех отсутствующих.

Приёмка: при двух намеренно отсутствующих ключах одна ошибка называет оба имени.

Итоги

ТезисВывод
Определение CustomНепредустановленные поля сохраняются как есть; хост не всплывает и не валидирует
Входы чтенияxsConfigRoot (retained) / server->Custom / host->Custom (заимствование)
Запросные лимитыheader/path/body_limit + idle_timeout + две ws-ручки; превышение → 431/414/413/закрытие/1009
Дисциплина чтенияСтрогий разбор (паттерн XS_CustomReadUInt); ключи с префиксом приложения
Что дальше по xrt