Урок 6. Свой веб-сервер

Этот сайт написан на «Сфере»: сервер, шаблоны, раздача файлов и загрузка установщиков. В уроке — минимальный сервер на библиотеке веб.код из стандартной библиотеки.

Минимальный сервер

подключить веб;

функция главная(): целое
    результат из целого открыт = открыть_порт(8090);
    если ( не открыт.успешно)
        вывести(открыт.сообщение);
        вернуть 1;
    конец
    целое32 слушающий = целое32(открыт.значение);
    вывести("Сервер слушает на http://localhost:8090");

    пока (да)
        целое32 клиент = принять_подключение(слушающий, нулевой_указатель_сети(), нулевой_указатель_сети());
        если (клиент не меньше целое32(0))
            результат из Запрос запрос = прочитать_запрос(клиент);
            если (запрос.успешно)
                Запрос з = запрос.значение;
                если (з.путь == "/")
                    ответ_html(клиент, 200, "<h1>Привет из Сферы!</h1><p><a href=\"/время\">Который час?</a></p>");
                иначе если (з.путь == "/время")
                    ответ_html(клиент, 200, "<p>Секунд с 1970 года: " + целое_в_текст(юникс_время()) + "</p>");
                иначе
                    ответ_html(клиент, 404, "<h1>Нет такой страницы</h1>");
                конец
            конец
            закрыть_сокет(клиент);
        конец
    конец
    вернуть 0;
конец

Запустите программу и откройте в браузере http://localhost:8090/. По адресу /время увидите число секунд, а по любому другому — страницу с кодом 404.

Как это устроено:

  • подключить веб; подключает библиотеку (она сама тянет за собой сеть и память);
  • открыть_порт создаёт сокет и начинает слушать порт; вернуть она может ошибку, если порт занят;
  • в бесконечном цикле принять_подключение ждёт клиента, прочитать_запрос разбирает запрос в структуру Запрос (поля метод, путь, параметры, заголовки, тело);
  • ответ_html(клиент, код, тело) отправляет страницу; в конце соединение закрывается.

Что умеет веб.код

  • параметр(з.параметры, "имя") — значение из строки запроса ?имя=значение;
  • ответ_файлом_для(…) — отдача файлов с докачкой (Range), ETag и поддержкой HEAD; большие файлы идут кусками по 64 КБ;
  • подставить(шаблон, имена, значения) — шаблоны с {{имя}};
  • значение_cookie, заголовок_cookie, ответ_json, экранировать_html;
  • принять_тело_в_файл — приём больших загрузок (через него сделана загрузка установщиков на этот сайт).

Многопоточность

Пример выше обслуживает по одному клиенту. Сайт запускает пул из 16 потоков, каждый из которых в цикле принимает подключения с общего сокета. Исходники — в репозитории в папке сайт/: сервер.код и сайт.код.

Безопасность

  • Любой путь, пришедший из запроса, проверяйте функцией путь_безопасен до обращения к файлам.
  • Весь текст пользователя, который вставляете в HTML, пропускайте через экранировать_html.
  • HTTPS в язык пока не встроен: на сервере ставьте перед программой Caddy или nginx (см. сайт/DEPLOY.md).

Упражнения

  1. Добавьте страницу /привет?имя=…, которая здоровается с гостем (не забудьте экранировать имя).
  2. Сделайте счётчик посещений в файле (подсказка: дописать_в_файл_бинарно и размер_файла).