feat: implement Layer 2 per-file transit, RAM session, mock buffer, and tests (v0.2.0)

This commit is contained in:
“Naeel”
2026-09-06 12:01:56 +03:00
parent 0ea7ba058c
commit 112c84f11a
34 changed files with 2200 additions and 162 deletions
@@ -0,0 +1,69 @@
# 2026-09-06: Реализация и верификация Слоя 2 (пофайловый транзит через RAM)
## 1. Контекст задачи
В рамках репозитория `upload-platform` выполнена реализация Слоя 2 (транзитная доставка файлов в сессию бэкенда через внешний буфер) поверх завершённого Слоя 1 (File Picker v0.1.13) с поднятием версии платформы до `0.2.0`.
---
## 2. Ключевые архитектурные решения и реализация
### 1. Пофайловый транзит (Per-File Transit)
Вместо накопления всей пачки файлов на ВМ реализован потоковый пофайловый цикл:
1. Браузер берёт файл $k$ и отправляет его методом `PUT` в буфер на ВМ (`putToVm` через XHR со стримингом прогресса и поддержкой `AbortSignal`).
2. Браузер сразу делает `POST /api/upload_refs` только для этого одного файла $k$.
3. Бэкенд забирает файл исходящим потоковым `GET` (`httpx.stream`) прямо в оперативную память сессии (`upload/backend/session`).
4. Бэкенд немедленно отправляет `DELETE` на буфер ВМ.
5. Файл на ВМ удалён, буфер чист. Браузер переходит к файлу $k+1$.
- **Результат**: в буфере на ВМ в любой момент времени находится максимум один файл; объем потребляемой буфером памяти минимален.
### 2. Хранение строго в оперативной памяти (RAM-only)
- На бэкенде: файлы сессии хранятся в RAM-структуре `_sessions` (`upload/backend/session/state.py`). Никаких временных файлов на диске.
- Защита памяти: настраиваемые лимиты `MAX_FILE_BYTES` (по умолчанию 50 МБ) и `MAX_SESSION_BYTES` (по умолчанию 500 МБ).
- Автоматическая очистка: TTL-таймер (по умолчанию 30 мин), функции `touch`, `pause_ttl`, `resume_ttl`, `cleanup`.
### 3. Защита и параметры (Zero Hardcode & Security)
- **SSRF-защита**: `create_upload_refs_blueprint` валидирует входящие URL по префиксу `vmUploadPrefix` (поддерживаются абсолютные URL для прода и относительные для локального мока). Запросы к сторонним хостам отсекаются.
- **Path Traversal защита**: модуль `safe_name` нормализует пути, запрещает `..` и сохраняет безопасные относительные подпапки.
- **Мягкая отмена**: поддержка `AbortSignal` на фронтенде и `threading.Event` на бэкенде (`request_cancel`, `get_cancel_event`).
- **Слой 3 (эмуляция/интеграция)**: в `create_upload_refs_blueprint` добавлен колбэк `onFileReceived(sid, name, content)`, вызываемый при успешной доставке файла в RAM.
### 4. Автономный mock-буфер и тестовый стенд
- В `site/app.py` встроен in-memory mock WebDAV (`PUT`, `GET`, `DELETE` по пути `/mock-buffer/<key>`), хранящий данные в RAM.
- Позволяет запускать тесты и локальный demo-стенд на 100% автономно без доступа к внешней сети или боевой ВМ.
- В шаблоне `site/templates/index.html` добавлена панель запуска Слоя 2, отображение прогресса пофайловой передачи и просмотр файлов, сохранённых в RAM сессии.
---
## 3. Автоматическое тестирование
Создан полный набор автоматических тестов (19 тестов, 100% PASS):
1. **Бэкенд тесты (`pytest tests/ -v`, 10 тестов, 0.99s)**:
- `test_safe_name_simple` — проверка корректных путей и слэшей.
- `test_safe_name_traversal` — отсечение атак `..`, абсолютных путей, пустых строк.
- `test_session_lifecycle` — полный жизненный цикл сессии (создание, добавление, чтение, TTL, отмена, cleanup).
- `test_session_limits` — проверка ограничения суммарного размера сессии в RAM.
- `test_upload_refs_pull_and_delete` — проверка pull по исходящему GET, удаления из буфера, вызова колбэка Слоя 3.
- `test_upload_refs_ssrf_protection` — блокировка попыток pull с недоверенных хостов (SSRF).
- `test_health` — liveness-проверка платформы.
- `test_index_page` — проверка отдачи UI стенда.
- `test_mock_buffer_crud` — операции PUT, GET, DELETE и статус mock-буфера.
- `test_full_transit_flow_mock` — сквозной тест пофайлового транзита от mock-буфера до сессии Flask.
2. **Фронтенд тесты (`npm test`, `node:test`, 9 тестов, 329ms)**:
- `putToVm: успешная отправка` — проверка корректности HTTP PUT с сырым бинарным телом.
- `putToVm: ошибка HTTP статуса` — обработка ответов 4xx/5xx.
- `putToVm: сетевая ошибка` — обработка XHR onerror.
- `putToVm: таймаут` — обработка XHR ontimeout.
- `putToVm: прерывание через AbortSignal` — мгновенный abort текущего XHR.
- `uploadViaVM: пофайловый транзит` — проверка, что $N$ файлов вызывают $N$ парных запросов PUT + upload_refs последовательно, передавая `session_id`.
- `uploadViaVM: поддержка формата FilePicker.getFiles()` — совместимость со Слой 1.
- `uploadViaVM: обработка ошибки PUT` — остановка конвейера и возврат ошибки.
- `uploadViaVM: прерывание через signal` — отмена всего пофайлового цикла.
---
## 4. Версионирование и сборка
- Версия поднята с `0.1.13` до `0.2.0` в `package.json` и `site/app.py`.
- Собраны актуальные дистрибутивные бандлы `dist/file-picker.esm.js` (40.1 KB) и `dist/file-picker.iife.js` (43.4 KB).
- Документация в `README.md` и `upload/README.md` полностью обновлена с пошаговой инструкцией интеграции в `drhider` и `contractor`.