Система ручек Custom: открытый мир непредустановленных полей
Куда попадают поля xs.json, которых хост не предустановил? — Нетронутыми в Custom, на чтение приложению. Этот «открытый мир» заодно вмещает запросные лимиты http-драйвера.
Обзор
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_limit | 2048 | 414 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 и т.п.).
Частые ошибки
- Симптом
- Каждое поколение скрипта течёт одной ссылкой xvalue; в отчётах памяти растёт счётчик корневого объекта.
- Причина
- Задокументированный контракт: возврат retained, обязателен xrtValueRelease — хрестоматийное применение правил владения главы 3.
- Симптом
- В конфигурации "body_limt" (опечатка); приложение получает умолчание 0 и тихо ведёт себя неверно.
- Причина
- Чтение в стиле «нет значения — считай ноль». У Custom нет валидации хостом — опечатку вскрывает только строгое чтение на стороне приложения.
uint32 n = 0;
GetValue(pCustom, "body_limit", &n);
/* нет значения — молча 0 *//* строгое чтение: нет значения — ошибка конфигурации */Упражнения
Добавьте в корень xs.json "my_app": {"greeting": "hi"}; в ServiceInit скрипта прочитайте и напечатайте.
Задайте демо body_limit=100, отправьте POST на 99 и 101 байт и зафиксируйте разницу кодов; затем path_limit=50 и длинный путь.
Напишите 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 · value — полный API доступа к дереву Custom