跳过正文

http/http_body crate

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

http/http_body crate 是公共的 http 和 body 定义,在 tokio 系列项目,如 hyper/axum/reqwest,中得到广泛应用,这些 crate 通过 import + pub use 的方式将 http/http_body 的类型导入和使用。

比如 reqwest::method 实际是 http::method , 但是也有一些是封装,例如 reqwest::Request/reqwest::Response 是 http::Requesthttp::Response 的封装。

1 request
#

http::request module 包含三个类型:

  1. struct Request
  2. struct Parts
  3. struct Builder

http::request::Request%3CT%3E 是泛型类型,T 对应 body 的数据类型:

  1. Request<()>:未设置 body,具有创建 Builder 的一系列函数,后续使用 Builder 的 body() 方法来设置 body 并返回对应的 Request<T> 类型对象;
    • Request::builder() 函数返回的是 Request<()> 类型;
  2. Request<T> :设置 body 内容为 T 类型。T 没有限界,所以默认是 Sized;
    • Request::new(body: T) 函数返回的是 Request<()> 类型;

Request 由 Parts + body 组成,Parts 是 Request 中除 body 外的信息(类似的还有 http::response::Parts 表示 Response 中除 body 外的内容)。

Request 还可以关联 Extensions,用来传递一些自定义内容。

  • 类似于 Context,是根据值类型来保存和获取的,所以一般需要和自定义类型结合使用。
// impl Request<()> 实现的关联函数,返回各种类型的 Builder,如:
//
// Request::builder().uri("https://www.rust-lang.org/")
// Request::delete("https://www.rust-lang.org/")
//     .body(())
//     .unwrap();

