feat: implement Layer 2 per-file transit, RAM session, mock buffer, and tests (v0.2.0)
This commit is contained in:
@@ -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`.
|
||||
Reference in New Issue
Block a user