Files
XC_VM/docs/ru/development/http-request-handling.md
T
Divarion_D dbf535bcc5 docs(translate): robust {N} sentinels + possessive handling; regenerate ru
Fix Markdown-mangling artifacts the free web engine (yandex) produced in the
committed docs/ru, and regenerate the whole tree cleanly (0 fallbacks):

- Sentinel format @@N@@ -> {N}. MT engines are trained to preserve curly
  format-string placeholders, so {N} survives code-heavy lines where @@N@@ (and
  ZZZ…ZZZ, which also duplicated its Z) were split/moved — e.g. the stray
  "@0@@" in the FAQ and "load balancerZ" in the README are gone.
- Possessive: a trailing English `'s` is consumed INTO the masked span and
  dropped on restore. Every sentinel format breaks when a bare `'s` sits right
  after it, and Russian has no possessive `'s`.
- Validate + retry: after restore, any leftover brace fragment triggers a retry
  (the engine is non-deterministic); after a few failures the line stays English
  so a broken token is never emitted.
- Glossary += KeyDB, yt-dlp, Ubuntu, iptables, MAGSCAN.

Regenerated docs/ru (37 files, translators/yandex): no residual sentinels,
mkdocs build --strict clean.
2026-08-21 16:31:01 +03:00

407 lines
26 KiB
Markdown