pub fn builder() -> Builder
pub fn get<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn put<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn post<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn delete<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn options<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn head<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn connect<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn patch<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn trace<T>(uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>

// 以下是 impl<T> Request<T> 的方法
// 创建 Request<T> 类型对象时需要传入 body,

pub fn new(body: T) -> Request<T> // 创建一个 HTTP 1.1 GET / 请求
    let request = Request::new("hello world"); //  内容为 body
    assert_eq!(*request.method(), Method::GET);
    assert_eq!(*request.body(), "hello world");
pub fn from_parts(parts: Parts, body: T) -> Request<T>
pub fn method(&self) -> &Method
pub fn method_mut(&mut self) -> &mut Method
pub fn uri(&self) -> &Uri
pub fn uri_mut(&mut self) -> &mut Uri
pub fn version(&self) -> Version
pub fn version_mut(&mut self) -> &mut Version
pub fn headers(&self) -> &HeaderMap<HeaderValue>
pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue>
pub fn extensions(&self) -> &Extensions
pub fn extensions_mut(&mut self) -> &mut Extensions
    let mut request: Request<()> = Request::default();
    request.extensions_mut().insert("hello");
    assert_eq!(request.extensions().get(), Some(&"hello"));

pub fn body(&self) -> &T
pub fn body_mut(&mut self) -> &mut T

pub fn into_body(self) -> T
// 返回 http reqeust Parts 和 body 内容
pub fn into_parts(self) -> (Parts, T)

pub fn map<F, U>(self, f: F) -> Request<U>where F: FnOnce(T) -> U,

http::request::Builder 用于设置 Request 的具体参数:的方法可以设置

  • body<T>(self, body: T):消耗 Builder,使用传入的 body 内容返回一个 Request。
// impl Builder 的方法

pub fn new() -> Builder

pub fn method<T>(self, method: T) -> Builder where Method: TryFrom<T>, <Method as TryFrom<T>>::Error: Into<Error>
pub fn method_ref(&self) -> Option<&Method>

pub fn uri<T>(self, uri: T) -> Builder where Uri: TryFrom<T>, <Uri as TryFrom<T>>::Error: Into<Error>
pub fn uri_ref(&self) -> Option<&Uri>

pub fn version(self, version: Version) -> Builder
pub fn version_ref(&self) -> Option<&Version>

pub fn header<K, V>(self, key: K, value: V) -> Builder where
    HeaderName: TryFrom<K>, <HeaderName as TryFrom<K>>::Error: Into<Error>,
    HeaderValue: TryFrom<V>, <HeaderValue as TryFrom<V>>::Error: Into<Error>
pub fn headers_ref(&self) -> Option<&HeaderMap<HeaderValue>>
pub fn headers_mut(&mut self) -> Option<&mut HeaderMap<HeaderValue>>

// 为 Request 关联一个 Extension<T>
pub fn extension<T>(self, extension: T) -> Builder where T: Clone + Any + Send + Sync + 'static
pub fn extensions_ref(&self) -> Option<&Extensions>
pub fn extensions_mut(&mut self) -> Option<&mut Extensions>

// body() 消耗 Builder,用传入的 body 返回一个 Request<T>
pub fn body<T>(self, body: T) -> Result<Request<T>>

http::request::Parts 是 Request 除 body 外的信息(类似的还有 http::response::Parts 表示 Response 除 body 外的内容):

pub struct Parts {
    pub method: Method,
    pub uri: Uri,
    pub version: Version,
    pub headers: HeaderMap<HeaderValue>,
    pub extensions: Extensions,
    /* private fields */
}

示例:

let mut request: Request<String> = Request::default();
assert!(request.body().is_empty());
request.body_mut().push_str("hello world");
assert!(!request.body().is_empty());

use http::{Request, Response};

// Request::builder() 创建的是 Request<()> 类型对象,所以 request.body(()) 传入的是 ();
let mut request = Request::builder()
    .uri("https://www.rust-lang.org/")
    .header("User-Agent", "my-awesome-agent/1.0");
if needs_awesome_header() {
    request = request.header("Awesome", "yes");
}

let response = send(request.body(()).unwrap());
fn send(req: Request<()>) -> Response<()> {
    // ...
}

// Inspecting a request to see what was sent.
use http::{Request, Response, StatusCode};

fn respond_to(req: Request<()>) -> http::Result<Response<()>> {
// T 都是 () 类型
    if req.uri() != "/awesome-url" {
        return Response::builder()
            .status(StatusCode::NOT_FOUND)
            .body(())
    }

    let has_awesome_header = req.headers().contains_key("Awesome");
    let body = req.body();
    // ...
}

// Deserialize a request of bytes via json:
use http::Request;
use serde::de;

fn deserialize<T>(req: Request<Vec<u8>>) -> serde_json::Result<Request<T>>
    where for<'de> T: de::Deserialize<'de>,
{
    let (parts, body) = req.into_parts();
    let body = serde_json::from_slice(&body)?;
    Ok(Request::from_parts(parts, body))
}

// Or alternatively, serialize the body of a request to json
use http::Request;
use serde::ser;

fn serialize<T>(req: Request<T>) -> serde_json::Result<Request<Vec<u8>>>
    where T: ser::Serialize,
{
    let (parts, body) = req.into_parts();
    let body = serde_json::to_vec(&body)?;
    Ok(Request::from_parts(parts, body))
}

2 response
#

http::response module 包含:

  1. Response struct

  2. Parts struct

  3. Builder struct

    http::response::Response%3CT%3E 中 T 对应 body 的数据类型:

  4. Response<()>:未设置 body 的请求,具有创建 Builder 的一系列函数,后续使用 Builder 的 body() 方法来设置 body 并返回对应的 Response<T> 类型对象;

    • response::builder() 返回 Response<()> 类型;
  5. Response<T> :设置 body 内容为 T 类型的响应。T 没有限界,所以默认是 Sized。

    • response::new(body: T) 返回 Response<T> 类型;
// 创建一个 Response Builder
impl Response<()>
    pub fn builder() -> Builder

// 直接创建 Response,需要在 new() 创建时传入 body
impl<T> Response<T>
    pub fn new(body: T) -> Response<T>
    pub fn from_parts(parts: Parts, body: T) -> Response<T>
    pub fn status(&self) -> StatusCode
    pub fn status_mut(&mut self) -> &mut StatusCode
    pub fn version(&self) -> Version
    pub fn version_mut(&mut self) -> &mut Version
    pub fn headers(&self) -> &HeaderMap<HeaderValue>
    pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue>
    pub fn extensions(&self) -> &Extensions
    pub fn extensions_mut(&mut self) -> &mut Extensions
    pub fn body(&self) -> &T
    pub fn body_mut(&mut self) -> &mut T
    pub fn into_body(self) -> T
    pub fn into_parts(self) -> (Parts, T)
    pub fn map<F, U>(self, f: F) -> Response<U> where F: FnOnce(T) -> U,

http::response::Builder 类型方法:

impl Builder
    pub fn new() -> Builder
    pub fn status<T>(self, status: T) -> Builder where StatusCode: TryFrom<T>, <StatusCode as TryFrom<T>>::Error: Into<Error>
    pub fn version(self, version: Version) -> Builder
    pub fn header<K, V>(self, key: K, value: V) -> Builder
        where HeaderName: TryFrom<K>, <HeaderName as TryFrom<K>>::Error: Into<Error>, HeaderValue: TryFrom<V>,
        <HeaderValue as TryFrom<V>>::Error: Into<Error>
    pub fn headers_ref(&self) -> Option<&HeaderMap<HeaderValue>>
    pub fn headers_mut(&mut self) -> Option<&mut HeaderMap<HeaderValue>>
    // Response 可以关联 Extensions<T>
    pub fn extension<T>(self, extension: T) -> Builderwhere T: Clone + Any + Send + Sync + 'static
    pub fn extensions_ref(&self) -> Option<&Extensions>
    pub fn extensions_mut(&mut self) -> Option<&mut Extensions>
    // 消耗 Builder,使用传入的 T 类型 body,返回一个 Response<T>
    pub fn body<T>(self, body: T) -> Result<Response<T>>

http::response::Parts 表示除了 body 外的 HTTP 响应内容:

pub struct Parts {
    pub status: StatusCode,
    pub version: Version,
    pub headers: HeaderMap<HeaderValue>,
    pub extensions: Extensions,
    /* private fields */
}

示例:

pub struct Response<T> { /* private fields */ }

use http::{Request, Response, StatusCode};

fn respond_to(req: Request<()>) -> http::Result<Response<()>> {
    let mut builder = Response::builder()
        .header("Foo", "Bar")
        .status(StatusCode::OK);

    if req.headers().contains_key("Another-Header") {
        builder = builder.header("Another-Header", "Ack");
    }

    // Builder 的 body() 方法返回一个 Response<T> 对象
    builder.body(())
}

fn not_found(_req: Request<()>) -> http::Result<Response<()>> {
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(())
}

// 解序列化
fn deserialize<T>(res: Response<Vec<u8>>) -> serde_json::Result<Response<T>> where for<'de> T: de::Deserialize<'de>,
{
    let (parts, body) = res.into_parts();
    let body = serde_json::from_slice(&body)?;
    Ok(Response::from_parts(parts, body))
}

3 header #

http::header module 提供了三个类型:

HeaderMap
HTTP headers 集合
HeaderName
HTTP header field name
HeaderValue
HTTP header field value

HeaderName 提供了大小写不敏感的 header field name(内部会进行小写归一化),以加快比较和处理。

  • from_XX() 方法将传入的 key name 转换为 HTTP 标准 key name
pub struct HeaderName { /* private fields */ }

impl HeaderName
// 创建 HeaderName
pub fn from_bytes(src: &[u8]) -> Result<HeaderName, InvalidHeaderName>
pub fn from_lowercase(src: &[u8]) -> Result<HeaderName, InvalidHeaderName>
pub const fn from_static(src: &'static str) -> HeaderName
pub fn as_str(&self) -> &str

    // Parsing a lower case header
    let hdr = HeaderName::from_lowercase(b"content-length").unwrap();
    assert_eq!(CONTENT_LENGTH, hdr);

    // Parsing a header that contains uppercase characters
    assert!(HeaderName::from_lowercase(b"Content-Length").is_err());

    // Parsing a standard header
    let hdr = HeaderName::from_static("content-length");
    assert_eq!(CONTENT_LENGTH, hdr);

    // Parsing a custom header
    let CUSTOM_HEADER: &'static str = "custom-header";

    let a = HeaderName::from_lowercase(b"custom-header").unwrap();
    let b = HeaderName::from_static(CUSTOM_HEADER);
    assert_eq!(a, b);

// HeaerName  实现了 FromStr,姑可以使用字符串的 parse() 方法来创建 HeaderName
impl FromStr for HeaderName

http::header module 提供了标准的 HeaderName 常量定义:

pub const ACCEPT: HeaderName;
pub const RANGE: HeaderName;
// ...

HeaderValue 代表一个值,而 HasherMap 为 HeaderName 关联一系列 HeaderValue 值:

  • Header Value 一般 ASCII 字符串,但是 HTTP spec 没有做强制要求,所以也可能是二进制值;
  • HeaderValue 封装了上面的差别语义。
pub struct HeaderValue { /* private fields */ }

impl HeaderValue
    pub const fn from_static(src: &'static str) -> HeaderValue
    pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue>
    pub fn from_name(name: HeaderName) -> HeaderValue
    pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue>
    pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue> where     T: AsRef<[u8]> + 'static,
    pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue where  T: AsRef<[u8]> + 'static,
    pub fn to_str(&self) -> Result<&str, ToStrError>
    pub fn len(&self) -> usize
    pub fn is_empty(&self) -> bool
    pub fn as_bytes(&self) -> &[u8] 
    pub fn set_sensitive(&mut self, val: bool)
    pub fn is_sensitive(&self) -> bool

impl FromStr for HeaderValue // 可以使用 &str.parse() 来创建 HeaderValue

//  示例
let val = HeaderValue::from_static("hello");
assert_eq!(val, "hello");

let val = HeaderValue::from_str("hello").unwrap();
assert_eq!(val, "hello");

let val = HeaderValue::from_bytes(b"hello\xfa").unwrap();
assert_eq!(val, &b"hello\xfa"[..]);

HeaderMap 是高度优化的 HTTP Header 集合,内部使用 multimap 解构,一个heade field 可以有多个 header value,它的 APIs 接口和 HashMap 类似。

  • T 代表 header value 值类型,默认为 HeaderValue;
  • 一个 key 可以通过 append() 方法添加多个 value,后续通过 get_all() 获得该 key 的所有 value;
pub struct HeaderMap<T = HeaderValue> { /* private fields */ }

impl HeaderMap
pub fn new() -> HeaderMap

impl<T> HeaderMap<T>
pub fn with_capacity(capacity: usize) -> HeaderMap<T>
pub fn try_with_capacity( capacity: usize ) -> Result<HeaderMap<T>, MaxSizeReached>
pub fn len(&self) -> usize
pub fn keys_len(&self) -> usize
pub fn is_empty(&self) -> bool
pub fn clear(&mut self)
pub fn capacity(&self) -> usize
pub fn reserve(&mut self, additional: usize)
pub fn try_reserve(&mut self, additional: usize) -> Result<(), MaxSizeReached>

// 获得 header 对应的 value,这里的 key 可以是任何实现 AsHeaderName 的类型
pub fn get<K>(&self, key: K) -> Option<&T> where K: AsHeaderName
pub fn get_mut<K>(&mut self, key: K) -> Option<&mut T> where K: AsHeaderName
pub fn get_all<K>(&self, key: K) -> GetAll<'_, T> where K: AsHeaderName
pub fn contains_key<K>(&self, key: K) -> bool where K: AsHeaderName

// 支持迭代
pub fn iter(&self) -> Iter<'_, T>
pub fn iter_mut(&mut self) -> IterMut<'_, T>

pub fn keys(&self) -> Keys<'_, T>
pub fn values(&self) -> Values<'_, T>
pub fn values_mut(&mut self) -> ValuesMut<'_, T>

pub fn drain(&mut self) -> Drain<'_, T>

// 支持和 HashMap 类似的 Entry 修改机制
pub fn entry<K>(&mut self, key: K) -> Entry<'_, T> where K: IntoHeaderName
pub fn try_entry<K>( &mut self, key: K) -> Result<Entry<'_, T>, InvalidHeaderName>
   where K: AsHeaderName

// 添加 key-value,一个 key 可以通过 append() 添加多个 value
pub fn insert<K>(&mut self, key: K, val: T) -> Option<T> where K: IntoHeaderName
pub fn try_insert<K>( &mut self, key: K, val: T) -> Result<Option<T>, MaxSizeReached>
   where K: IntoHeaderName
pub fn append<K>(&mut self, key: K, value: T) -> bool where K: IntoHeaderName
pub fn try_append<K>( &mut self, key: K, value: T) -> Result<bool, MaxSizeReached>
   where K: IntoHeaderName

pub fn remove<K>(&mut self, key: K) -> Option<T> where K: AsHeaderName

// HeaderMap 实现了 Index trait,可以使用 headers[key] 操作
impl<K, T> Index<K> for HeaderMap<T> where K: AsHeaderName
  fn index(&self, index: K) -> &T
  type Output = T

HeaderMap 中使用了 AsHeaderName trait,字符串均实现了它:

impl AsHeaderName for String
impl<'a> AsHeaderName for &'a str
impl<'a> AsHeaderName for &'a String
impl AsHeaderName for HeaderName
impl<'a> AsHeaderName for &'a HeaderName

HeaderMap 实现了 Extend/FromIterators/TryFrom trait,可以快速创建 HeaderMap:

  • 但都要求使用 HeaderName 类型值;
impl<T> Extend<(HeaderName, T)> for HeaderMap<T>
    fn extend<I: IntoIterator<Item = (HeaderName, T)>>(&mut self, iter: I)

impl<T> Extend<(Option<HeaderName>, T)> for HeaderMap<T>
    fn extend<I: IntoIterator<Item = (Option<HeaderName>, T)>>(&mut self, iter: I)

impl<T> FromIterator<(HeaderName, T)> for HeaderMap<T>
    fn from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = (HeaderName, T)>,

impl<'a, K, V, T> TryFrom<&'a HashMap<K, V>> for HeaderMap<T>where
    K: Eq + Hash,
    HeaderName: TryFrom<&'a K>,
    <HeaderName as TryFrom<&'a K>>::Error: Into<Error>,
    T: TryFrom<&'a V>,
    T::Error: Into<Error>,

示例:

let mut headers = HeaderMap::new();

// parse() 将 &str 转换为 HeaderValue
headers.insert(HOST, "example.com".parse().unwrap());
headers.insert(CONTENT_LENGTH, "123".parse().unwrap());
assert!(headers.contains_key(HOST));
assert!(!headers.contains_key(LOCATION));
assert_eq!(headers[HOST], "example.com"); // index 操作
headers.remove(HOST);
assert!(!headers.contains_key(HOST));

let mut map = HeaderMap::new();
assert!(map.get("host").is_none());
map.insert(HOST, "hello".parse().unwrap());
assert_eq!(map.get(HOST).unwrap(), &"hello");
assert_eq!(map.get("host").unwrap(), &"hello");
map.append(HOST, "world".parse().unwrap());
assert_eq!(map.get("host").unwrap(), &"hello");

// 一个 key 可以有多个 value
let mut map = HeaderMap::new();
map.insert(HOST, "hello".parse().unwrap());
map.append(HOST, "goodbye".parse().unwrap());
let view = map.get_all("host");

let mut iter = view.iter();
assert_eq!(&"hello", iter.next().unwrap());
assert_eq!(&"goodbye", iter.next().unwrap());
assert!(iter.next().is_none());

4 method
#

http::method module 提供了 http Method struct 定义,它包含一些标准的 http method 常量定义:

pub struct Method(/* private fields */);

// Method 类型关联常量
impl Method
pub const GET: Method = _
pub const POST: Method = _
pub const PUT: Method = _
pub const DELETE: Method = _
pub const HEAD: Method = _
pub const OPTIONS: Method = _
pub const CONNECT: Method = _
pub const PATCH: Method = _
pub const TRACE: Method = _

// 创建 Method
pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod>
pub fn is_safe(&self) -> bool
pub fn is_idempotent(&self) -> bool
pub fn as_str(&self) -> &str

示例:

use http::Method;
assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
assert!(Method::GET.is_idempotent());
assert_eq!(Method::POST.as_str(), "POST");

5 status
#

http::status module 提供了 StatusCode struct 类型和标准 HTTP status 常量:

pub struct StatusCode(/* private fields */);

impl StatusCode

// 创建 StatusCode
pub fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode>
pub fn from_bytes(src: &[u8]) -> Result<StatusCode, InvalidStatusCode>

pub fn as_u16(&self) -> u16
pub fn as_str(&self) -> &str
pub fn canonical_reason(&self) -> Option<&'static str>
pub fn is_informational(&self) -> bool
pub fn is_success(&self) -> bool
pub fn is_redirection(&self) -> bool
pub fn is_client_error(&self) -> bool
pub fn is_server_error(&self) -> bool

// 标准 HTTP StatusCode:
pub const CONTINUE: StatusCode = _
pub const SWITCHING_PROTOCOLS: StatusCode = _
pub const PROCESSING: StatusCode = _
pub const OK: StatusCode = _
//..

示例:

use http::StatusCode;
assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
assert_eq!(StatusCode::NOT_FOUND.as_u16(), 404);
assert!(StatusCode::OK.is_success());

6 extensions
#

struct http::Extensions 在 Request 和 Response 中使用, 使用特定类型来保存数据。具有 get/insert/remove() 方法,它们都是根据泛型类型来获取或设置值的。

let mut ext = Extensions::new();
assert!(ext.insert(5i32).is_none());
assert!(ext.insert(4u8).is_none());
assert_eq!(ext.insert(9i32), Some(5i32));

let mut ext = Extensions::new();
assert!(ext.get::<i32>().is_none());
ext.insert(5i32);
assert_eq!(ext.get::<i32>(), Some(&5i32));

let mut ext = Extensions::new();
ext.insert(5i32);
assert_eq!(ext.remove::<i32>(), Some(5i32));
assert!(ext.get::<i32>().is_none());

// http::Request/Response 类型均可以保存 Extentions
let mut request: Request<()> = Request::default();
request.extensions_mut().insert("hello"); // &str 类型
assert_eq!(request.extensions().get(), Some(&"hello"));

7 uri
#

uri module 提供了 Uri/Parts/Port/Schema 等类型, Uri 并不等于 URL, Uri 可能只包含 Full URL 的部分内容。

Uri:

impl Uri
pub fn builder() -> Builder // 使用 Builder 来构造 Uri
pub fn from_parts(src: Parts) -> Result<Uri, InvalidUriParts>
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri> where T: AsRef<[u8]> + 'static,
pub fn from_static(src: &'static str) -> Self
pub fn into_parts(self) -> Parts

pub fn path_and_query(&self) -> Option<&PathAndQuery>
pub fn path(&self) -> &str
pub fn scheme(&self) -> Option<&Scheme>
pub fn scheme_str(&self) -> Option<&str>
pub fn authority(&self) -> Option<&Authority>
pub fn host(&self) -> Option<&str>
pub fn port(&self) -> Option<Port<&str>>
pub fn port_u16(&self) -> Option<u16>
pub fn query(&self) -> Option<&str>

示例:

// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
// |-|   |-------------------------------||--------| |-------------------| |-----|
//  |                  |                       |               |              |
// scheme          authority                 path            query         fragment

use http::Uri;

let uri = "/foo/bar?baz".parse::<Uri>().unwrap();
assert_eq!(uri.path(), "/foo/bar");
assert_eq!(uri.query(), Some("baz"));
assert_eq!(uri.host(), None);

let uri = "https://www.rust-lang.org/install.html".parse::<Uri>().unwrap();
assert_eq!(uri.scheme_str(), Some("https"));
assert_eq!(uri.host(), Some("www.rust-lang.org"));
assert_eq!(uri.path(), "/install.html");

可以使用字符串或 Uri::Builder 来创建 Uri:

pub struct Builder { /* private fields */ }

impl Builder
pub fn new() -> Builder
pub fn scheme<T>(self, scheme: T) -> Self where Scheme: TryFrom<T>, <Scheme as TryFrom<T>>::Error: Into<Error>,
pub fn authority<T>(self, auth: T) -> Self where Authority: TryFrom<T>, <Authority as TryFrom<T>>::Error: Into<Error>,
pub fn path_and_query<T>(self, p_and_q: T) -> Self where PathAndQuery: TryFrom<T>, <PathAndQuery as TryFrom<T>>::Error: Into<Error>,
pub fn build(self) -> Result<Uri, Error>

// 示例
let uri = uri::Builder::new()
    .scheme("https")
    .authority("hyper.rs")
    .path_and_query("/hello?foo=bar")
    .build()
    .unwrap();

8 http_body/http_body_util crate
#

http crateRequest<T>Response<T> 都是泛型类型,T 表示 HTTP Body 值类型。

http_body 提供了 FrameSizeHint 类型,以及 Body trait

Frame<T>: 代表任意类型的 HTTP Stream(Body),可以保存 data 或 trailers:

  1. T 是 data 的类型,在 Body trait 中使用该类型;
  2. trailers headers(类型为 HeaderMap)在 HTTP/2.0 中使用,表示 request/response exchange 的结束。
pub struct Frame<T> { /* private fields */ }

impl<T> Frame<T>
    pub fn data(buf: T) -> Self
    pub fn trailers(map: HeaderMap) -> Self

    pub fn map_data<F, D>(self, f: F) -> Frame<D> where F: FnOnce(T) -> D
    pub fn is_data(&self) -> bool
    pub fn into_data(self) -> Result<T, Self>
    pub fn data_ref(&self) -> Option<&T>
    pub fn data_mut(&mut self) -> Option<&mut T>

    pub fn is_trailers(&self) -> bool
    pub fn into_trailers(self) -> Result<HeaderMap, Self>
    pub fn trailers_ref(&self) -> Option<&HeaderMap>
    pub fn trailers_mut(&mut self) -> Option<&mut HeaderMap>

Body trait :代表 Request/Response 的 body Stream:

  • 提供异步的 poll_frame() 方法,每次返回 body 的部分数据, 封装到上面的 Frame 类型中:
  • Body 关联的 Data 类型需要实现 bytes::Buf trait,即包含读指针的缓冲区;
pub trait Body {
    type Data: Buf;
    type Error;

    // Required method,返回的数据封装在 Frame 类型中
    fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>>;

    // Provided methods
    fn is_end_stream(&self) -> bool { ... }
    fn size_hint(&self) -> SizeHint { ... }
}

Body 的实现类型:

  1. String 类型实现了 http_body::Body
  2. 当 B 是 http_body::Body 时,Request<B>/Response<B> 也实现了 http_body::Body。
impl Body for String
    type Data = Bytes // 返回 bytes::Bytes struct 类型
    type Error = Infallible
    fn poll_frame(
        self: Pin<&mut Self>,
        _cx: &mut Context<'_>
    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>>


impl<B: Body> Body for Request<B>
    type Data = <B as Body>::Data
    type Error = <B as Body>::Error
    fn poll_frame(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>
    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>>

impl<B: Body> Body for Response<B>
    type Data = <B as Body>::Data
    type Error = <B as Body>::Error
    fn poll_frame(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>
    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>>

其它 HTTP 库也提供了实现 http_body::Body trait 和 http_body_util::BodyExt trait 的 Body 类型,它们都可以作为异步 HTTP 请求或响应的 Body。

这些类型在实现 http_body::Body 时,Data 关联类型为 bytes::Bytes, 它满足 bytes::Buf trait 的限界要求。

  1. Struct reqwest::Body:

           // https://docs.rs/reqwest/latest/reqwest/struct.Body.html#impl-Body-for-Body
           pub struct Body { /* private fields */ }
    
           impl http_body::Body for Body
               type Data = Bytes // bytes::Bytes
               type Error = Error
    
  2. Struct axum::body::Body:

           // https://docs.rs/axum/latest/axum/body/struct.Body.html
           pub struct Body(/* private fields */); // The body type used in axum requests and responses.
    
           impl http_body::Body for Body
               type Data = Bytes
               type Error = Error
    
  3. Struct hyper::body::Incoming:

           // https://docs.rs/hyper/latest/hyper/body/struct.Incoming.html
           pub struct Incoming { /* private fields */ }
    
           impl http_body::Body for Incoming
               type Data = Bytes
               type Error = Error
    
  4. Struct hyper::Response:

           // https://docs.rs/hyper/latest/hyper/struct.Response.html
           pub struct Response<T> { /* private fields */ }
    
           // B 一般为 IncomingBody 类型,它实现了 Body, Data 为 Bytes
           impl<B> Body for Response<B> where B: Body,
               type Data = <B as Body>::Data
               type Error = <B as Body>::Error
    
           // https://docs.rs/hyper/latest/hyper/client/conn/http1/struct.SendRequest.html#method.send_request
           impl<B> SendRequest<B> where B: Body + 'static
               pub fn send_request(
                   &mut self,
                   req: Request<B>,
               ) -> impl Future<Output = Result<Response<IncomingBody>>>
    

注:hyper::body::Body 实际是 http_body::Body trait 的 pub use export。

http_body_util crate 提供了如下类型:

  1. http_body_util::BodyExt trait:为 Body trait 添加一系列方便使用的方法和适配器;
  2. http_body_util::Empty:总是为空的 Body 实现;
  3. http_body_util::Full:包含一个 chunk 的 Body 实现;
  4. http_body_util::BodyDataStream:从 Body Data 创建一个 Stream;
  5. http_body_util::BodyStream:从 Body 创建一个 Stream;
  6. http_body_util::StreamBody:从 Stream 创建 Body;

http_body_util::BodyExt trait

  1. 提供了便于使用的 frame() 方法, 可以在 while 循环中连续调用,返回实现 Future 的 Frame ,可以流式迭代。
  2. 提供了 into_data_stream() 方法,将 Body::Data 数据转换为 BodyDataStream ,它实现了 futures::stream::Stream trait ,通过使用 futures::stream::StreamExt的 next() 等方法,可以流式迭代;
  3. 提供了 collect() 方法,一次收集所有 Body 数据,而 frame() 需要持续迭代调用。
  4. with_trailers() 为 Body 添加 trailers;

所有实现了 http_body::Body trait 的对象 均实现 BodyExt。

pub trait BodyExt: Body { // http_body::Body
    // Provided methods

    fn frame(&mut self) -> Frame<'_, Self> where Self: Unpin { ... }

    fn map_frame<F, B>(self, f: F) -> MapFrame<Self, F>
       where Self: Sized, F: FnMut(Frame<Self::Data>) -> Frame<B>, B: Buf { ... }
    fn map_err<F, E>(self, f: F) -> MapErr<Self, F> where Self: Sized, F: FnMut(Self::Error) -> E { ... }

    fn boxed(self) -> BoxBody<Self::Data, Self::Error> where Self: Sized + Send + Sync + 'static { ... }
    fn boxed_unsync(self) -> UnsyncBoxBody<Self::Data, Self::Error> where Self: Sized + Send + 'static { ... }

    fn collect(self) -> Collect<Self> where Self: Sized { ... }

    fn with_trailers<F>(self, trailers: F) -> WithTrailers<Self, F>
       where Self: Sized, F: Future<Output = Option<Result<HeaderMap, Self::Error>>> { ... }

    fn into_data_stream(self) -> BodyDataStream<Self> where Self: Sized { ... }
}

impl<T> BodyExt for T where T: Body + ?Sized,

BodyDataStream 实现了 Stream 和 StreamExt trait, 每次迭代返回 Body::Data 内容:

pub struct BodyDataStream<B> { /* private fields */ }

impl<B> BodyDataStream<B> // A data stream created from a Body.
    pub fn new(body: B) -> Self //  Create a new BodyDataStream

// B 必须是 http_body::Body trait
impl<B> Stream for BodyDataStream<B> where B: Body,
    type Item = Result<<B as Body>::Data, <B as Body>::Error>
    fn poll_next(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Self::Item>>
    fn size_hint(&self) -> (usize, Option<usize>)

Empty 是空的 Body 实现:

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.

Full 也实现了 Body:

use http::{Request, Response};
use tower::{ServiceBuilder, ServiceExt, Service};
use tower_http::trace::TraceLayer;
use std::convert::Infallible;
use http_body_util::Full;
use bytes::Bytes;

// Request 是 http::Request, Response 是 http::Resonse, Full<Bytes> 实现了 http_body::Body trait
async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, Infallible> {
    Ok(Response::new(Full::default()))
}

tracing_subscriber::fmt::init();

let mut service = ServiceBuilder::new()
    .layer(TraceLayer::new_for_http())
    // 使用 ServiceBuilderExt 提供的 Layer 集成方法更方面
    // .trace_for_http()
    .service_fn(handle);

let request = Request::new(Full::from("foo"));
let response = service
    .ready()
    .await?
    .call(request)
    .await?;

示例:

use http::HeaderMap;
use http_body_util::{Full, BodyExt};
use bytes::Bytes;

async fn main() {
let (tx, rx) = tokio::sync::oneshot::channel::<HeaderMap>();

let body = Full::<Bytes>::from("Hello, World!")
    // add trailers via a future
    .with_trailers(async move {
        match rx.await {
            Ok(trailers) => Some(Ok(trailers)),
            Err(_err) => None,
        }
    });

// compute the trailers in the background
tokio::spawn(async move {
    let _ = tx.send(compute_trailers().await);
});

async fn compute_trailers() -> HeaderMap {
    // ...
}


#![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);

    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(())
}
rust crate - 这篇文章属于一个选集。
§ 16: 本文

相关文章

reqwest
··4647 字
Rust Rust-Crate
reqwest 是在 hyper 基础上实现的高层 HTTP Client 库,支持异步和同步。
axum
··14161 字
Rust Rust-Crate
axum 是基于 hyper 实现的高性能异步 HTTP 1/2 Server 库。
clap
··5510 字
Rust Rust-Crate
clap 用于快速构建命令行程序,提供命令&参数定义、解析等功能。
config
··1978 字
Rust Rust-Crate
config 提供从文件或环境变量解析配置参数的功能。