From 2affe8480ea156316821a35af7a22a2c786ab768 Mon Sep 17 00:00:00 2001 From: amanzhola Date: Thu, 30 Apr 2026 03:07:49 +0500 Subject: [PATCH 01/60] Move static files lessons and README to final-task-pr --- README.md | 12 + .../CMakeLists.txt | 18 + .../README.md | 856 ++++++++++++++++++ .../src/main.cpp | 171 ++++ .../static/app.js | 7 + .../static/index.html | 13 + .../static/style.css | 14 + .../CMakeLists.txt | 18 + .../README.md | 461 ++++++++++ .../src/main.cpp | 178 ++++ .../static/app.js | 7 + .../static/audio/front_center.wav | Bin 0 -> 137134 bytes .../static/images/road_t.png | Bin 0 -> 1088 bytes .../static/images/road_tr.png | Bin 0 -> 1369 bytes .../static/images/road_vh.png | Bin 0 -> 667 bytes .../static/images/tv_architecture.png | Bin 0 -> 104953 bytes .../static/index.html | 56 ++ .../static/style.css | 34 + .../static/video/reset_position.mp4 | Bin 0 -> 240740 bytes .../static/video/track_horizontal_plane.mp4 | Bin 0 -> 972478 bytes .../static/video/track_vertical_plane.mp4 | Bin 0 -> 631994 bytes .../CMakeLists.txt | 18 + .../README.md | 714 +++++++++++++++ .../src/main.cpp | 305 +++++++ .../static/app.js | 7 + .../static/audio/front_center.wav | Bin 0 -> 137134 bytes .../static/hello world.txt | 3 + .../static/images/road_t.png | Bin 0 -> 1088 bytes .../static/images/road_tr.png | Bin 0 -> 1369 bytes .../static/images/road_vh.png | Bin 0 -> 667 bytes .../static/images/tv_architecture.png | Bin 0 -> 104953 bytes .../static/index.html | 79 ++ .../static/page with space.html | 12 + .../static/style.css | 44 + .../static/video/reset_position.mp4 | Bin 0 -> 240740 bytes .../static/video/track_horizontal_plane.mp4 | Bin 0 -> 972478 bytes .../static/video/track_vertical_plane.mp4 | Bin 0 -> 631994 bytes .../CMakeLists.txt | 18 + .../README.md | 661 ++++++++++++++ .../secret.txt | 2 + .../src/main.cpp | 311 +++++++ .../static/app.js | 7 + .../static/audio/front_center.wav | Bin 0 -> 137134 bytes .../static/file..name.txt | 3 + .../static/hello world.txt | 3 + .../static/images/road_t.png | Bin 0 -> 1088 bytes .../static/images/road_tr.png | Bin 0 -> 1369 bytes .../static/images/road_vh.png | Bin 0 -> 667 bytes .../static/images/tv_architecture.png | Bin 0 -> 104953 bytes .../static/index.html | 58 ++ .../static/page with space.html | 12 + .../static/style.css | 44 + .../static/video/reset_position.mp4 | Bin 0 -> 240740 bytes .../static/video/track_horizontal_plane.mp4 | Bin 0 -> 972478 bytes .../static/video/track_vertical_plane.mp4 | Bin 0 -> 631994 bytes .../CMakeLists.txt | 18 + .../README.md | 346 +++++++ .../secret.txt | 2 + .../src/main.cpp | 344 +++++++ .../static/app.js | 7 + .../static/audio/front_center.wav | Bin 0 -> 137134 bytes .../static/file..name.txt | 3 + .../static/hello world.txt | 3 + .../static/images/road_t.png | Bin 0 -> 1088 bytes .../static/images/road_tr.png | Bin 0 -> 1369 bytes .../static/images/road_vh.png | Bin 0 -> 667 bytes .../static/images/tv_architecture.png | Bin 0 -> 104953 bytes .../static/index.html | 58 ++ .../static/page with space.html | 12 + .../static/style.css | 44 + .../static/video/reset_position.mp4 | Bin 0 -> 240740 bytes .../static/video/track_horizontal_plane.mp4 | Bin 0 -> 972478 bytes .../static/video/track_vertical_plane.mp4 | Bin 0 -> 631994 bytes 73 files changed, 4983 insertions(+) create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/CMakeLists.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/README.md create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/src/main.cpp create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/app.js create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/index.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/style.css create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/CMakeLists.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/README.md create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/src/main.cpp create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/app.js create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/audio/front_center.wav create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/images/road_t.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/images/road_tr.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/images/road_vh.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/images/tv_architecture.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/index.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/style.css create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/video/reset_position.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/video/track_horizontal_plane.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/static/video/track_vertical_plane.mp4 create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/CMakeLists.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/README.md create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/src/main.cpp create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/app.js create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/audio/front_center.wav create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/hello world.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/images/road_t.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/images/road_tr.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/images/road_vh.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/images/tv_architecture.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/index.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/page with space.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/style.css create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/video/reset_position.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/video/track_horizontal_plane.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/static/video/track_vertical_plane.mp4 create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/CMakeLists.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/README.md create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/secret.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/src/main.cpp create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/app.js create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/audio/front_center.wav create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/file..name.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/hello world.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/images/road_t.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/images/road_tr.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/images/road_vh.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/images/tv_architecture.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/index.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/page with space.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/style.css create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/video/reset_position.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/video/track_horizontal_plane.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/static/video/track_vertical_plane.mp4 create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/CMakeLists.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/README.md create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/secret.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/src/main.cpp create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/app.js create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/audio/front_center.wav create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/file..name.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/hello world.txt create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/images/road_t.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/images/road_tr.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/images/road_vh.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/images/tv_architecture.png create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/index.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/page with space.html create mode 100644 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/style.css create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/video/reset_position.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/video/track_horizontal_plane.mp4 create mode 100755 lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/static/video/track_vertical_plane.mp4 diff --git a/README.md b/README.md index 98bb81a..02a1f95 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,18 @@ cppbackend/ --- +## 🧪 Sprint 2 — Статические файлы и веб-клиент + +| 🧪 Папка урока второго спринта, связанного с отдачей статических файлов и клиентской частью | 📖 Что это за урок и какую задачу он решает в рамках развития HTTP-сервера | 🧠 Почему этот урок относится к следующему этапу после базового HTTP-сервера | ✅ Переход | +| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------- | +| `lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files` | Урок по отдаче статических файлов: HTML, CSS, JavaScript, MIME-типы и защита путей | Переводит сервер из API-режима в полноценный веб-сервер с клиентской частью | [README](lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/README.md) | +| `lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2` | Расширение урока: поддержка изображений, аудио и видео, расширенные MIME-типы | Добавляет полноценную работу с media-ресурсами (PNG, WAV, MP4) и делает сервер ближе к production | [README](lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/README.md) | +| `lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3` | Расширение урока: разделение REST API и статических файлов, URI, URL-encoding и percent-encoding | Добавляет маршрутизацию между API и static, JSON-ответы, декодирование URL-encoded путей и работу с файлами с пробелами | [README](lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part3/README.md) | +| `lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4` | Расширение урока: безопасный доступ к файлам, path traversal, URL Decode и canonical-проверка пути | Показывает правильную защиту static-каталога через `weakly_canonical` и проверку, что путь остаётся внутри `static_root` | [README](lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part4/README.md) | +| `lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5` | Расширение урока: отдача файлов через `boost::beast::http::file_body` вместо чтения в `std::string` | Делает отдачу больших static-файлов правильнее и ближе к реальному HTTP-серверу, особенно для видео, аудио и крупных изображений | [README](lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part5/README.md) | + +--- + # 🚀 SPRINT 1 ## 📦 Что находится в sprint1 diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/CMakeLists.txt b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/CMakeLists.txt new file mode 100644 index 0000000..72e0260 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.11) + +project(web_server_lesson_18_static_files CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(static_server + src/main.cpp +) + +target_include_directories(static_server PRIVATE + /home/ubuntu/.conan/data/boost/1.86.0/_/_/package/4c73f888ee1301ffed212b5fd37391dd45ff1c09/include +) + +target_link_libraries(static_server + pthread +) diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/README.md b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/README.md new file mode 100644 index 0000000..58dd6c2 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/README.md @@ -0,0 +1,856 @@ +# 🌐 Урок: Отдача статических файлов + +--- + +## 🎯 Цель урока + +| 📌 Что именно изучается в этом практическом уроке по отдаче статических файлов через HTTP-сервер на C++ | 📖 Подробное объяснение цели урока и того, что сервер научился отдавать клиенту через браузер | 🧠 Почему это важно для полноценной браузерной игры и чем это отличается от REST API-сервера | ✅ Итог | +| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| Отдача статических файлов | В этом практическом уроке мы научили HTTP-сервер отдавать статические файлы: HTML-страницы, CSS-стили, JavaScript-сценарии, изображения и другие файлы | До этого сервер мог работать как REST API-сервер, то есть отдавать данные, например JSON с игровыми картами. Но для полноценной игры в браузере нужен ещё игровой клиент | Сервер теперь может отдавать не только API-данные, но и файлы клиентской части | +| Игровой клиент | Игровой клиент обычно состоит из `index.html`, `style.css`, `app.js`, `images/*` | Браузер загружает HTML, затем отдельно CSS и JavaScript, а уже они формируют внешний вид и поведение страницы | Клиентская часть может жить рядом с C++ сервером | +| Проверка результата | После этой практики браузер может открыть страницу `http://localhost:8080/` и получить HTML, CSS и JavaScript с нашего C++ сервера | Это главный практический результат урока | `http://localhost:8080/` открывает страницу с C++ сервера | + +--- + +## 📂 Что такое статические файлы + +| 📌 Понятие статических файлов и примеры ресурсов, которые сервер отдаёт клиенту без генерации нового содержимого | 📖 Подробное объяснение и список примеров файлов, которые относятся к статическим ресурсам | 🧠 Как браузер запрашивает такие файлы и где сервер должен искать их на диске | ✅ Итог | +| ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | +| Статические файлы | Статические файлы — это файлы, которые сервер отдаёт клиенту без генерации нового содержимого | Примеры: `index.html`, `style.css`, `app.js`, `logo.png`, `favicon.ico` | Сервер просто находит файл, читает его и отдаёт в HTTP-ответе | +| Пример запроса | Если браузер запрашивает `GET /index.html`, сервер должен найти файл `static/index.html` | После этого сервер должен прочитать файл и отправить HTTP-ответ с корректным `Content-Type` | Запрос `/index.html` соответствует файлу `static/index.html` | +| Пример ответа | Для HTML сервер должен отправить ответ вида `HTTP/1.1 200 OK` и `Content-Type: text/html` | Так браузер понимает, что тело ответа нужно интерпретировать как HTML | `Content-Type` обязателен для правильной обработки файла | + +```text +index.html +style.css +app.js +logo.png +favicon.ico +``` + +```text +GET /index.html +``` + +```text +static/index.html +``` + +```text +HTTP/1.1 200 OK +Content-Type: text/html +``` + +--- + +## 🌍 Что такое HTTP + +| 📌 Термин HTTP и его расшифровка | 📖 Подробное объяснение каждого слова из названия и роли HTTP в общении браузера и сервера | 🧠 Что показывает пример HTTP-запроса | ✅ Итог | +| -------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | +| HTTP | HTTP — HyperText Transfer Protocol | Расшифровка: Hyper — гипертекстовый, Text — текст, Transfer — передача, Protocol — протокол | HTTP — это протокол общения браузера и сервера | +| Пример запроса | Пример запроса: `GET /style.css HTTP/1.1` и `Host: localhost:8080` | Здесь `GET` означает: клиент просит получить ресурс; `/style.css` — это путь к ресурсу; `HTTP/1.1` — это версия протокола HTTP | Запрос говорит серверу, какой ресурс нужен клиенту | + +```http +GET /style.css HTTP/1.1 +Host: localhost:8080 +``` + +```text +GET +``` + +```text +/style.css +``` + +```text +HTTP/1.1 +``` + +--- + +## 🔌 Что такое REST API + +| 📌 Термин REST API и его расшифровка | 📖 Подробное объяснение REST, API и примера endpoint-а, который возвращает JSON-данные | 🧠 Почему одного REST API недостаточно для браузерной игры | ✅ Итог | +| ------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| REST API | REST API — Representational State Transfer Application Programming Interface | Расшифровка REST: Representational — представление ресурса, State — состояние, Transfer — передача. Расшифровка API: Application — приложение, Programming — программирование, Interface — интерфейс | REST API обычно отдаёт данные | +| Пример REST API | Например, `GET /api/v1/maps` может вернуть JSON-массив с картами | Это подходит для данных, но не заменяет HTML-страницу, CSS и JavaScript | Для браузерной игры нужен не только REST API, но и статические файлы | + +```text +GET /api/v1/maps +``` + +```json +[ + { + "id": "map1", + "name": "Desert" + } +] +``` + +--- + +## 🧱 Что такое HTML + +| 📌 Термин HTML и его расшифровка | 📖 Подробное объяснение роли HTML-файла в браузерной странице | 🧠 Какой файл используется в этом уроке | ✅ Итог | +| -------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------- | +| HTML | HTML — HyperText Markup Language | Расшифровка: Hyper — гипертекстовый, Text — текст, Markup — разметка, Language — язык | HTML описывает структуру веб-страницы | +| Файл урока | В этом уроке используется файл `static/index.html` | Именно этот файл отдаётся при запросе `/` или `/index.html` | HTML-файл является главной страницей | + +```text +static/index.html +``` + +--- + +## 🎨 Что такое CSS + +| 📌 Термин CSS и его расшифровка | 📖 Подробное объяснение того, за что отвечает CSS на странице | 🧠 Какой файл используется в этом уроке | ✅ Итог | +| ------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- | ---------------------------------------------- | +| CSS | CSS — Cascading Style Sheets | Расшифровка: Cascading — каскадные, Style — стили, Sheets — таблицы | CSS отвечает за внешний вид страницы | +| Что задаёт CSS | CSS отвечает за цвет фона, цвет текста, отступы, рамки, расположение элементов | В этом уроке используется файл `static/style.css` | CSS-файл делает страницу визуально оформленной | + +```text +static/style.css +``` + +--- + +## ⚡ Что такое JavaScript + +| 📌 Термин JavaScript и сокращение JS | 📖 Подробное объяснение того, зачем JavaScript нужен браузерной странице | 🧠 Какой файл используется в этом уроке | ✅ Итог | +| ------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------- | +| JavaScript | JavaScript — язык программирования, который выполняется в браузере. JS — JavaScript | Расшифровка: Java — историческая часть названия, Script — сценарий | JavaScript нужен, чтобы страница могла выполнять код | +| Назначение JS | JavaScript нужен, чтобы страница могла рисовать игру, обращаться к API и обрабатывать действия игрока | В этом уроке используется файл `static/app.js` | JS-файл добавляет поведение странице | + +```text +static/app.js +``` + +--- + +## 🧾 Что такое MIME type + +| 📌 Термин MIME и его расшифровка | 📖 Подробное объяснение того, зачем MIME-типы нужны в HTTP и какой заголовок за это отвечает | 🧠 Как браузер использует `Content-Type` | ✅ Итог | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | +| MIME | MIME — Multipurpose Internet Mail Extensions | Расшифровка: Multipurpose — многоцелевые, Internet — интернет, Mail — почта, Extensions — расширения | MIME-типы говорят клиенту, как понимать тело ответа | +| История MIME | Изначально MIME-типы появились для электронной почты, чтобы письма могли содержать не только простой текст, но и вложения | В HTTP MIME-типы используются через заголовок `Content-Type` | Один и тот же механизм используется для файлов разных типов | +| `Content-Type` | Этот заголовок говорит браузеру, как обрабатывать тело ответа | Если сервер отдаёт HTML, он должен указать `Content-Type: text/html`; если CSS — `text/css`; если JavaScript — `application/javascript` | Без правильного типа браузер может обработать файл неверно | + +```http +Content-Type +``` + +| 📄 Расширение файла и тип ресурса, который отдаётся браузеру | 🌐 Корректный `Content-Type`, который должен быть выставлен сервером | 🧠 Как браузер будет интерпретировать тело ответа | ✅ Итог | +| ------------------------------------------------------------ | -------------------------------------------------------------------- | ------------------------------------------------- | --------------------------------- | +| `.html` | `text/html` | HTML-страница | Страница открывается как HTML | +| `.css` | `text/css` | CSS-стили | Стили применяются к странице | +| `.js` | `application/javascript` | JavaScript-сценарий | Скрипт выполняется браузером | +| `.json` | `application/json` | JSON-данные | Данные читаются как JSON | +| `.png` | `image/png` | PNG-изображение | Картинка отображается | +| `.jpg` | `image/jpeg` | JPEG-изображение | Картинка отображается | +| `.txt` | `text/plain` | Обычный текст | Текст показывается как plain text | + +```text +.html -> text/html +.css -> text/css +.js -> application/javascript +.json -> application/json +.png -> image/png +.jpg -> image/jpeg +.txt -> text/plain +``` + +```http +Content-Type: text/html +``` + +```http +Content-Type: text/css +``` + +```http +Content-Type: application/javascript +``` + +--- + +## 🎯 Практическая цель + +| 📌 Запрос браузера или curl к HTTP-серверу | 📖 Какой файл или ответ должен получить клиент | 🧠 Что проверяется этим сценарием | ✅ Итог | +| ------------------------------------------ | ---------------------------------------------- | ---------------------------------------------- | ------------------------- | +| `GET /` | `static/index.html` | Главная страница открывается по корневому пути | Работает главная страница | +| `GET /index.html` | `static/index.html` | HTML-файл можно открыть напрямую | HTML отдаётся | +| `GET /style.css` | `static/style.css` | CSS-файл отдаётся отдельно | Стили загружаются | +| `GET /app.js` | `static/app.js` | JavaScript-файл отдаётся отдельно | Скрипт загружается | +| `GET /unknown.png` | `404 Not Found` | Несуществующий файл не найден | Ошибка 404 работает | +| `GET /../../secret` | `403 Forbidden` | Попытка выхода из `static` запрещена | Защита пути работает | + +```text +GET / -> static/index.html +GET /index.html -> static/index.html +GET /style.css -> static/style.css +GET /app.js -> static/app.js +GET /unknown.png -> 404 Not Found +GET /../../secret -> 403 Forbidden +``` + +--- + +## 📂 Итоговая папка урока + +| 📌 Где находится практический урок и как устроена итоговая папка проекта | 📖 Полный путь и дерево проекта | 🧠 Что находится в каждой части проекта | ✅ Итог | +| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------ | +| Папка урока | Практический урок находится здесь: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files` | Внутри лежит CMake-проект, исходный код сервера и папка `static` с клиентскими файлами | Урок оформлен отдельной папкой | +| Структура проекта | `CMakeLists.txt`, `README.md`, `build/`, `src/main.cpp`, `static/index.html`, `static/style.css`, `static/app.js` | `src` содержит C++ сервер, `static` содержит файлы для браузера | Структура проекта понятна | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files +``` + +```text +sprint_18_20_theme_1_4_lesson_3_10_static_files/ +├── CMakeLists.txt +├── README.md +├── build/ +├── src/ +│ └── main.cpp +└── static/ + ├── index.html + ├── style.css + └── app.js +``` + +--- + +## ⚠️ Важный момент про Boost + +| 📌 Что произошло при сборке и почему Boost сначала не был найден стандартным способом | 📖 Полное объяснение проблемы, проверки, найденного решения и причины отказа от установки `libboost-all-dev` | 🧠 Почему выбранный способ лучше в этой ситуации | ✅ Итог | +| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------ | +| Ошибка Boost | Сначала возникла ошибка `Could NOT find Boost` | Причина: в WSL есть `cmake`, но нет `/usr/include/boost/asio.hpp` | Boost не был установлен в системные include-пути | +| Проверка системы | Проверка показала `cmake version 3.28.3` и ошибку `ls: cannot access '/usr/include/boost/asio.hpp': No such file or directory` | Это подтвердило, что CMake есть, а системных заголовков Boost нет | Причина ошибки найдена | +| Boost в Conan | При этом Boost уже был найден в Conan-кэше: `/home/ubuntu/.conan/data/boost/1.86.0/_/_/package/4c73f888ee1301ffed212b5fd37391dd45ff1c09/include/boost/asio.hpp` | Значит Boost уже был скачан ранее и его можно использовать без установки через apt | Используем готовый Boost из Conan | +| Что не стали делать | Поэтому мы не стали устанавливать `sudo apt install libboost-all-dev` | Так мы избежали лишней установки Boost на 500 MB или больше | 500 MB или больше не загружались | +| Что сделали вместо этого | Вместо этого мы переиспользовали уже существующий Boost из Conan | Conan — это пакетный менеджер для C++ | Решение экономит место и время | + +```text +Could NOT find Boost +``` + +```text +В WSL есть cmake, но нет /usr/include/boost/asio.hpp +``` + +```bash +cmake --version +ls /usr/include/boost/asio.hpp +``` + +```text +cmake version 3.28.3 +ls: cannot access '/usr/include/boost/asio.hpp': No such file or directory +``` + +```text +/home/ubuntu/.conan/data/boost/1.86.0/_/_/package/4c73f888ee1301ffed212b5fd37391dd45ff1c09/include/boost/asio.hpp +``` + +```bash +sudo apt install libboost-all-dev +``` + +--- + +## ⚙️ CMakeLists.txt + +| 📌 Файл сборки проекта и его полный путь | 📖 Назначение файла `CMakeLists.txt` в этом уроке | 🧠 Что он делает при сборке | ✅ Итог | +| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | +| `CMakeLists.txt` | Файл находится здесь: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/CMakeLists.txt` | Он описывает C++ проект, стандарт C++17, исполняемый файл `static_server`, путь к Boost из Conan и подключение `pthread` | Проект собирается через CMake | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/CMakeLists.txt +``` + +```cmake +cmake_minimum_required(VERSION 3.11) + +project(web_server_lesson_18_static_files CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(static_server + src/main.cpp +) + +target_include_directories(static_server PRIVATE + /home/ubuntu/.conan/data/boost/1.86.0/_/_/package/4c73f888ee1301ffed212b5fd37391dd45ff1c09/include +) + +target_link_libraries(static_server + pthread +) +``` + +| 📌 Строка или команда из `CMakeLists.txt` | 📖 Полный разбор того, что она делает | 🧠 Зачем это нужно проекту | ✅ Итог | +| ------------------------------------------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------- | +| `cmake_minimum_required(VERSION 3.11)` | Минимальная версия CMake для сборки проекта | CMake — это система генерации сборочных файлов | Версия сборочной системы задана | +| `project(web_server_lesson_18_static_files CXX)` | Создаёт проект C++ | `CXX` означает C++ compiler | Проект объявлен как C++ | +| `set(CMAKE_CXX_STANDARD 17)` | Включает стандарт C++17 | Код компилируется как C++17 | Стандарт задан | +| `set(CMAKE_CXX_STANDARD_REQUIRED ON)` | Требует именно этот стандарт | Сборка не должна откатиться на другой стандарт | Требование стандарта включено | +| `add_executable(static_server src/main.cpp)` | Создаёт исполняемый файл `static_server` из файла `src/main.cpp` | Это главный бинарник сервера | Бинарник создаётся | +| `target_include_directories(...)` | Добавляет путь к заголовочным файлам Boost | Используется Boost из Conan-кэша | Заголовки Boost найдены | +| `target_link_libraries(static_server pthread)` | Подключает POSIX threads | POSIX — Portable Operating System Interface. `pthread` — POSIX threads, библиотека потоков | Потоки подключены | + +```cmake +cmake_minimum_required(VERSION 3.11) +``` + +```cmake +project(web_server_lesson_18_static_files CXX) +``` + +```cmake +set(CMAKE_CXX_STANDARD 17) +``` + +```cmake +set(CMAKE_CXX_STANDARD_REQUIRED ON) +``` + +```cmake +add_executable(static_server src/main.cpp) +``` + +```text +static_server +``` + +```text +src/main.cpp +``` + +```cmake +target_include_directories(...) +``` + +```cmake +target_link_libraries(static_server pthread) +``` + +--- + +## 📄 HTML-файл + +| 📌 HTML-файл урока и его полный путь | 📖 Полное содержимое файла и важные элементы страницы | 🧠 Что именно делает браузер при загрузке HTML | ✅ Итог | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| `static/index.html` | Файл находится здесь: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/index.html` | В HTML подключается CSS через ``, JavaScript через ``, а также создаётся `` | HTML запускает загрузку CSS, JS и создаёт область для игры | +| Главное в HTML | `link` заставляет браузер отдельно запросить CSS-файл, `script` заставляет браузер отдельно запросить JavaScript-файл, `canvas` создаёт область для рисования игры | Поэтому один запрос к `/` приводит к дополнительным запросам `/style.css` и `/app.js` | Клиентская страница собирается из нескольких файлов | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/index.html +``` + +```html + + + + + Static Files Lesson + + + +

