Применение: HTTP сервер

Давайте используем async/.await для создания echo-сервера!

Для начала, запустите rustup update stable чтобы быть уверенным, что используете стабильную версию Rust - 1.39 или более новую. Когда вы закончите это, создайте новый проект с помощь cargo new async-await-echo и откройте созданную директорию async-await-echo.

Добавим некоторые зависимости в файл Cargo.toml:

{{#include ../../examples/01_05_http_server/Cargo.toml:9:18}}

Теперь, когда у нас есть свои зависимости, давайте начнём писать код. Вот список зависимостей, которые необходимо добавить:


#![allow(unused)]
fn main() {
{{#include ../../examples/01_05_http_server/src/lib.rs:imports}}
}

Как только закончим с импортами, мы можем собрать вместе весь шаблонный код, который позволит обрабатывать запросы:


#![allow(unused)]
fn main() {
{{#include ../../examples/01_05_http_server/src/lib.rs:boilerplate}}
}

Если вы сейчас запустите cargo run, в консоли вы увидите сообщение "Listening on http://127.0.0.1:3000". Если вы откроете URL в вашем любимом браузере, вы увидите как в нём отобразится "hello, world!". Поздравляем! Вы только что написали свой первый асинхронный web-сервер на Rust.

Вы также можете посмотреть сам запрос, который содержит такую информацию, как URI, версию HTTP, заголовки и другие метаданные. Например, мы можем вывести URI запроса следующим образом:


#![allow(unused)]
fn main() {
println!("Got request at {:?}", req.uri());
}

Вы могли заметить, что мы до сих пор не делали ничего асинхронного для обработки запроса - мы только незамедлительно ответили на него, мы не пользуемся гибкостью, которую нам даёт async fn. Вместо этого, мы только возвращаем статическое сообщение. Давайте попробуем проксировать пользовательский запрос на другой web-сайт используя HTTP-клиент Hyper'а.

Мы начнём с парсинга URL, который мы хотим запросить:


#![allow(unused)]
fn main() {
{{#include ../../examples/01_05_http_server/src/lib.rs:parse_url}}
}

Затем мы создадим новый hyper::Client и используем его для создания GET запроса, который вернём пользователю ответ:


#![allow(unused)]
fn main() {
{{#include ../../examples/01_05_http_server/src/lib.rs:get_request}}
}

Client::get возвращает hyper::client::FutureResponse, который реализует Future<Output = Result<Response, Error>> (или Future<Item = Response, Error = Error> в терминах futures 0.1). Когда мы разрешаем (.await) футуру, отправляется HTTP-запрос, текущая задача приостанавливается и становится в очередь, чтобы продолжить работу после получения ответа.

Если вы сейчас запустите cargo run и откроете http://127.0.0.1:3000/foo в браузере, вы увидите домашнюю страницу Rust, а в консоли следующий вывод:

Listening on http://127.0.0.1:3000
Got request at /foo
making request to http://www.rust-lang.org/en-US/
request finished-- returning response

Поздравляем! Вы только что проксировали HTTP запрос.