# Обработка HTTP-запросов
Этот документ описывает, как обрабатываются HTTP-запросы в XC_VM, охватывая полный жизненный цикл от первоначального ввода до маршрутизации и отправки. В зависимости от типа запроса существует несколько путей выполнения.
---
## Обзор
Уровень HTTP построен из этих основных компонентов:
|Компонент|Файл|Роль|
| --- | --- | --- |
| `RequestGuard` | `src/Core/Http/RequestGuard.php` |Безопасность перед маршрутизацией: защита от наводнений, проверка хоста, запуск регистратора|
| `InputValidator` | `src/Core/Validation/InputValidator.php` |Очистка входных данных (очистка глобальных объектов, повторный анализ)|
| `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` |Устаревший bootstrap, который подключает очистку к `RequestManager`|
| `StreamingRequestBootstrap` | `src/Infrastructure/Bootstrap/StreamingRequestBootstrap.php` |Облегченный bootstrap для конечных точек потоковой передачи|
---
## Поток запросов: Страницы администратора/панели управления
Точка входа: `src/Public/index.php`
```text
nginx -> Public/index.php
-> URL parsing (scope + pageName)
-> XC_Bootstrap::boot(CONTEXT_ADMIN)
-> floodProtection() (block banned IPs)
-> hostVerification() (check 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()
-> Load routes from src/Public/routes/{scope}.php
-> Load routes from src/Public/routes/api.php
-> ModuleLoader::bootAll() (admin/reseller scope, with collision detection)
-> Router::dispatchApi($action) (checked first for "api" page)
-> Router::dispatch($pageName, $method)
-> Controller handler
```
### Ключевая деталь: санитарная обработка входных данных
В процессе производственного администрирования не используется `Request::capture()`. Вместо этого `LegacyInitializer::initCore()` управляет обработкой входных данных:
1. `InputValidator::cleanGlobals()` вызывается при `$_GET`, `$_POST`, `$_SESSION`, и `$_COOKIE` на месте, удаляя нулевые байты, последовательности обхода пути (`../`) и символы переопределения RTL.
2. `InputValidator::parseIncomingRecursively()` очищает ключи и значения (HTML-объекты, теги скриптов, разделители комментариев, окончания строк) и возвращает чистый массив.
3. Результат (объединяется с сообщением, сообщение имеет приоритет) сохраняется через `RequestManager::set()`.
Во всей кодовой базе доступ к данным запроса осуществляется через `RequestManager::get($key)` и `RequestManager::getAll()`, а не через объект `Request`.
---
## Поток запросов: REST API
Точка входа: `src/Public/index.php` (короткое замыкание перед маршрутизатором)
Когда `XC_SCOPE` равно `includes/api/admin` или `includes/api/reseller`:
```text
nginx -> Public/index.php
-> XC_Bootstrap::boot(CONTEXT_ADMIN)
-> new AdminApiController() or new ResellerRestApiController()
-> $controller->index()
-> exit
```
Этот путь полностью обходит маршрутизатор.
---
## Поток запросов: Потоковая передача
Точка входа: `www/stream/*.php` конечных точек или `Public/index.php` для `player_api`
```text
nginx -> StreamingRequestBootstrap::init($filename)
-> Load error codes, paths, config, binaries
-> Flood protection (check block_{IP} file)
-> Load settings from file cache
-> Host verification
-> Logger init
-> StreamingBootstrap::bootstrap($filename, $settings)
-> LegacyInitializer::initStreaming()
-> Request::cleanGlobals() on superglobals
-> Request::parseIncomingRecursively() -> $GLOBALS['rRequest']
-> RequestManager::set($GLOBALS['rRequest'])
```
Путь потоковой передачи намеренно упрощен. Он не загружает маршрутизатор, EventDispatcher, транслятор или полный сервисный контейнер. Диспетчеризация маршрутов отсутствует; каждая конечная точка потоковой передачи имеет выделенную точку входа.
---
## `RequestGuard`
Файл: `src/Core/Http/RequestGuard.php`
Процедурный защитный скрипт, включенный ранее в устаревший 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)` |Удаление нулевых байтов на месте, обход пути (`../`) и символы переопределения RTL. Максимум 10 уровней рекурсии.|
| `parseIncomingRecursively(&$data, $input, $iteration)` |Возвращает новый обработанный массив. Очищаются как ключи, так и значения. Максимальное количество уровней рекурсии - 20.|
| `parseCleanKey($key)` |Удаляет двойные точки, узоры `__wrapped__`, применяет `htmlspecialchars(urldecode())`.|
| `parseCleanValue($value)` |Удаляет теги `<script`, HTML-комментарии, нормализует разрывы строк, обрабатывает кодировку объектов.|
### Подтверждение действия
|Метод|Описание|
| --- | --- |
| `validate($action, $data)` |Возвращает `true`/`false` для определения того, соответствует ли `$data` минимальным требованиям для данного действия API.|
| `validateOrFail($action, $data)` |Возвращает `null`, если допустимо, или `['status' => STATUS_INVALID_INPUT, 'data' => $data]`, если недопустимо.|
| `confirmIDs($ids)` |Фильтрует массив только по целочисленным положительным идентификаторам.|
---
## `RequestManager`
Файл: `src/Core/Http/RequestManager.php`
Статический интерфейс, в котором хранятся объединенные данные запроса GET+POST. Это основной шаблон доступа к данным запроса, используемый во всей базе кода.
### Как данные попадают в
`LegacyInitializer::initCore()` вызовы:
```php
$rInput = InputValidator::parseIncomingRecursively($_GET, array());
RequestManager::set(InputValidator::parseIncomingRecursively($_POST, $rInput));
```
Параметры POST переопределяют параметры GET с помощью того же ключа (POST объединяется поверх GET).
### интерфейс прикладного программирования
|Метод|Описание|
| --- | --- |
| `set(array $request)` |Храните весь массив данных запроса.|
| `getAll()` |Извлеките все сохраненные данные запроса.|
| `get(string $key, $default = null)` |Извлеките одно значение по ключу.|
| `update(string $key, $value)` |Обновите один ключ в сохраненных данных.|
### Использование
```php
// Read a request parameter
$streamId = RequestManager::get('stream_id');
// Read all parameters
$allParams = RequestManager::getAll();
// Update a value (rare, used by some legacy handlers)
RequestManager::update('status', 'active');
```
---
## `Request`
Файл: `src/Core/Http/Request.php`
Объектно-ориентированная оболочка запроса. Содержит статическую фабрику `capture()` и методы экземпляра для доступа к обработанным входным данным. Хотя класс существует и полностью функционален, в основном производственном потоке вместо него используются `InputValidator` + `RequestManager`. Методы статической очистки класса `Request` (`cleanGlobals`, `parseIncomingRecursively`) используются `LegacyInitializer::initStreaming()` для обеспечения обратной совместимости.
### Строительство
```php
// Static factory (singleton, not used in production admin flow)
$request = Request::capture();
// Direct construction
$request = new Request($_GET, $_POST, $_SERVER, $_COOKIE);
```
### Методы экземпляра
|Метод|Подпись|Описание|
| --- | --- | --- |
| `input` | `input($key, $default = null)` |Получить из объединенных входных данных (приоритет POST над GET)|
| `get` | `get($key = null, $default = null)` |Получаем из строки запроса (`$_GET`). `null` ключ возвращает все.|
| `post` | `post($key = null, $default = null)` |Получаем из данных POST. `null` ключ возвращает все.|
| `all` | `all()` |Все объединенные входные данные|
| `has` | `has($key)` |Проверьте, существует ли ключ в объединенном вводе|
| `getInt` | `getInt($key, $default = 0)` |Получить значение в виде целого числа|
| `getBool` | `getBool($key, $default = false)` |Получить значение в виде логического значения (через `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()` |Заголовок пользовательского агента|
| `host` | `host()` |Заголовок узла (возвращается к `SERVER_NAME`)|
| `rawBody` | `rawBody()` |Необработанный текст сообщения из `php://input`|
| `json` | `json($assoc = true)` |Текст СООБЩЕНИЯ, декодированный в формате JSON|
### Методы статической дезинфекции (обратная совместимость)
Они отражают `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`
Одноэлементный маршрутизатор для отправки страниц и API. Заменяет устаревший шаблон `switch($rAction)`.
### Регистрация маршрута
|Метод|Подпись|Описание|
| --- | --- | --- |
| `get` | `get($route, $handler, $options = [])` |Зарегистрируйте маршрут получения страницы|
| `post` | `post($route, $handler, $options = [])` |Зарегистрируйте маршрут почтовой формы|
| `any` | `any($route, $handler, $options = [])` |Зарегистрируйте как GET, так и POST для одного и того же маршрута|
| `api` | `api($action, $handler, $options = [])` |Зарегистрируйте маршрут API (JSON, отправляемый по имени действия)|
| `group` | `group($prefix, $callback, $options = [])` |Группируйте маршруты под общим префиксом с общим промежуточным программным обеспечением/разрешениями|
Параметр `$handler` принимает:
- `[ClassName::class, 'method']` -- создается через ServiceContainer (с возможностью возврата к `new`)
- Завершающий или вызываемый
- `[object, 'method']`
Массив `$options` поддерживает:
- `'permission' => ['type', 'key']` -- проверяется с помощью `Authorization::check()` перед запуском обработчика
- `'middleware' => [callable, ...]` -- массив вызываемых объектов, выполняемых после проверки прав доступа, перед обработчиком
### Примеры маршрутов
```php
$router = Router::getInstance();
// Simple page routes
$router->get('streams', [StreamController::class, 'index']);
$router->post('stream/save', [StreamController::class, 'save']);
// API route (JSON)
$router->api('deleteStream', [StreamController::class, 'apiDelete']);
// Grouped routes with middleware and permissions
$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],
]);
```
### Нормализация маршрута
Маршрутизатор нормализует устаревшие названия страниц, преобразуя символы подчеркивания в косые черты:
|Ввод|Нормализованный|
| --- | --- |
| `watch` | `watch` |
| `watch_add` | `watch/add` |
| `settings_watch` | `settings/watch` |
| `plex_add.php` | `plex/add` |
Эта нормализация применяется как во время регистрации (`buildRoute`), так и во время отправки (`normalizePage`), поэтому маршруты, зарегистрированные как `watch/add`, соответствуют названиям страниц, подобным `watch_add`.
### Отправка
```php
// Page dispatch (called from Public/index.php)
$router->dispatch($pageName, $method); // returns true if matched
// API dispatch (called for action= parameter)
$router->dispatchApi($action); // returns true if matched
```
#### `dispatch($page, $method)` порядок исполнения
1. Нормализовать `$page` (символы подчеркивания заменить косыми чертами, зачеркнуть `.php`).
2. Посмотрите в разделе POST routes (если используется метод POST) или GET routes. Если POST route не найден, вернитесь к GET routes.
3. **Проверка прав доступа ** через `checkPermission()`. Если отказано, вызывает `denyAccess()` (перенаправление или 403).
4. **Выполнение промежуточного программного обеспечения**. Вызывается каждый вызываемый объект в массиве `middleware`. Если какой-либо из них возвращает значение `false`, выполнение прекращается.
5. **Вызов обработчика** через `callHandler()`.
#### `dispatchApi($action)` порядок исполнения
1. Найдите в API маршруты по названию действия.
2. **Проверка разрешений**. Если отказано, выводит `{"result": false}` и завершает работу.
3. **Вызов обработчика**. Промежуточное программное обеспечение не выполняется.
Важно: `dispatchApi()` не запускает промежуточное программное обеспечение. Это намеренное отличие от отправки страниц.
### Регистрация маршрута модуля
Модули регистрируют маршруты с помощью `ModuleInterface::registerRoutes()`. Маршрутизатор поддерживает безопасный режим регистрации, предотвращающий перезапись модулями основных маршрутов:
```php
$router->beginModuleRegistration();
// Module routes registered here -- duplicates are silently skipped
$moduleLoader->bootAll($container, $router);
$router->endModuleRegistration();
// Check for collisions (logged in development mode)
$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()` |Установка заголовков без кэширования (используется для плейлистов HLS)|
| `raw` | `raw($content, $contentType, $statusCode)` |Отправьте необработанный контент с указанием типа контента и завершите работу|
| `empty` | `empty($statusCode = 204)` |Отправьте пустой ответ и завершите работу|
---
## Контексты начальной загрузки
`XC_Bootstrap::boot($context)` обеспечивает контекстно-зависимую инициализацию. Каждый контекст основывается на предыдущем:
|Контекст|Что он инициализирует|
| --- | --- |
| `CONTEXT_MINIMAL` |Автозагрузка + константы + конфигурация + регистратор. Нет подключения к базе данных.|
| `CONTEXT_CLI` |+ База данных + `LegacyInitializer::initCore()` (очистка входных данных, настройки, пути FFmpeg). Необязательно Redis.|
| `CONTEXT_STREAM` |+ Только база данных (упрощенная, без `LegacyInitializer`). Конечные точки потоковой передачи используют вместо этого `StreamingRequestBootstrap`.|
| `CONTEXT_ADMIN` |+ Сессия + База данных + `LegacyInitializer::initCore()` + Redis + API администратора + Переводчик + глобальные настройки администратора. Полная инициализация.|
Все HTTP-контексты (не CLI) также запускают защиту от наводнений и проверку хоста перед инициализацией, зависящей от контекста.
---
## Связанные файлы
|Файл|Цель|
| --- | --- |
| `src/Core/Http/RequestGuard.php` |Безопасность предварительной маршрутизации и инициализация регистратора (включая устаревшие)|
| `src/Core/Http/Request.php` |Оболочка запроса ООП с методами санитарной обработки|
| `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` |Инициализация устаревшего ядра (очистка проводов в RequestManager)|
| `src/Infrastructure/Bootstrap/StreamingRequestBootstrap.php` |Облегченный загрузчик конечной точки потоковой передачи|
| `src/Streaming/StreamingBootstrap.php` |Потоковое подключение к базе данных и устаревшая инициализация|
| `src/bootstrap.php` |Унифицированный bootstrap (класс`XC_Bootstrap`)|
| `src/Public/index.php` |Внешний контроллер для администратора/реселлера/игрока/API|
| `src/Public/routes/admin.php` |Определения маршрутов на странице администратора|
| `src/Public/routes/reseller.php` |Определения маршрута на странице реселлера|
| `src/Public/routes/player.php` |Определения маршрута на странице игрока|