跳过正文

hyper

··797 字
Rust Rust-Crate
目录
rust crate - 这篇文章属于一个选集。
§ 8: 本文

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 client 库。

http1: 使用 hyper::client::conn::http1::handshake(io).await?; 来发送请求。

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 可以配置的内容:

  1. pub fn keep_alive_interval()
  2. pub fn keep_alive_timeout()
  3. pub fn max_frame_size()
  4. pub fn max_send_buf_size()

2 server
#

hyper::server::conn module 提供了 http1/http2 两个 server 实现。

rust crate - 这篇文章属于一个选集。
§ 8: 本文

相关文章

anyhow
··1816 字
Rust Rust-Crate
anyhow crate 提供了自定义 Error 类型和 Result 类型,Error 类型自带 backtrace 和 context,支持用户友好的格式化信息输出。
bytes
··2834 字
Rust Rust-Crate
bytes 提供了高效的 zero-copy 连续内存区域的共享和读写能力。
chrono
··4003 字
Rust Rust-Crate
chrono 提供了丰富的 Date/Time 类型和相关操作。
serde
··1546 字
Rust Rust-Crate
Rust 主流的序列化/反序列化库。