Files
XC_VM/docs/ru/development/http-request-handling.md
T
Divarion-D 788ec37637 refactor: replace DEVELOPMENT flag, fix bootstrap and logging
- Remove DEVELOPMENT constant from AppConfig.php; introduce DB_ACCESS_ENABLED
  (controls phpMiniAdmin access in admin panel only, not core DB connections)
- Detach bootstrap.php from www/constants.php: load core/Config/* and
  core/Logging/Logger directly; define PHP_ERRORS fallback
- Switch Logger::init() to PHP_ERRORS in bootstrap.php, stream/init.php,
  RequestGuard.php; remove leftover TODO comment
- Logger: always set error_reporting(E_ALL); UI visibility controlled
  separately via display_errors; rename $development -> $showErrors
- MigrationRunner: track applied/failed counts separately; failed migrations
  are not marked as applied
- Replace DEVELOPMENT with DB_ACCESS_ENABLED in admin settings.php and
  database.php (phpMiniAdmin gate)
- Replace DEVELOPMENT with PHP_ERRORS in CertbotCronJob
- Docs (en/ru): add DB_ACCESS_ENABLED flag description; update feature-flags,
  updates_checklist, http-request-handling; remove DEVELOPMENT references
- MIGRATION.md: mark L-1 as done, remove from backlog and wave A;
  unblock L-2; renumber steps
2026-04-19 18:13:56 +03:00

225 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Обработка HTTP-запросов и middleware
Слой обработки HTTP-запросов в XC_VM состоит из трёх компонентов: **RequestGuard** (безопасность входящих запросов), **Request** (обёртка над суперглобальными переменными) и **Router** (маршрутизация к обработчикам).
---
## Обзор потока запроса
```
Входящий HTTP-запрос
│
▼
[RequestGuard]
├── Flood-protection (блокировка по IP-файлу)
├── Host verification (проверка домена)
├── Загрузка $rSettings из кеша
└── Инициализация Logger
│
▼
[Request::capture()]
├── Санитизация $_GET, $_POST, $_COOKIE
└── Создание объекта запроса
│
▼
[Router::dispatch()]
├── Определение маршрута по URL
├── Проверка метода (GET / POST)
└── Вызов обработчика [Controller::method]
```
---
## RequestGuard
`src/core/Http/RequestGuard.php`
Выполняется автоматически при загрузке `www/constants.php` в контекстах HTTP (STREAM, ADMIN).
### Flood-protection
Блокирует IP, если существует файл `FLOOD_TMP_PATH/block_{IP}`.
```
/tmp/xc_vm/flood/block_1.2.3.4 → HTTP 403 + exit()
```
Файлы блокировки создаются отдельной защитной логикой при превышении лимита запросов.
### Host verification
Если в настройках включена проверка хоста (`verify_host = true`) и загружен кеш разрешённых доменов (`allowed_domains`):
- Запрос с не зарегистрированного домена → `generateError('INVALID_HOST')`
- IP-адреса как хост всегда разрешены
- Служебный хост `xc_vm` всегда разрешён
### Загрузка настроек
`$rSettings` загружается из файлового кеша (`CACHE_TMP_PATH/settings`) через `igbinary_unserialize()`. Это позволяет избежать обращения к БД на каждом запросе.
### Инициализация Logger
```php
Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log');
```
После RequestGuard Logger доступен во всех последующих компонентах.
---
## Request
`src/core/Http/Request.php`
Обёртка над `$_GET`, `$_POST`, `$_SERVER`, `$_COOKIE` с санитизацией входных данных.
### Создание объекта
```php
// Захватить текущий HTTP-запрос
$request = Request::capture();
// Или создать вручную
$request = new Request($_GET, $_POST, $_SERVER, $_COOKIE);
```
При создании все данные из `$_GET`, `$_POST`, `$_COOKIE` проходят санитизацию через `cleanGlobals()` и `parseIncomingRecursively()`.
### Получение данных
```php
$request->get('key'); // из $_GET
$request->post('key'); // из $_POST
$request->input('key'); // из POST или GET (POST имеет приоритет)
$request->cookie('key'); // из $_COOKIE
$request->server('key'); // из $_SERVER (без санитизации)
// С дефолтным значением
$request->get('page', 1);
$request->input('action', '');
```
### Вспомогательные методы
```php
$request->method(); // 'GET' или 'POST'
$request->isPost(): bool
$request->isGet(): bool
$request->ip(): string // IP клиента
$request->all(): array // Весь merged input
$request->has('key'): bool
$request->only(['a', 'b']): array
$request->except(['x']): array
```
### Санитизация
- Ключи: очищаются через `parseCleanKey()` — удаляются все символы кроме `a-z`, `A-Z`, `0-9`, `_`, `-`
- Значения: очищаются через `parseCleanValue()` — `strip_tags()` + обрезка пробелов
- `$_SERVER` намеренно не санитизируется (содержит пути и бинарные данные)
---
## Router
`src/core/Http/Router.php`
Маршрутизатор для администраторской панели. Заменяет паттерн `switch($rAction)` и прямые `include` в admin-страницах.
### Регистрация маршрутов
```php
$router = new Router();
// GET-маршруты (страницы)
$router->get('streams', [StreamController::class, 'index']);
$router->get('stream/add', [StreamController::class, 'add']);
// POST-маршруты (формы)
$router->post('stream/save', [StreamController::class, 'save']);
// API-маршруты (JSON-ответ)
$router->api('getStreams', [StreamController::class, 'apiList']);
$router->api('deleteStream', [StreamController::class, 'apiDelete']);
```
### Группировка маршрутов
```php
$router->group('plex', function(Router $r) {
$r->get('', [PlexController::class, 'index']);
$r->get('add', [PlexController::class, 'add']);
$r->post('save', [PlexController::class, 'save']);
$r->api('enable', [PlexController::class, 'apiEnable']);
});
// Результат: 'plex', 'plex/add', 'plex/save', 'plex/enable'
```
### Диспетчеризация
```php
// Страница по URL
$router->dispatch($page, $method);
// API-вызов
$router->dispatchApi($action);
```
### Модульная регистрация маршрутов
Каждый модуль регистрирует свои маршруты через `ModuleInterface::registerRoutes()`:
```php
class WatchModule implements ModuleInterface {
public function registerRoutes(Router $router): void {
$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']);
});
}
}
```
### Обратная совместимость
Пока `admin/api.php` использует `switch($rAction)`, Router используется как fallback в конце switch-цепочки:
```php
// В конце switch($rAction):
default:
$router->dispatchApi($rAction);
```
---
## RequestManager
`src/core/Http/RequestManager.php`
Статический фасад для доступа к параметрам запроса без создания объекта `Request`.
```php
RequestManager::get('key');
RequestManager::post('key');
RequestManager::getAll(): array
RequestManager::postAll(): array
```
Используется в компонентах, которые не получают `Request` через инъекцию.
---
## Связанные файлы
| Файл | Назначение |
| --------------------------------- | ----------------------------------- |
| `src/core/Http/RequestGuard.php` | Flood-protection, host check, settings |
| `src/core/Http/Request.php` | Обёртка над суперглобальными |
| `src/core/Http/Router.php` | Маршрутизация GET/POST/API |
| `src/core/Http/RequestManager.php`| Статический фасад для запросов |
| `src/core/Http/Response.php` | Формирование HTTP-ответов |
| `src/www/constants.php` | Загружает RequestGuard |