Начало работы с сервером
Давайте начнем с создания сервера "Hello, World!" и расширим его функциональность.
Первым делом нам нужно объявить наши зависимости. Добавим следующее в наш Cargo.toml:
[dependencies]
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }
Затем нам нужно добавить несколько импортов в наш файл main.rs:
extern crate tokio; extern crate hyper; extern crate http_body_util; extern crate hyper_util; use std::convert::Infallible; use std::net::SocketAddr; use http_body_util::Full; use hyper::body::Bytes; use hyper::server::conn::http1; use hyper::service::service_fn; use hyper::{Request, Response}; use hyper_util::rt::TokioIo; use tokio::net::TcpListener; fn main() {}
Создание Сервиса (Service)
[Service][service] позволяет нам определить, как наш сервер будет отвечать на входящие запросы. Он представляет собой асинхронную функцию, которая принимает [Request][request] и возвращает Future. Когда обработка этого future завершается, он разрешается в [Response][response] или ошибку.
Hyper предоставляет утилиту для создания Service из функции, которая должна покрыть большинство случаев использования: [service_fn][service_fn]. Мы используем её, чтобы создать сервис из нашей функции hello, когда будем готовы запустить сервер.
extern crate hyper; extern crate http_body_util; use std::convert::Infallible; use http_body_util::Full; use hyper::body::Bytes; use hyper::{Request, Response}; fn main() {} async fn hello(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> { Ok(Response::new(Full::new(Bytes::from("Hello, World!")))) }
Используя эту функцию в качестве сервиса, мы указываем нашему серверу отвечать на все запросы статусом 200 OK по умолчанию. Тело ответа Body будет содержать наше дружеское приветствие в виде одного фрагмента байтов (chunk of bytes), а заголовок Content-Length будет установлен автоматически.
Запуск сервера
Наконец, нам нужно подключить наш сервис hello к работающему серверу Hyper.
Мы углубимся в детали некоторых из этих вещей в другом руководстве.
extern crate tokio; extern crate hyper; extern crate http_body_util; extern crate hyper_util; mod no_run { use std::convert::Infallible; use std::net::SocketAddr; use http_body_util::Full; use hyper::body::Bytes; use hyper::server::conn::http1; use hyper::service::service_fn; use hyper::{Request, Response}; use hyper_util::rt::TokioIo; use tokio::net::TcpListener; async fn hello( _: Request<hyper::body::Incoming>, ) -> Result<Response<Full<Bytes>>, Infallible> { Ok(Response::new(Full::new(Bytes::from("Hello World!")))) } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); // Мы создаем TcpListener и привязываем его к 127.0.0.1:3000 let listener = TcpListener::bind(addr).await?; // Мы запускаем цикл для непрерывного принятия входящих соединений loop { let (stream, _) = listener.accept().await?; // Используем адаптер для доступа к чему-то, реализующему трейты `tokio::io`, как если бы они реализовывали // трейты ввода-вывода `hyper::rt`. let io = TokioIo::new(stream); // Порождаем задачу tokio для конкурентного обслуживания множественных соединений tokio::task::spawn(async move { // Наконец, мы привязываем входящее соединение к нашему сервису `hello` if let Err(err) = http1::Builder::new() // `service_fn` преобразует нашу функцию в `Service` .serve_connection(io, service_fn(hello)) .await { eprintln!("Ошибка при обслуживании соединения: {:?}", err); } }); } } } fn main() {}
Чтобы увидеть все фрагменты кода, собранные вместе, ознакомьтесь с [полным примером][example]!
Кроме того, если service_fn не удовлетворяет вашим требованиям и вы хотите реализовать Service самостоятельно, см. этот [пример][impl service].
HTTP/2
Этот пример использует модуль http1 для создания сервера, который работает по протоколу HTTP/1.
Если вы хотите использовать HTTP/2, вы можете использовать модуль http2 с небольшими изменениями по сравнению с сервером http1. Сборщик (builder) для http2 требует для работы исполнитель (executor). Исполнитель должен реализовывать трейт hyper::rt::Executor.
Чтобы реализовать Executor, ознакомьтесь с примером [runtime][runtime]. Чтобы увидеть полный пример с HTTP/2, ознакомьтесь с [полным примером][example_http2]!
[service]: {{ site.hyper_docs_url }}/hyper/service/trait.Service.html [service_fn]: {{ site.hyper_docs_url }}/hyper/service/fn.service_fn.html [request]: {{ site.hyper_docs_url }}/hyper/struct.Request.html [response]: {{ site.hyper_docs_url }}/hyper/struct.Response.html [parts]: {{ site.http_docs_url }}/http/response/struct.Parts.html [example]: {{ site.examples_url }}/hello.rs [example_http2]: {{ site.examples_url }}/hello-http2.rs [runtime]: ../init/runtime.md [impl service]: {{ site.examples_url }}/service_struct_impl.rs