Files
XC_VM/docs/ru/development/http-request-handling.md
T
Divarion-D 76844fef11 docs: restructure, fix PSR-4 drift, and unify en/ru
Overhaul the Docsify documentation (English + Russian) so it matches the current
codebase and follows one consistent pattern.

Content accuracy (post-migration):
- Rewrite development/autoloader.md to PSR-4 / Composer (the old XC_Autoloader
  scanner, igbinary tmp/cache/autoload_map and registerDirectories are gone).
- PascalCase every source path (src/core -> src/Core, domain/Stream, cli/Commands,
  public/Controllers, Infrastructure/Redis, ...) across all docs.
- Replace the removed autoload.php references with vendor/autoload.php
  (build_system, bootstrap-contexts, error-handling, modules).
- ssl-generation: note that the installer now auto-generates a unique self-signed
  certificate before Nginx starts.

Common pattern (Clean & uniform):
- Strip emoji from headings; remove the in-page Navigation blocks (the Docsify
  sidebar already provides navigation).
- One H1 + intro per doc; uniform "Related files" / "Связанные файлы" section,
  added to the code-centric docs that lacked it.

Structure:
- Remove the empty stray docs/api/; move updates_checklist.md into builds/;
  link the previously-orphaned ucs-integration.md.
- Regroup the sidebars (split the oversized guides group into Developer Guides /
  Security & Access / Integrations; fold builds into Build & Release).

Augment:
- dev-workflow: Local Setup (make dev-tools) + Quality Checks (phpstan, cs, gates).
- build_system: Composer Dependencies section (committed prod-only vendor,
  committed lock, dev tools via composer install, no build-time vendor step).

en/ru parity:
- Apply the same structure, fixes and pattern to docs/ru/ (translated), including
  a new Russian ucs-integration.md. The en and ru file sets are now identical.
2026-06-26 15:56:15 +03:00

25 KiB
Raw Blame History

Обработка HTTP-запросов

Этот документ описывает, как обрабатываются HTTP-запросы в XC_VM, охватывая полный жизненный цикл — от первичного входа до маршрутизации и диспетчеризации. Существует несколько runtime-путей в зависимости от типа запроса.


Обзор

HTTP-уровень состоит из следующих основных компонентов:

Компонент Файл Роль
RequestGuard src/Core/Http/RequestGuard.php Защита до маршрутизации: защита от флуда, проверка хоста, инициализация логгера
InputValidator src/Core/Validation/InputValidator.php Санитизация ввода (cleanGlobals, parseIncomingRecursively)
RequestManager src/Core/Http/RequestManager.php Статический фасад, хранящий объединённые данные GET+POST
Request src/Core/Http/Request.php Объектно-ориентированная обёртка запроса (существует, но не используется в основном продакшен-потоке)
Router src/Core/Http/Router.php Регистрация и диспетчеризация маршрутов страниц и API
Response src/Core/Http/Response.php Статические хелперы для ответов (JSON, redirect, CORS и т.д.)
LegacyInitializer src/Core/Init/LegacyInitializer.php Legacy-bootstrap, который связывает санитизацию с RequestManager
StreamingRequestBootstrap src/Infrastructure/Bootstrap/StreamingRequestBootstrap.php Лёгкий bootstrap для стриминговых endpoint'ов

Поток запроса: страницы Admin/Panel

Точка входа: src/Public/index.php

nginx -> Public/index.php
  -> разбор URL (scope + pageName)
  -> XC_Bootstrap::boot(CONTEXT_ADMIN)
       -> floodProtection()          (блокировка забаненных IP)
       -> hostVerification()         (проверка allowed_domains)
       -> initSession()
       -> initDatabase()
       -> initLegacyCore()
            -> LegacyInitializer::initCore()
                 -> InputValidator::cleanGlobals($_GET, $_POST, $_SESSION, $_COOKIE)
                 -> InputValidator::parseIncomingRecursively($_GET) -> $rInput
                 -> InputValidator::parseIncomingRecursively($_POST, $rInput) -> RequestManager::set()
       -> initRedis()
       -> initAdminAPI()
       -> initTranslator()
  -> Загрузка маршрутов из src/Public/routes/{scope}.php
  -> Загрузка маршрутов из src/Public/routes/api.php
  -> ModuleLoader::bootAll() (scope admin/reseller, с детекцией коллизий)
  -> Router::dispatchApi($action)  (проверяется первым для страницы "api")
  -> Router::dispatch($pageName, $method)
  -> Обработчик контроллера

