【Rust每周一庫】hyper – 底層http庫

  • 2020 年 2 月 12 日
  • 筆記

現在說到寫應用,網路框架肯定是必不可少的。今天就給大家簡單介紹一下hyper。hyper是一個偏底層的http庫,支援HTTP/1和HTTP/2,支援非同步Rust,並且同時提供了服務端和客戶端的API支援。很多同學可能覺得既然hyper是個偏底層的框架,那是不是就不需要去了解了呢?首先很多上層的框架,比如rocket、iron和reqwest底層都是基於hyper的。(關於Rust中各種網路開發框架,這裡有個很全面的綜述和比較。)所以如果在使用這些框架的時候遇到了一些問題,對hyper的了解肯定是有一定的幫助的。再者學習Rust的我們都是奔著成為大佬的路線去的,很難說不會有直接操作偏底層框架的需求。

Hello World

我們首先來實現一個簡單的伺服器端和客戶端,支援最簡單的GET操作。

伺服器端

首先是依賴,除了hyper本身之外,我們還需要tokio的runtime去執行async函數

[dependencies]  hyper = "0.13"  tokio = { version = "0.2", features = ["full"] }  

然後就是main.rs

use std::{convert::Infallible, net::SocketAddr};  use hyper::{Body, Request, Response, Server};  use hyper::service::{make_service_fn, service_fn};    // 返回200  async fn handle(_: Request<Body>) -> Result<Response<Body>, Infallible> {      Ok(Response::new("Hello, World!n".into()))  }    #[tokio::main]  async fn main() {      let addr = SocketAddr::from(([127, 0, 0, 1], 3000));        // 從handle創建一個服務      let make_svc = make_service_fn(|_conn| async {          Ok::<_, Infallible>(service_fn(handle))      });        let server = Server::bind(&addr).serve(make_svc);        // 運行server      if let Err(e) = server.await {          eprintln!("server error: {}", e);      }  }  

客戶端

依賴同伺服器端

use hyper::Client;  use hyper::body::HttpBody as _;  use tokio::io::{stdout, AsyncWriteExt as _};    #[tokio::main]  async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {        // 構建一個client,調用GET      let client = Client::new();      let uri = "http://127.0.0.1:3000".parse()?;      let mut resp = client.get(uri).await?;      println!("Response: {}", resp.status());        // 將response(是個stream)輸出到stdout      while let Some(chunk) = resp.body_mut().data().await {          stdout().write_all(&chunk?).await?;      }        Ok(())  }  

先啟動服務端,然後啟動客戶端,就可以看到服務端成功相應客戶端的GET請求啦~

Response: 200 OK  Hello, World!  

更真實的例子

下面我們通過實現一個echo服務主要看一下伺服器端如何進行路由,以及如何支援POST請求

伺服器端

依賴

[dependencies]  hyper = "0.13"  tokio = { version = "0.2", features = ["full"] }  futures-util = { version = "0.3", default-features = false }  

程式碼

use futures_util::TryStreamExt;  use hyper::service::{make_service_fn, service_fn};  use hyper::{Body, Method, Request, Response, Server, StatusCode};    async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {      let mut response = Response::new(Body::empty());        // 通過req.method()和req.uri().path()來識別方法和請求路徑      match (req.method(), req.uri().path()) {          (&Method::GET, "/") => {              *response.body_mut() = Body::from("Try POSTing data to /echo");          },          (&Method::POST, "/echo") => {              // 將POST的內容保持不變返回              *response.body_mut() = req.into_body();          },          (&Method::POST, "/echo/uppercase") => {              // 把請求stream中的字母都變成大寫,並返回              let mapping = req                  .into_body()                  .map_ok(|chunk| {                      chunk.iter()                          .map(|byte| byte.to_ascii_uppercase())                          .collect::<Vec<u8>>()                  });                // 把stream變成body              *response.body_mut() = Body::wrap_stream(mapping);          },          (&Method::POST, "/echo/reverse") => {              // 這裡需要完整的body,所以需要等待全部的stream並把它們變為bytes              let full_body = hyper::body::to_bytes(req.into_body()).await?;                // 把body逆向              let reversed = full_body.iter()                  .rev()                  .cloned()                  .collect::<Vec<u8>>();                *response.body_mut() = reversed.into();          },          _ => {              *response.status_mut() = StatusCode::NOT_FOUND;          },      };        Ok(response)  }    #[tokio::main]  async fn main() {      let addr = ([127, 0, 0, 1], 3000).into();        let make_svc = make_service_fn(|_conn| async {          Ok::<_, hyper::Error>(service_fn(echo))      });        let server = Server::bind(&addr).serve(make_svc);        if let Err(e) = server.await {          eprintln!("server error: {}", e);      }  }  

客戶端

依賴和之前客戶端一樣。我們這裡的程式碼以向/echo/reverse提交內容為echo的POST請求為例:

use hyper::Client;  use hyper::{Body, Method, Request};    #[tokio::main]  async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {      let req = Request::builder()          .method(Method::POST)          .uri("http://127.0.0.1:3000/echo/reverse")          .body(Body::from("echo"))?;          let client = Client::new();      let resp = client.request(req).await?;      println!("Response: {}", resp.status());      println!("{:?}", hyper::body::to_bytes(resp.into_body()).await.unwrap());        Ok(())  }  

依次啟動服務端和客戶端,就可以看到服務端響應了客戶端的POST請求啦~

Response: 200 OK  b"ohce"  

好了,對hyper的介紹就到這裡了。接下來就靠大家自己去深似海的網路編程世界中去摸索啦~