0%

Rust 库学习之 hyper

Hyper 库是 Rust 中的一个高效的、相对底层的 HTTP 库,可以用于构建 HTTP Client、HTTP Server,支持 HTTP/1、HTTP/2,同时也支持异步 Rust。这篇文章将学习 hyper 库的基本使用方法。

创建 HTTP Server

首先学习如何使用 Hyper 库构建一个基本的 HTTP Server,HTTP Client。

添加 Service

Service 用于定义 HTTP Server 如何响应 HTTP 请求,一个 Service 代表一个异步函数,该异步函数接受一个 Request 并返回一个 Future。当该 Future 处理完成之后,它会得到一个 Response(或者 error)。

Hyper 库提供了一个工具函数 service_fn,它可以根据用户提供的一个函数来创建 Service。如下提供了一个 hello 函数:

1
2
3
async fn hello(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
Ok(Response::new(Full::new(Bytes::from("Hello, World"))))
}

当使用该函数作为 service 时,所有请求都会返回 200 响应,响应 body 会设置为 Hello, World!,响应头等字段会自动设置。

启动 Server

接下来需要将 hello service 添加到运行的 server 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await?;

loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);

tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(io, service_fn(hello))
.await
{
println!("Error serving connection {:?}", err);
}
});
}
}

创建 HTTP Client

接下来展示如何使用 Hpyer 库构建一个 HTTP client。在这个示例中,会给 http://httpbin.org/ip 发送一个 GET 请求,该站点会在 HTTP 响应中返回 client 的 IP 地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let url = "http://httpbin.org/ip".parse::<hyper::Uri>()?;
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);
let address = format!("{}:{}", host, port);

let stream = TcpStream::connect(address).await?;
let io = TokioIo::new(stream);
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?;

tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});

let authority = url.authority().unwrap().clone();

let req = Request::builder()
.uri(url)
.header(hyper::header::HOST, authority.as_str())
.body(Empty::<Bytes>::new())?;

let mut res = sender.send_request(req).await?;
println!("Response status: {}", res.status());

while let Some(next) = res.frame().await {
let frame = next?;
if let Some(chunk) = frame.data_ref() {
io::stdout().write_all(chunk).await?;
}
}
Ok(())
}

Reference