hyper 提供了 HTTP1/HTTP2 的 client 和 server 实现,属于底层库:
- 对于 client 建议使用
reqwest
; - 对于 server 建议使用
axum
;
hyper pub export 了 http 和 http_body crate
中的 header, Method, Request, Response,
StatusCode, Uri, Version 等类型,这些类型在 axum/tower 等 crate 中都得到了复用:
Result/Error:
pub struct Error { /* private fields */ }
pub type Result<T> = Result<T, Error>;
1 client #
hyper::client::conn module
提供了单连接的 HTTP1/HTTP2 Client(不支持连接池),建议使用更友好、高层的 reqwest
crate。
http1: 使用 hyper::client::conn::http1::handshake(io).await?;
返回的 SendRequest 对象来发送请求:
- io 是实现了 Read/Write 的 TCPStream。
- Struct hyper::body::Incoming 类型,实现了 http_body::Body 和 http_body_util::BodyExt trait,后续可以 while-let 调用 BodyExt 提供的 frame() 方法来流式获得响应。
pub async fn handshake<T, B>( io: T, ) -> Result<(SendRequest<B>, Connection<T, B>)> //
where
T: Read + Write + Unpin,
B: Body + 'static,
B::Data: Send,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
// 发送请求
impl<B> SendRequest<B> where B: Body + 'static
// Sends a Request on the associated connection.
pub fn send_request( &mut self, req: Request<B>, ) -> impl Future<Output = Result<Response<IncomingBody>>>
// 示例:创建一个缺省 http1 client,使用 hyper::client::conn::http1::Builder 来个性化配置
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?;
let mut res = sender.send_request(req).await?;
while let Some(next) = res.frame().await {
let frame = next?;
if let Some(chunk) = frame.data_ref() { // 获取 frame 中数据,chunk 类型是 bytes::Bytes
io::stdout().write_all(&chunk).await?;
}
}
// let body = res.collect().await?.aggregate();
hyper::client::conn::http1::Builder
用于配置 HTTP connection,最终提供了
handleshake() 方法来获得异步响应。配置的内容(部分):
- pub fn max_buf_size(&mut self, max: usize) -> &mut Self
- pub fn read_buf_exact_size(&mut self, sz: Option<usize>) -> &mut Builder
#![deny(warnings)]
#![warn(rust_2018_idioms)]
use std::env;
use bytes::Bytes;
use http_body_util::{BodyExt, Empty};
use hyper::Request;
use tokio::io::{self, AsyncWriteExt as _};
use tokio::net::TcpStream;
#[path = "../benches/support/mod.rs"]
mod support;
use support::TokioIo;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#[tokio::main]
async fn main() -> Result<()> {
pretty_env_logger::init();
// Some simple CLI args requirements...
let url = match env::args().nth(1) {
Some(url) => url,
None => {
println!("Usage: client <url>");
return Ok(());
}
};
// HTTPS requires picking a TLS implementation, so give a better warning if the
// user tries to request an 'https' URL.
let url = url.parse::<hyper::Uri>().unwrap();
if url.scheme_str() != Some("http") {
println!("This example only works with 'http' URLs.");
return Ok(());
}
fetch_url(url).await
}
async fn fetch_url(url: hyper::Uri) -> Result<()> {
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await?;
let io = TokioIo::new(stream);
// 创建一个缺省的 http1 client,也可以使用 hyper::client::conn::http1::Builder 来
// 个性化配置 client 参数。
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 path = url.path();
let req = Request::builder()
.uri(path)
.header(hyper::header::HOST, authority.as_str())
.body(Empty::<Bytes>::new())?; // 使用 http_body_util::Empty 来设置空的 Request Body.
// res 是 Struct hyper::Response 类型,实现了 hyper::body::Body trait 即
// http_body::Body trait,具体类型是 Struct hyper::body::Incoming
let mut res = sender.send_request(req).await?;
println!("Response: {}", res.status());
println!("Headers: {:#?}\n", res.headers());
// res 是 Struct hyper::body::Incoming 类型,实现了 http_body::Body 和
// http_body_util::BodyExt trait 这里调用 BodyExt 提供的 frame() 方法,
while let Some(next) = res.frame().await {
let frame = next?;
if let Some(chunk) = frame.data_ref() { // 获取 frame 中数据,chunk 类型是 bytes::Bytes
io::stdout().write_all(&chunk).await?;
}
}
// 或者使用 http_body_util::BodyExt 提供的 collect() 方法,一次聚合所有 Body 的内容。
// asynchronously aggregate the chunks of the body
// let body = res.collect().await?.aggregate();
// // try to parse as json with serde_json
// let users = serde_json::from_reader(body.reader())?;
Ok(users)
println!("\n\nDone!");
Ok(())
}
hyper::client::conn::http2::Builder 可以配置的内容:
- pub fn keep_alive_interval()
- pub fn keep_alive_timeout()
- pub fn max_frame_size()
- pub fn max_send_buf_size()
2 server #
hyper::server::conn module 提供了 http1/http2 两个 server 实现。