Игровой клиент запущен

+ + + + +``` + +```html + +``` + +```html + +``` + +```html + +``` + +--- + +## 🎨 CSS-файл + +| 📌 CSS-файл урока и его полный путь | 📖 Полное содержимое CSS и его назначение | 🧠 Что меняется на странице благодаря CSS | ✅ Итог | +| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | +| `static/style.css` | Файл находится здесь: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/style.css` | Этот файл задаёт внешний вид страницы: фон, цвет текста, шрифт, центрирование, внешний вид canvas, рамку и отступы | Страница становится оформленной, а canvas получает визуальные границы | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/style.css +``` + +```css +body { + margin: 0; + background-color: #202124; + color: white; + font-family: Arial, sans-serif; + text-align: center; +} + +canvas { + display: block; + margin: 40px auto; + background-color: #111; + border: 2px solid white; +} +``` + +--- + +## ⚡ JavaScript-файл + +| 📌 JavaScript-файл урока и его полный путь | 📖 Полное содержимое JavaScript и что делает каждая часть | 🧠 Как это связано с canvas и браузерной игрой | ✅ Итог | +| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `static/app.js` | Файл находится здесь: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/app.js` | Код находит canvas, получает 2D-контекст, рисует зелёный квадрат и пишет сообщение в консоль браузера | JavaScript оживляет HTML-страницу и показывает, что скрипт реально загружен | +| Что делает код | 1. Находит canvas. 2. Получает 2D-контекст. 3. Рисует зелёный квадрат. 4. Пишет сообщение в консоль браузера | Это минимальная демонстрация работы клиентского JavaScript, отданного C++ сервером | JS успешно работает | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/app.js +``` + +```javascript +const canvas = document.getElementById("game"); +const context = canvas.getContext("2d"); + +context.fillStyle = "lime"; +context.fillRect(100, 100, 80, 80); + +console.log("Static files lesson started"); +``` + +--- + +## ❌ Важная ошибка, которую исправили + +| 📌 В чём была ошибка с путём к статической папке | 📖 Подробное объяснение причины, неправильного поведения и исправления | 🧠 Почему сервер возвращал 404 и как это было исправлено | ✅ Итог | +| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | +| Неправильный `Static root` | Изначально сервер показывал `Static root: "/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/build/static"`, но файлы лежали не там | Правильная папка: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static` | Сервер искал файлы в `build/static`, где их не было | +| Причина ошибки | Ошибка была в строке `fs::path static_root = fs::current_path() / "static";` | Потому что сервер запускался из папки `build`, а значит `current_path()` указывал на `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/build` | Поэтому сервер искал `build/static/index.html` | +| Последствие | Такой папки не было, поэтому сервер возвращал `404 Not Found` | Файл `index.html` физически лежал на уровень выше, в папке `static` | 404 был ожидаем из-за неправильного пути | +| Исправление | Исправление: `fs::path static_root = fs::current_path().parent_path() / "static";` | Теперь сервер из папки `build` поднимается на уровень выше и берёт правильную папку `static` | `Static root` стал правильным | + +```text +Static root: "/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/build/static" +``` + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static +``` + +```cpp +fs::path static_root = fs::current_path() / "static"; +``` + +```text +build +``` + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/build +``` + +```text +build/static/index.html +``` + +```text +404 Not Found +``` + +```cpp +fs::path static_root = fs::current_path().parent_path() / "static"; +``` + +--- + +## 🧠 Основной код сервера + +| 📌 Файл основного кода сервера и его полный путь | 📖 Что реализует код в `main.cpp` | 🧠 Какие HTTP-сценарии покрывает сервер | ✅ Итог | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `src/main.cpp` | Файл находится здесь: `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/src/main.cpp` | Код реализует HTTP-сервер на Boost.Beast, обработку GET-запросов, чтение файлов, определение MIME-типа, проверку безопасности пути, ответы `200 OK`, `403 Forbidden`, `404 Not Found`, `405 Method Not Allowed` | Сервер умеет отдавать статические файлы и защищаться от опасных путей | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/src/main.cpp +``` + +| 📌 Возможность сервера | 📖 Что именно реализовано | 🧠 Для чего это нужно | ✅ Итог | +| -------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------- | ---------------------- | +| HTTP-сервер на Boost.Beast | Сервер принимает HTTP-запросы и формирует HTTP-ответы | Это основа сетевой части урока | HTTP работает | +| Обработка GET-запросов | Разрешён основной метод для получения статических файлов | Браузер обычно получает файлы через GET | GET работает | +| Чтение файлов | Сервер открывает файл из папки `static` и отправляет его содержимое | Без этого нельзя отдавать HTML/CSS/JS | Файлы читаются | +| Определение MIME-типа | Сервер выбирает `Content-Type` по расширению файла | Браузер корректно понимает тип ответа | MIME работает | +| Проверка безопасности пути | Проверяется, что итоговый путь остаётся внутри `static_root` | Это защита от path traversal | Безопасность добавлена | +| HTTP-коды | Реализованы ответы `200 OK`, `403 Forbidden`, `404 Not Found`, `405 Method Not Allowed` | Клиент получает корректный статус | Ошибки различаются | + +--- + +## 🔄 Логика обработки запроса + +| 📌 HTTP-запрос клиента | 📖 Что делает сервер при таком запросе | 🧠 Какой файл или статус возвращается | ✅ Итог | +| ---------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------- | ------------------------- | +| `GET /` | Если приходит `GET /`, сервер отдаёт `static/index.html` | Корневой URL открывает главную страницу | Главная страница работает | +| `GET /style.css` | Если приходит `GET /style.css`, сервер отдаёт `static/style.css` | CSS загружается отдельно | Стили работают | +| `GET /app.js` | Если приходит `GET /app.js`, сервер отдаёт `static/app.js` | JavaScript загружается отдельно | Скрипт работает | +| `GET /unknown.png` | Если файла нет, сервер возвращает `404 Not Found` | Несуществующий ресурс корректно обрабатывается | 404 работает | +| `GET /../../secret` | Если пользователь пытается выйти из папки `static`, сервер должен вернуть `403 Forbidden` | Защита не даёт читать файлы вне разрешённой папки | 403 работает | + +```text +GET / +``` + +```text +static/index.html +``` + +```text +GET /style.css +``` + +```text +static/style.css +``` + +```text +GET /app.js +``` + +```text +static/app.js +``` + +```text +GET /unknown.png +``` + +```text +404 Not Found +``` + +```text +GET /../../secret +``` + +```text +403 Forbidden +``` + +--- + +## 🔧 Сборка проекта + +| 📌 Шаг сборки проекта из папки урока | 💻 Команда, которую нужно выполнить в терминале | 📖 Что делает команда | ✅ Итог | +| ------------------------------------ | ------------------------------------------------------------------------------------ | ------------------------------ | --------------------- | +| Перейти в папку проекта | `cd /home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files` | Открывает корневую папку урока | Готово к сборке | +| Удалить старую сборку | `rm -rf build` | Удаляет старый build-каталог | Сборка будет чистой | +| Создать папку сборки | `mkdir build` | Создаёт новый build-каталог | Папка создана | +| Перейти в build | `cd build` | Открывает папку сборки | Можно запускать CMake | +| Запустить CMake | `cmake ..` | Генерирует файлы сборки | Конфигурация готова | +| Собрать проект | `cmake --build .` | Компилирует `static_server` | Бинарник собран | + +```bash +cd /home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files +rm -rf build +mkdir build +cd build +cmake .. +cmake --build . +``` + +--- + +## ▶️ Запуск сервера + +| 📌 Откуда запускать сервер и какую команду выполнить | 📖 Какой вывод ожидается при правильном запуске | 🧠 Как понять, что путь к `static` исправлен правильно | ✅ Итог | | +| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | ----------------------------------------------- | +| Запуск из папки build | Из папки `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/build` запустить `./static_server` | Ожидаемый вывод: `Server started: http://localhost:8080` и `Static root: "/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static"` | Если в `Static root` написано `build/static`, значит путь исправлен неправильно | Сервер должен показывать правильный static root | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/build +``` + +```bash +./static_server +``` + +```text +Server started: http://localhost:8080 +Static root: "/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static" +``` + +```text +build/static +``` + +--- + +## 🧪 Проверка через WSL + +| 📌 Что проверяется через curl во втором терминале WSL | 💻 Команда для проверки | 📖 Ожидаемый HTTP-ответ или ключевые заголовки | ✅ Итог | +| ----------------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------- | ------------------------- | +| Главная страница | `curl -i http://localhost:8080/` | Ожидается `HTTP/1.1 200 OK` и `Content-Type: text/html` | Главная страница отдаётся | +| HTML напрямую | `curl -i http://localhost:8080/index.html` | Ожидается `HTTP/1.1 200 OK` и `Content-Type: text/html` | HTML отдаётся | +| CSS | `curl -i http://localhost:8080/style.css` | Ожидается `HTTP/1.1 200 OK` и `Content-Type: text/css` | CSS отдаётся | +| JavaScript | `curl -i http://localhost:8080/app.js` | Ожидается `HTTP/1.1 200 OK` и `Content-Type: application/javascript` | JS отдаётся | +| Несуществующий файл | `curl -i http://localhost:8080/unknown.png` | Ожидается `HTTP/1.1 404 Not Found` и `Content-Type: text/plain` | 404 работает | +| Попытка выхода из директории | `curl -i http://localhost:8080/../../secret` | Ожидается `HTTP/1.1 403 Forbidden` и `Content-Type: text/plain` | 403 работает | + +```bash +curl -i http://localhost:8080/ +``` + +```http +HTTP/1.1 200 OK +Content-Type: text/html +``` + +```bash +curl -i http://localhost:8080/index.html +``` + +```http +HTTP/1.1 200 OK +Content-Type: text/html +``` + +```bash +curl -i http://localhost:8080/style.css +``` + +```http +HTTP/1.1 200 OK +Content-Type: text/css +``` + +```bash +curl -i http://localhost:8080/app.js +``` + +```http +HTTP/1.1 200 OK +Content-Type: application/javascript +``` + +```bash +curl -i http://localhost:8080/unknown.png +``` + +```http +HTTP/1.1 404 Not Found +Content-Type: text/plain +``` + +```bash +curl -i http://localhost:8080/../../secret +``` + +```http +HTTP/1.1 403 Forbidden +Content-Type: text/plain +``` + +--- + +## 🌐 Проверка через браузер Windows + +| 📌 Что открыть в браузере Windows | 📖 Что должно произойти | 🧠 Что этим проверяется | ✅ Итог | +| ------------------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------- | ---------------------------- | +| `http://localhost:8080/` | Ожидается HTML-страница `Игровой клиент запущен` и canvas с зелёным квадратом | Проверяется главная страница, CSS, JS и canvas | Браузерная проверка проходит | +| `http://localhost:8080/index.html` | Должна открыться страница | Проверяется прямой доступ к HTML | HTML работает | +| `http://localhost:8080/style.css` | Должен открыться CSS-текст | Проверяется отдача CSS-файла | CSS доступен | +| `http://localhost:8080/app.js` | Должен открыться JavaScript-текст | Проверяется отдача JS-файла | JS доступен | +| `http://localhost:8080/unknown.png` | Должно быть `404 Not Found` | Проверяется несуществующий файл | 404 работает | +| `http://localhost:8080/../../secret` | Должно быть `403 Forbidden` | Проверяется защита от выхода из папки `static` | 403 работает | + +```text +http://localhost:8080/ +``` + +```text +Игровой клиент запущен +``` + +```text +http://localhost:8080/index.html +``` + +```text +http://localhost:8080/style.css +``` + +```text +http://localhost:8080/app.js +``` + +```text +http://localhost:8080/unknown.png +``` + +```text +404 Not Found +``` + +```text +http://localhost:8080/../../secret +``` + +```text +403 Forbidden +``` + +--- + +## 💻 Почему браузер Windows видит сервер из WSL + +| 📌 Что такое WSL и почему Windows может открыть сервер, запущенный внутри Linux-подсистемы | 📖 Расшифровка WSL и объяснение работы `localhost` между Windows и WSL | 🧠 Почему это удобно для разработки | ✅ Итог | +| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| WSL | WSL — Windows Subsystem for Linux | Расшифровка: Windows — операционная система Windows, Subsystem — подсистема, for — для, Linux — Linux | WSL позволяет запускать Linux-сервер рядом с Windows-браузером | +| Доступ через localhost | Когда сервер в WSL слушает адрес `0.0.0.0:8080`, Windows обычно может открыть его через `http://localhost:8080/` | Это позволяет запускать сервер в WSL, а проверять страницу в обычном браузере Windows | Проверка через Windows-браузер удобна | + +```text +0.0.0.0:8080 +``` + +```text +http://localhost:8080/ +``` + +--- + +## 🔢 Почему использовали порт 8080 + +| 📌 Почему выбран порт `8080`, а не обычный HTTP-порт `80` | 📖 Подробное объяснение причины выбора учебного порта | 🧠 Почему это удобно в разработке | ✅ Итог | +| --------------------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------------------------------ | +| Порт 8080 | Порт `8080` часто используют для учебных HTTP-серверов | Обычный HTTP-порт — `80`, но он может требовать прав администратора или быть занят | Для разработки удобнее использовать `8080` | + +```text +80 +``` + +```text +8080 +``` + +--- + +## ✅ Что такое 200 OK + +| 📌 HTTP-код `200 OK` | 📖 Что означает этот статус | 🧠 Пример из этого урока | ✅ Итог | +| -------------------- | -------------------------------------- | ------------------------------------------------------------------ | --------------------- | +| `200 OK` | Означает, что запрос успешно обработан | Например, `GET /index.html`: файл найден, сервер вернул содержимое | Успешная отдача файла | + +```text +200 OK +``` + +```text +GET /index.html +``` + +--- + +## ⛔ Что такое 403 Forbidden + +| 📌 HTTP-код `403 Forbidden` | 📖 Что означает этот статус | 🧠 Пример из этого урока | ✅ Итог | +| --------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- | +| `403 Forbidden` | Означает, что сервер понял запрос, но запрещает доступ | В этом уроке 403 используется для защиты от выхода из папки `static`, например при опасном запросе `/../../secret` | Доступ запрещён по правилам безопасности | + +```text +403 Forbidden +``` + +```text +/../../secret +``` + +--- + +## ❓ Что такое 404 Not Found + +| 📌 HTTP-код `404 Not Found` | 📖 Что означает этот статус | 🧠 Пример из этого урока | ✅ Итог | +| --------------------------- | ------------------------------------------ | ------------------------ | ----------------------------- | +| `404 Not Found` | Означает, что запрошенный ресурс не найден | Пример: `/unknown.png` | Несуществующий файл не найден | + +```text +404 Not Found +``` + +```text +/unknown.png +``` + +--- + +## 🚫 Что такое 405 Method Not Allowed + +| 📌 HTTP-код `405 Method Not Allowed` | 📖 Что означает этот статус | 🧠 Как используется в этом уроке | ✅ Итог | +| ------------------------------------ | ------------------------------------ | -------------------------------------------------------------------------------------------------------- | ----------------------------------- | +| `405 Method Not Allowed` | Означает, что HTTP-метод не разрешён | В этом уроке разрешён только `GET`. Если отправить `POST`, `PUT` или `DELETE`, сервер должен вернуть 405 | Неподдерживаемые методы отклоняются | + +```text +405 Method Not Allowed +``` + +```text +GET +``` + +--- + +## 🛡 Что такое path traversal + +| 📌 Понятие path traversal и пример атаки обхода пути | 📖 Подробное объяснение атаки и защиты от выхода из разрешённой папки `static` | 🧠 Какой код используется для проверки безопасности | ✅ Итог | +| ---------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | +| Path traversal | Path traversal — атака обхода пути | Пример: `/../../secret`. Злоумышленник пытается выйти из разрешённой папки `static` и прочитать файлы вне неё | Это опасный сценарий, который нужно блокировать | +| Защита | Для защиты используется проверка `IsSubPath(requested_path, static_root)` | Она проверяет, что итоговый путь всё ещё находится внутри `static_root` | Файлы вне `static` не отдаются | + +```text +/../../secret +``` + +```text +static +``` + +```cpp +IsSubPath(requested_path, static_root) +``` + +--- + +## 🏁 Итог + +| 📌 Что сделано в практическом уроке по отдаче статических файлов | 📖 Подробное описание результата без сокращений | 🧠 Почему это важно | ✅ Итог | +| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | ---------------------------- | +| Создана отдельная папка урока | Практический урок оформлен в отдельной директории `sprint_18_20_theme_1_4_lesson_3_10_static_files` | Это удобно для самостоятельной сборки и проверки | Папка готова | +| Добавлены статические файлы | Добавлены `static/index.html`, `static/style.css`, `static/app.js` | Браузер получает HTML, стили и JavaScript | Клиентская часть есть | +| Написан C++ HTTP-сервер | Сервер написан на C++ с использованием Boost.Beast | Он умеет принимать HTTP-запросы и отдавать файлы | Сервер работает | +| Реализована отдача статических файлов | Сервер отдаёт HTML, CSS и JS из папки `static` | Это превращает сервер в поставщика браузерного клиента | Static serving работает | +| Добавлено определение MIME-типа | Сервер выставляет правильный `Content-Type` для файлов | Браузер корректно обрабатывает HTML, CSS и JS | MIME работает | +| Добавлена защита от выхода из директории | Запросы вида `/../../secret` должны получать `403 Forbidden` | Это защита от path traversal | Безопасность добавлена | +| Исправлена ошибка с путём `build/static` | Используется `fs::current_path().parent_path() / "static"` | Сервер ищет файлы в правильной папке | Путь исправлен | +| Переиспользован уже существующий Boost из Conan | Использован Boost из Conan-кэша | Не понадобилось ставить системный Boost | Conan помог | +| Не устанавливался `libboost-all-dev` | Мы не стали устанавливать `sudo apt install libboost-all-dev` | Не загружались лишние 500 MB или больше | Место и время сохранены | +| Проверка выполняется через `curl` | Проверяются `/`, `/index.html`, `/style.css`, `/app.js`, `/unknown.png`, `/../../secret` | CLI-проверка показывает HTTP-коды и заголовки | curl-тесты готовы | +| Проверка выполняется через браузер Windows | Открывается `http://localhost:8080/` | Видно HTML-страницу, canvas и работу CSS/JS | Браузерная проверка проходит | + +Главный результат: + +```text +http://localhost:8080/ +``` + +открывает игровой HTML-клиент, который загружает CSS и JavaScript с C++ сервера. diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/src/main.cpp b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/src/main.cpp new file mode 100644 index 0000000..925a0a3 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/src/main.cpp @@ -0,0 +1,171 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +namespace fs = std::filesystem; + +using tcp = net::ip::tcp; + +std::string GetMimeType(const fs::path& path) { + static const std::unordered_map types = { + {".html", "text/html"}, + {".htm", "text/html"}, + {".css", "text/css"}, + {".js", "application/javascript"}, + {".json", "application/json"}, + {".png", "image/png"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".gif", "image/gif"}, + {".svg", "image/svg+xml"}, + {".ico", "image/vnd.microsoft.icon"}, + {".txt", "text/plain"} + }; + + auto extension = path.extension().string(); + auto it = types.find(extension); + + if (it != types.end()) { + return it->second; + } + + return "application/octet-stream"; +} + +std::string ReadFile(const fs::path& path) { + std::ifstream file(path, std::ios::binary); + + if (!file) { + throw std::runtime_error("Cannot open file"); + } + + return std::string( + std::istreambuf_iterator(file), + std::istreambuf_iterator() + ); +} + +bool IsSubPath(const fs::path& path, const fs::path& base) { + auto canonical_path = fs::weakly_canonical(path); + auto canonical_base = fs::weakly_canonical(base); + + auto path_it = canonical_path.begin(); + auto base_it = canonical_base.begin(); + + for (; base_it != canonical_base.end(); ++base_it, ++path_it) { + if (path_it == canonical_path.end() || *path_it != *base_it) { + return false; + } + } + + return true; +} + +http::response MakeTextResponse( + http::status status, + std::string text, + unsigned version +) { + http::response response(status, version); + response.set(http::field::content_type, "text/plain"); + response.body() = std::move(text); + response.prepare_payload(); + return response; +} + +http::response HandleRequest( + const http::request& request, + const fs::path& static_root +) { + if (request.method() != http::verb::get) { + return MakeTextResponse( + http::status::method_not_allowed, + "Only GET is allowed", + request.version() + ); + } + + std::string target = std::string(request.target()); + + fs::path requested_path; + + if (target == "/") { + requested_path = static_root / "index.html"; + } else { + requested_path = static_root / target.substr(1); + } + + if (!IsSubPath(requested_path, static_root)) { + return MakeTextResponse( + http::status::forbidden, + "403 Forbidden", + request.version() + ); + } + + if (!fs::exists(requested_path) || !fs::is_regular_file(requested_path)) { + return MakeTextResponse( + http::status::not_found, + "404 Not Found", + request.version() + ); + } + + auto body = ReadFile(requested_path); + + http::response response(http::status::ok, request.version()); + response.set(http::field::content_type, GetMimeType(requested_path)); + response.body() = std::move(body); + response.prepare_payload(); + + return response; +} + +void HandleSession(tcp::socket socket, const fs::path& static_root) { + beast::flat_buffer buffer; + + http::request request; + http::read(socket, buffer, request); + + auto response = HandleRequest(request, static_root); + + http::write(socket, response); + + beast::error_code ec; + socket.shutdown(tcp::socket::shutdown_send, ec); +} + +int main() { + try { + const auto address = net::ip::make_address("0.0.0.0"); + const unsigned short port = 8080; + + fs::path static_root = fs::current_path().parent_path() / "static"; + + net::io_context io_context; + + tcp::acceptor acceptor(io_context, {address, port}); + + std::cout << "Server started: http://localhost:8080\n"; + std::cout << "Static root: " << static_root << "\n"; + + while (true) { + tcp::socket socket(io_context); + acceptor.accept(socket); + + HandleSession(std::move(socket), static_root); + } + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << "\n"; + return 1; + } +} diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/app.js b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/app.js new file mode 100644 index 0000000..2e7c7c3 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/app.js @@ -0,0 +1,7 @@ +const canvas = document.getElementById("game"); +const context = canvas.getContext("2d"); + +context.fillStyle = "lime"; +context.fillRect(100, 100, 80, 80); + +console.log("Static files lesson started"); diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/index.html b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/index.html new file mode 100644 index 0000000..395e174 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/index.html @@ -0,0 +1,13 @@ + + + + + Static Files Lesson + + + +

Игровой клиент запущен

+ + + + diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/style.css b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/style.css new file mode 100644 index 0000000..84d1814 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files/static/style.css @@ -0,0 +1,14 @@ +body { + margin: 0; + background-color: #202124; + color: white; + font-family: Arial, sans-serif; + text-align: center; +} + +canvas { + display: block; + margin: 40px auto; + background-color: #111; + border: 2px solid white; +} diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/CMakeLists.txt b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/CMakeLists.txt new file mode 100644 index 0000000..72e0260 --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.11) + +project(web_server_lesson_18_static_files CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(static_server + src/main.cpp +) + +target_include_directories(static_server PRIVATE + /home/ubuntu/.conan/data/boost/1.86.0/_/_/package/4c73f888ee1301ffed212b5fd37391dd45ff1c09/include +) + +target_link_libraries(static_server + pthread +) diff --git a/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/README.md b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/README.md new file mode 100644 index 0000000..869ab1a --- /dev/null +++ b/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/README.md @@ -0,0 +1,461 @@ +# 📘 README — Sprint 18_20 → Theme 1_4 → Lesson 3_10 (Part 2) + +--- + +## 🧩 Тема + +| 🧩 Название темы урока | 📖 Подробное описание темы | 🧠 Что именно расширяется в этом практическом уроке | ✅ Итог | +| ---------------------------------------- | ---------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------- | +| Работа с кэшем и эффективное логирование | Работа с кэшем и эффективное логирование | Практическое расширение: медиа-типы и статические файлы | Урок продолжает развитие HTTP-сервера статических файлов | + +--- + +## 🎯 Цель + +| 🎯 Главная цель урока | 📖 Что должен научиться отдавать HTTP-сервер на C++ | 🧠 Почему это важно для браузера | ✅ Итог | +| ---------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| Расширить HTTP-сервер на C++ | Сервер должен уметь отдавать HTML, CSS, JavaScript, изображения PNG/JPG, аудио WAV и видео MP4 | Браузер должен корректно понимать разные типы ресурсов и показывать/воспроизводить их | Сервер становится полноценнее как мини веб-сервер для фронтенда | + +```text +✔ HTML +✔ CSS +✔ JavaScript +✔ изображения (PNG, JPG) +✔ аудио (WAV) +✔ видео (MP4) +``` + +--- + +## 📂 Полный путь проекта + +| 📂 Что указывается | 📖 Полный путь проекта | 🧠 Зачем нужен этот путь | ✅ Итог | +| ------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------- | +| Папка практического урока | `/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2` | По этому пути открывается проект, собирается сервер и создаётся README | Путь проекта зафиксирован | + +```text +/home/ubuntu/cppbackend/lessons/sprint_18_20_theme_1_4_lesson_3_10_static_files_part2 +``` + +--- + +## 📁 Структура проекта + +| 📁 Часть проекта | 📖 Что находится внутри | 🧠 Для чего нужна эта часть | ✅ Итог | +| ------------------- | ----------------------- | ------------------------------------------------------------ | ------------------------------------ | +| `CMakeLists.txt` | Файл сборки CMake | Описывает сборку C++ проекта | Проект можно собрать через CMake | +| `README.md` | Документация урока | Объясняет цель, структуру, теорию, сборку, запуск и проверки | Урок оформлен как полноценный README | +| `build/` | Папка сборки | В неё генерируются build-файлы и бинарник | Сборка отделена от исходников | +| `src/main.cpp` | Основной код сервера | Реализует HTTP-сервер, MIME-типы и отдачу файлов | Серверная логика находится в `src` | +| `static/index.html` | HTML-страница | Главная страница браузерного клиента | HTML отдаётся сервером | +| `static/style.css` | CSS-стили | Оформление страницы | CSS отдаётся сервером | +| `static/app.js` | JavaScript-код | Клиентская логика страницы | JS отдаётся сервером | +| `static/images/` | Изображения PNG | Картинки для проверки `image/png` | Изображения доступны через HTTP | +| `static/audio/` | Аудио WAV | Звук для проверки `audio/wav` | Аудио доступно через HTTP | +| `static/video/` | Видео MP4 | Видео для проверки `video/mp4` | Видео доступно через HTTP | + +```text +sprint_18_20_theme_1_4_lesson_3_10_static_files_part2/ +├── CMakeLists.txt +├── README.md +├── build/ +├── src/ +│ └── main.cpp +└── static/ + ├── index.html + ├── style.css + ├── app.js + ├── images/ + │ ├── road_vh.png + │ ├── road_tr.png + │ ├── road_t.png + │ └── tv_architecture.png + ├── audio/ + │ └── front_center.wav + └── video/ + ├── reset_position.mp4 + ├── track_vertical_plane.mp4 + └── track_horizontal_plane.mp4 +``` + +--- + +## 🧠 Теория: MIME-type / media type + +| 🧠 Теоретическое понятие | 📖 Подробное объяснение | 🧩 Формат | ✅ Итог | +| ------------------------ | -------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------- | +| MIME-type | MIME-type также называют media type | `type/subtype` | MIME-type описывает тип содержимого, которое сервер отдаёт браузеру | +| Примеры MIME-type | HTML, изображения, аудио и видео имеют разные media type | `text/html`, `image/png`, `audio/wav`, `video/mp4` | Браузер понимает, как обрабатывать каждый файл | + +```text +type/subtype +``` + +```text +text/html +image/png +audio/wav +video/mp4 +``` + +--- + +## 🧾 Теория: Content-Type + +| 🧾 HTTP-заголовок | 📖 Что он сообщает браузеру | 🧠 Как браузер решает, что делать с данными | ✅ Итог | +| ------------------------- | -------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------- | +| `Content-Type` | HTTP заголовок сообщает тип содержимого ответа | По `Content-Type` браузер выбирает режим обработки данных | Без правильного `Content-Type` файл может быть обработан неверно | +| `Content-Type: image/png` | Сервер сообщает, что тело ответа — PNG-изображение | Браузер отображает картинку | Изображение открывается как изображение | +| `text` | Текстовый тип | Браузер показывает текст | Текст отображается | +| `image` | Тип изображения | Браузер отображает картинку | Картинка отображается | +| `audio` | Тип аудио | Браузер использует плеер | Звук воспроизводится | +| `video` | Тип видео | Браузер использует видеоплеер | Видео воспроизводится | + +```http +Content-Type: image/png +``` + +```text +text → показать +image → отобразить +audio → плеер +video → видеоплеер +``` + +--- + +## 🧩 Основные типы MIME + +| 🧩 Основной тип | 📖 Что означает | 🧠 Примеры использования | ✅ Итог | +| --------------- | ----------------- | -------------------------- | ------------------------------------------------------- | +| `text/*` | Текстовые ресурсы | HTML, CSS, TXT | Текстовые данные показываются или применяются браузером | +| `image/*` | Изображения | PNG, JPG, GIF, SVG | Картинки отображаются браузером | +| `audio/*` | Звук | WAV, MP3, OGG | Аудио воспроизводится через плеер | +| `video/*` | Видео | MP4, WebM | Видео воспроизводится через видеоплеер | +| `application/*` | Прочее | JSON, PDF и другие форматы | Используется для данных и бинарных форматов | + +```text +text/* → текст +image/* → изображения +audio/* → звук +video/* → видео +application/* → прочее (json, pdf) +``` + +--- + +## 🧯 Fallback + +| 🧯 Что такое fallback | 📖 Какой MIME-type используется по умолчанию | 🧠 Что это значит для браузера | ✅ Итог | +| ------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------- | +| Fallback для неизвестного файла | `application/octet-stream` | Неизвестный бинарный файл обычно воспринимается как файл для скачивания | Сервер всё равно может безопасно отдать неизвестный тип | + +```text +application/octet-stream +``` + +```text +неизвестный бинарный файл → скачивание +``` + +--- + +## 🛠 Реализация + +| 🛠 Что реализуется | 📖 Где реализуется | 🧠 Зачем это нужно | ✅ Итог | +| ------------------ | ---------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| MIME-функция | `main.cpp` | Функция определяет `Content-Type` по расширению файла | Сервер отдаёт HTML, CSS, JS, изображения, аудио и видео с правильным MIME | +| Таблица расширений | `std::unordered_map` | Связывает `.html`, `.css`, `.js`, `.png`, `.wav`, `.mp4` и другие расширения с MIME-type | Типы выбираются автоматически | +| Fallback | `application/octet-stream` | Возвращается, если расширение неизвестно | Неизвестные файлы получают безопасный бинарный тип | + +--- + +## 📌 MIME-функция (`main.cpp`) + +| 📌 Элемент функции | 📖 Что делает | 🧠 Почему важно | ✅ Итог | +| --------------------------- | ------------------------------------------ | ------------------------------------------------------ | ------------------------------------------- | +| `GetMimeType` | Принимает путь к файлу | Из пути можно получить расширение | MIME определяется по файлу | +| `types` | Хранит соответствие расширений и MIME-type | Быстрый поиск нужного `Content-Type` | Сервер знает основные media types | +| `path.extension().string()` | Получает расширение файла | Например `.png`, `.wav`, `.mp4` | Расширение используется как ключ | +| `types.find(ext)` | Ищет расширение в таблице | Если расширение известно, возвращается корректный MIME | Известные файлы получают правильный тип | +| `application/octet-stream` | Возвращается для неизвестного расширения | Это fallback | Неизвестные бинарные файлы не ломают сервер | + +```cpp +std::string GetMimeType(const fs::path& path) { + static const std::unordered_map types = { + {".html", "text/html"}, + {".css", "text/css"}, + {".js", "application/javascript"}, + {".json", "application/json"}, + + {".png", "image/png"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".gif", "image/gif"}, + {".svg", "image/svg+xml"}, + + {".mp3", "audio/mpeg"}, + {".wav", "audio/wav"}, + {".ogg", "audio/ogg"}, + + {".mp4", "video/mp4"}, + {".webm", "video/webm"}, + + {".txt", "text/plain"} + }; + + auto ext = path.extension().string(); + auto it = types.find(ext); + + if (it != types.end()) { + return it->second; + } + + return "application/octet-stream"; +} +``` + +--- + +## 🌐 HTML (`index.html`) + +| 🌐 HTML-блок | 📖 Что добавлено на страницу | 🧠 Какой файл запрашивает браузер | ✅ Итог | +| ------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------ | +| `Изображения` | Добавлены изображения через `` | `/images/road_vh.png`, `/images/road_tr.png`, `/images/road_t.png`, `/images/tv_architecture.png` | Браузер загружает картинки | +| `Звук` | Добавлен `