Введение
Rust - высокоэффективный язык программирования, стоящий на самом любимом языке 7 лет подряд. Это связано с тем, что он является производительным, безопасным для памяти и очень гибким. Сегодня мы узнаем о создании HTTP-сервера на rust и изучим несколько концепций языка.
Начнем, убедитесь, что в вашей системе установлен rust, и давайте создадим новый проект.
Построение TCP-сервера
Создание TCP-сервера
Протокол HTTP работает поверх протокола TCP уровня L4 (сетевой уровень). При создании сервера фактически создается сервер TCP, и данные отправляются в формате HTTP, т.е. TCP указывает, как происходит связь, а HTTP указывает схему данных, которые должны быть отправлены по этому соединению.
Откройте `main.rs` в любимой среде IDE и добавьте в нее следующий код:
Давайте перейдем к разбору кода.
Это макрос rust, который сообщает компилятору, что мы можем создать переменные, которые мы можем не использовать. Это упрощает обработку некоторых распространенных ошибок в процессе обучения, но это не следует использовать в производственной кодовой базе.
В основном bind возвращает функцию в перечислении типа Result , которое является способом выразить, что вышеупомянутая функция может или ответить паникой или результатом (здесь `TcpListener`), и мы хотим отказаться продолжать выполнение при ошибке, в другом случае продолжить программу.
Это имеет смысл использовать, если возникновение исключения может привести к остановке программы и она не сможет продолжать выполнение дальше, есть лучшие способы обработки такого исключения.
В имени функции `println` есть восклицательный знак, потому что она не является функцией и является макросом.
Метод `incoming()`, возвращает итератор потоков (в частности, потоков с типом, определяемым, например, `TcpStream`. Один поток представляет открытое соединение между клиентом и сервером, так что здесь мы в основном обходим по каждому соединению через цикл `for`.
Это может показаться тривиальным для наивного разработчика, но опытный человек может знать, что для обработки новых соединений в большинстве языков потребуется логика, управляемая событиями, что технически происходит и здесь, но это ленивый итератор, который автоматически вызывает следующую последовательность в цикле for, упрощая кодирование.
Поток переменных цикла возвращает `Result`, который необходимо развернуть. (Это не элегантный способ справиться с этим, так как один сбой соединения тут, разрушит все приложение, что не должно произойти продакшине).
Кроме того, обратите внимание, что имя переменного потока совпадает с именем потока переменной цикла, который называется затенением в rust. Это также присутствует в других языках, как javascript.
Протестируем код, выполнив запрос:
Вы, вероятно, должны получить что-то вроде
`Connection reset by peer`
Это потому, что мы ничего не делаем с соединением.
Создание обработчика соединений
Создание функции с именем `handle_connection` для данных потока:
О этой функции много можно рассказать, во-первых, ключевое слово `mut` сообщает rust, что `handle_connection` внесет изменения в поток (записав в него, чего мы сейчас не делаем).
Во второй строке мы создаем новый буфер с изменяемой ссылкой на тот же самый поток, то есть это ссылка кучи на тот же самый объект потока. (Больше пустяков дальше)
У нас есть некоторые простые буферные операции перед печатью самого вектора с помощью `{:#?}` это основной способ распечатать вектор в rust, вы также можете использовать `{:?}`, который распечатает все значения в одной строке.
Использование обработчика для потоков соединений
Одно важное примечание - Объект потока теперь принадлежит функции `handle_connection`, что означает, что на него больше нельзя ссылаться в цикле `for`. Если просто попытаться добавить `handle_connection(stream);` второй раз это даст вам ошибку во времени компиляции, указывающую, что поток переменных перемещен. Это очень мощный функционал rust, поскольку он упрощает очистку памяти.
Создание HTTP-соединения
Как мы узнали, HTTP - это просто способ определения структуры данных, передаваемых по TCP-соединению. Для получения дополнительной информации см. полное описание в разделе «Request For Comments».
Создание статуса ответа
Если бы вы сделали запрос с URL на локальном хосте (или открыли в браузере), вы бы не получили ответ, потому что мы фактически не отвечаем клиенту, вызывающему наш сервер.
Добавим это после `handle_connection` функции:
Это стандартные метаданные успеха, здесь сервер сообщает клиенту, что он получил информацию, отправленную клиентом, и все в порядке.
Обратите внимание, что\r\n присутствует в переменной ответа два раза, это стандартный разделитель строк, определенный в протоколе, часто называемый CRLF.
- CR - возврат каретки (`\r`)
- LF - переход на новую строку (`\n`)
Давайте отправим html
Создайте файл с именем `response.html`. Создайте его в корне приложения, а не внутри `src/`.
Замените последние 2 строки в функции `handle_connection` на эти:
`fs` - модуль, используемый для работы с файловой системой в rust.
Мы используем `format!` добавляя содержимое файла в качестве тела ответа об успешном выполнении вместе с длиной содержимого, требуемой протоколом.
Если открыть `localhost:8477` в браузере, должно визуализировать HTML-файл.
Обратите внимание, что если вы также откроете какой-либо маршрут в браузере `localhost:8477/vedant` например, это вернет вам тот же результат. Это не желаемый результат, так как мы хотим управлять ответом на основании того, по какому маршруту сделан запрос `GET`.
Добавление маршрутов
Здесь мы извлекаем строку состояния запроса и проверяем его, чтобы узнать, какой маршрут вызывается клиентом.
Если мы сделаем это, соединение останется открытым для любого маршрута, который не является корнем, и будет не иметь никакого ответа, ни успеха, ни неудачи. Давайте добавим 404 для других маршрутов.
Создание файла 404.html:
Добавить блок `else` в функцию `handle_request`:
Теперь любой маршрут, не являющийся корневым, приведет к ошибкам с содержимым 404.
Рефакторинг кода
Мы можем переделать вышеуказанную функцию, чтобы бы она была более краткой.
Здесь значения `status_line` и имя файла получаются в условии, вроде троичного оператора (но это не точно).
Статья на rusty-code.ru