Ключевая деталь: санитизация ввода

Продакшен-поток admin НЕ использует Request::capture(). Вместо этого обработкой ввода управляет LegacyInitializer::initCore():

  1. InputValidator::cleanGlobals() вызывается на $_GET, $_POST, $_SESSION и $_COOKIE in-place, удаляя NULL-байты, последовательности path traversal (../) и RTL override-символы.
  2. InputValidator::parseIncomingRecursively() санитизирует ключи и значения (HTML-сущности, теги script, разделители комментариев, переводы строк) и возвращает чистый массив.
  3. Результат (GET, объединённый с POST, причём POST имеет приоритет) сохраняется через RequestManager::set().

По всему коду данные запроса читаются через RequestManager::get($key) и RequestManager::getAll(), а не через объект Request.


Поток запроса: REST API

Точка входа: src/Public/index.php (срабатывает до Router'а)

Когда XC_SCOPE равно includes/api/admin или includes/api/reseller:

nginx -> Public/index.php
  -> XC_Bootstrap::boot(CONTEXT_ADMIN)
  -> new AdminApiController() или new ResellerRestApiController()
  -> $controller->index()
  -> exit

Этот путь полностью обходит Router.


Поток запроса: стриминг

Точка входа: endpoint'ы www/stream/*.php или Public/index.php для player_api

nginx -> StreamingRequestBootstrap::init($filename)
  -> Загрузка кодов ошибок, путей, конфигурации, бинарников
  -> Защита от флуда (проверка файла block_{IP})
  -> Загрузка настроек из файлового кэша
  -> Проверка хоста
  -> Инициализация логгера
  -> StreamingBootstrap::bootstrap($filename, $settings)
       -> LegacyInitializer::initStreaming()
            -> Request::cleanGlobals() на суперглобалах
            -> Request::parseIncomingRecursively() -> $GLOBALS['rRequest']
            -> RequestManager::set($GLOBALS['rRequest'])

Стриминговый путь намеренно лёгкий. Он не загружает Router, EventDispatcher, Translator или полный ServiceContainer. Диспетчеризации маршрутов нет; у каждого стримингового endpoint'а — собственная точка входа.


RequestGuard

Файл: src/Core/Http/RequestGuard.php

Процедурный guard-скрипт, подключаемый рано в legacy-bootstrap. Выполняется только для HTTP-запросов (пропускается при наличии $_SERVER['argc'], что указывает на CLI).

Обязанности

  1. Защита от флуда — если файл FLOOD_TMP_PATH/block_{IP} существует, запрос отклоняется с HTTP 403.
  2. Загрузка кэша настроек — читает $rSettings из igbinary-сериализованного файлового кэша по пути CACHE_TMP_PATH/settings.
  3. Проверка хоста — когда $rSettings['verify_host'] равно true, проверяет, что HOST присутствует в кэшированном списке allowed_domains. Исключения: имя хоста xc_vm и любой валидный IP-адрес всегда разрешены.
  4. Флаг отображения ошибок — устанавливает константу PHP_ERRORS из $rSettings['debug_show_errors'].
  5. Инициализация логгера — вызывает Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log').

Примечание: В современном bootstrap (XC_Bootstrap::boot()) эти обязанности выполняют методы floodProtection() и hostVerification() напрямую, а не через подключение RequestGuard.php.


InputValidator

Файл: src/Core/Validation/InputValidator.php

Предоставляет статические методы для санитизации ввода и валидации на уровне действий.

Методы санитизации

Метод Описание
cleanGlobals(&$data, $iteration) In-place удаление NULL-байтов, path traversal (../) и RTL override-символов. Максимум 10 уровней рекурсии.
parseIncomingRecursively(&$data, $input, $iteration) Возвращает новый санитизированный массив. Очищает и ключи, и значения. Максимум 20 уровней рекурсии.
parseCleanKey($key) Удаляет двойные точки, паттерны __wrapped__, применяет htmlspecialchars(urldecode()).
parseCleanValue($value) Удаляет теги <script, HTML-комментарии, нормализует переводы строк, обрабатывает entity-кодирование.

Валидация действий

Метод Описание
validate($action, $data) Возвращает true/false — соответствуют ли $data минимальным требованиям для заданного API-действия.
validateOrFail($action, $data) Возвращает null, если валидно, или ['status' => STATUS_INVALID_INPUT, 'data' => $data], если нет.
confirmIDs($ids) Фильтрует массив, оставляя только положительные целочисленные ID.

RequestManager

Файл: src/Core/Http/RequestManager.php

Статический фасад, хранящий объединённые данные GET+POST. Это основной паттерн доступа к данным запроса, используемый по всему коду.

Как данные туда попадают

LegacyInitializer::initCore() вызывает:

$rInput = InputValidator::parseIncomingRecursively($_GET, array());
RequestManager::set(InputValidator::parseIncomingRecursively($_POST, $rInput));

POST-параметры переопределяют GET-параметры с тем же ключом (POST накладывается поверх GET).

API

Метод Описание
set(array $request) Сохранить весь массив данных запроса.
getAll() Получить все сохранённые данные запроса.
get(string $key, $default = null) Получить одно значение по ключу.
update(string $key, $value) Обновить один ключ в сохранённых данных.

Использование

// Чтение параметра запроса
$streamId = RequestManager::get('stream_id');

// Чтение всех параметров
$allParams = RequestManager::getAll();

// Обновление значения (редко, используется некоторыми legacy-обработчиками)
RequestManager::update('status', 'active');

Request

Файл: src/Core/Http/Request.php

Объектно-ориентированная обёртка запроса. Содержит статическую factory capture() и instance-методы для доступа к санитизированному вводу. Хотя класс существует и полностью функционален, основной продакшен-поток вместо этого использует InputValidator + RequestManager. Статические методы санитизации класса Request (cleanGlobals, parseIncomingRecursively) используются в LegacyInitializer::initStreaming() для обратной совместимости.

Создание

// Статическая factory (singleton, не используется в продакшен-потоке admin)
$request = Request::capture();

// Прямое создание
$request = new Request($_GET, $_POST, $_SERVER, $_COOKIE);

Instance-методы

Метод Сигнатура Описание
input input($key, $default = null) Получить из объединённого ввода (POST имеет приоритет над GET)
get get($key = null, $default = null) Получить из query string ($_GET). Ключ null возвращает всё.
post post($key = null, $default = null) Получить из POST-данных. Ключ null возвращает всё.
all all() Все объединённые данные ввода
has has($key) Проверить, существует ли ключ в объединённом вводе
getInt getInt($key, $default = 0) Получить значение как integer
getBool getBool($key, $default = false) Получить значение как boolean (через filter_var)
server server($key, $default = null) Получить значение из $_SERVER
cookie cookie($key, $default = null) Получить значение cookie
method method() HTTP-метод (GET, POST и т.д.)
isPost isPost() Проверить, является ли метод POST
isAjax isAjax() Проверка X-Requested-With: XMLHttpRequest
ip ip() IP клиента (проверяет X-Forwarded-For, X-Real-IP, REMOTE_ADDR)
uri uri() URI запроса
userAgent userAgent() Заголовок User-Agent
host host() Заголовок Host (с fallback на SERVER_NAME)
rawBody rawBody() Сырое тело POST из php://input
json json($assoc = true) JSON-декодированное тело POST

Статические методы санитизации (обратная совместимость)

Зеркалят InputValidator и используются на пути инициализации стриминга:

Метод Описание
cleanGlobals(&$data, $iteration) То же, что InputValidator::cleanGlobals()
parseIncomingRecursively(&$data, $input, $iteration) То же, что InputValidator::parseIncomingRecursively()
parseCleanKey($key) То же, что InputValidator::parseCleanKey()
parseCleanValue($value) То же, что InputValidator::parseCleanValue()

Router

Файл: src/Core/Http/Router.php

Singleton-роутер для диспетчеризации страниц и API. Заменяет legacy-паттерн switch($rAction).

Регистрация маршрутов

Метод Сигнатура Описание
get get($route, $handler, $options = []) Зарегистрировать GET-маршрут страницы
post post($route, $handler, $options = []) Зарегистрировать POST-маршрут формы
any any($route, $handler, $options = []) Зарегистрировать одновременно GET и POST для одного маршрута
api api($action, $handler, $options = []) Зарегистрировать API-маршрут (JSON, диспетчеризируется по имени action)
group group($prefix, $callback, $options = []) Сгруппировать маршруты под общим префиксом с общим middleware/правами

Параметр $handler принимает:

  • [ClassName::class, 'method'] — создаётся через ServiceContainer (с fallback на new)
  • Closure или callable
  • [object, 'method']

Массив $options поддерживает:

  • 'permission' => ['type', 'key'] — проверяется через Authorization::check() перед запуском обработчика
  • 'middleware' => [callable, ...] — массив callables, выполняемых после проверки прав, перед обработчиком

Примеры маршрутов

$router = Router::getInstance();

// Простые маршруты страниц
$router->get('streams', [StreamController::class, 'index']);
$router->post('stream/save', [StreamController::class, 'save']);

// API-маршрут (JSON)
$router->api('deleteStream', [StreamController::class, 'apiDelete']);

// Сгруппированные маршруты с middleware и правами
$router->group('watch', function (Router $r) {
    $r->get('', [WatchController::class, 'index']);
    $r->get('add', [WatchController::class, 'add']);
    $r->post('settings', [WatchController::class, 'saveSettings']);
    $r->api('enable', [WatchController::class, 'apiEnable']);
}, [
    'permission' => ['admin', 'watch'],
    'middleware' => [$authCheck],
]);

Нормализация маршрутов

Роутер нормализует legacy-имена страниц, преобразуя символы подчёркивания в слэши:

Вход Нормализованное
watch watch
watch_add watch/add
settings_watch settings/watch
plex_add.php plex/add

Эта нормализация применяется как при регистрации (buildRoute), так и при диспетчеризации (normalizePage), поэтому маршруты, зарегистрированные как watch/add, совпадают с именами страниц вроде watch_add.

Диспетчеризация

// Диспетчеризация страницы (вызывается из Public/index.php)
$router->dispatch($pageName, $method);    // возвращает true при совпадении

// Диспетчеризация API (вызывается для параметра action=)
$router->dispatchApi($action);            // возвращает true при совпадении

Порядок выполнения dispatch($page, $method)

  1. Нормализовать $page (подчёркивания → слэши, удалить .php).
  2. Найти в POST-маршрутах (если метод POST) или GET-маршрутах. Если POST-маршрут не найден, идёт fallback на GET-маршруты.
  3. Проверка прав через checkPermission(). При отказе вызывается denyAccess() (редирект или 403).
  4. Выполнение middleware. Каждый callable из массива middleware вызывается. Если любой возвращает false, выполнение останавливается.
  5. Вызов обработчика через callHandler().

Порядок выполнения dispatchApi($action)

  1. Найти в API-маршрутах по имени действия.
  2. Проверка прав. При отказе выводится {"result": false} и происходит выход.
  3. Вызов обработчика. Middleware не выполняется.

Важно: dispatchApi() НЕ запускает middleware. Это намеренное отличие от диспетчеризации страниц.

Регистрация модульных маршрутов

Модули регистрируют маршруты через ModuleInterface::registerRoutes(). Роутер поддерживает безопасный режим регистрации, предотвращающий перезапись модулями core-маршрутов:

$router->beginModuleRegistration();
// Маршруты модулей регистрируются здесь — дубликаты молча пропускаются
$moduleLoader->bootAll($container, $router);
$router->endModuleRegistration();

// Проверка коллизий (логируется в режиме development)
$collisions = $router->drainRouteCollisions();

В режиме регистрации модулей (preserveExistingRoutes = true), если модуль пытается зарегистрировать уже существующий маршрут, существующий сохраняется, а коллизия фиксируется. drainRouteCollisions() возвращает и очищает накопленные коллизии в виде массива ['type' => 'get'|'post'|'api', 'key' => 'route/path'].

Интроспекция

Метод Описание
hasRoute($page) Проверить, существует ли маршрут страницы (GET или POST)
hasApiRoute($action) Проверить, существует ли API-маршрут
getRoutes() Вернуть все зарегистрированные ключи маршрутов как ['get' => [...], 'post' => [...], 'api' => [...]]

Response

Файл: src/Core/Http/Response.php

Статический хелпер для отправки HTTP-ответов. Заменяет разрозненные паттерны header() + echo + exit().

Метод Сигнатура Описание
json json($data, $statusCode = 200, $options = 0) Отправить JSON-ответ и выйти
jsonError jsonError($message, $statusCode = 400, $extra = []) Отправить JSON-ошибку и выйти
redirect redirect($url, $statusCode = 302) Отправить редирект и выйти
notFound notFound($message = 'Not Found') Отправить 404 и выйти
header header($name, $value) Установить один заголовок ответа
cors cors() Установить CORS-заголовки (Access-Control-Allow-Origin: *)
noCache noCache() Установить no-cache заголовки (используется для HLS-плейлистов)
raw raw($content, $contentType, $statusCode) Отправить сырой контент с указанным content type и выйти
empty empty($statusCode = 204) Отправить пустой ответ и выйти

Контексты bootstrap

XC_Bootstrap::boot($context) обеспечивает контекстно-зависимую инициализацию. Каждый контекст надстраивается над предыдущим:

Контекст Что инициализируется
CONTEXT_MINIMAL Autoload + константы + конфигурация + Logger. Без подключения к БД.
CONTEXT_CLI + База данных + LegacyInitializer::initCore() (санитизация ввода, настройки, пути FFmpeg). Опционально Redis.
CONTEXT_STREAM + Только база данных (лёгкий, без LegacyInitializer). Стриминговые endpoint'ы используют StreamingRequestBootstrap.
CONTEXT_ADMIN + Сессия + база данных + LegacyInitializer::initCore() + Redis + Admin API + Translator + admin globals. Полная инициализация.

Все HTTP-контексты (не CLI) также выполняют защиту от флуда и проверку хоста перед контекстно-зависимой инициализацией.


Связанные файлы

Файл Назначение
src/Core/Http/RequestGuard.php Защита до маршрутизации и инициализация логгера (legacy include)
src/Core/Http/Request.php OOP-обёртка запроса с методами санитизации
src/Core/Http/Router.php Регистрация и диспетчеризация маршрутов
src/Core/Http/RequestManager.php Статический фасад данных запроса (основной паттерн доступа)
src/Core/Http/Response.php Хелперы вывода ответов
src/Core/Validation/InputValidator.php Санитизация ввода и валидация действий
src/Core/Init/LegacyInitializer.php Legacy core-инициализация (связывает санитизацию с RequestManager)
src/Infrastructure/Bootstrap/StreamingRequestBootstrap.php Лёгкий bootstrap стримингового endpoint'а
src/Streaming/StreamingBootstrap.php Подключение к БД и legacy-инициализация стриминга
src/bootstrap.php Унифицированный bootstrap (класс XC_Bootstrap)
src/Public/index.php Front-контроллер для admin/reseller/player/API
src/Public/routes/admin.php Определения маршрутов admin-страниц
src/Public/routes/reseller.php Определения маршрутов reseller-страниц
src/Public/routes/player.php Определения маршрутов player-страниц