Skip to content

Commit

Permalink
http_interop: Implement Request conversion for http::request::Parts
Browse files Browse the repository at this point in the history
I found the current conversion for `http::request::Builder` to be
rather useless when the `http` crate and various crates providing `http`
interfaces like `oauth2` are designed to provide an `http::Request`
directly, and there being no way to convert from a `http::Request`
back into its `http::request::Builder`.  That, together with strange
infallible defaults instead of providing  `TryFrom` make the current
implementation cumbersome to use.

Fortunately `http` provides `http::Request::into_parts()` to get
back a `Parts` structure (which is wrapped in `Result<>` inside
`http::request::Builder` as sole member!) together with the request body
(a generic type) which the user can manually pass to `send_string()`,
`send_bytes()` or `call()` if there's no data.

Implement a `From<http::request::Parts> for ureq::Request` to support
this case, making `ureq` finally capable of sending `http::Request`s.
(Note that, despite exclusively consisting of `Result<Parts>`,
 `http::request::Builder` has no constructor from `Ok(Parts {..})` which
 would have also facilitated this use-case somewhat)
  • Loading branch information
MarijnS95 committed Oct 10, 2023
1 parent 10e1c4a commit df125ac
Showing 1 changed file with 55 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/http_interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,24 @@ impl From<Response> for http::Response<Vec<u8>> {
/// # Ok(())
/// # }
/// ```
///
/// # Converting from [`http::Request`]
///
/// Notably `ureq` does _not_ implement the conversion from [`http::Request`] because it contains
/// the body of a request together with the actual request data. However, [`http`] provides
/// [`http::Request::into_parts()`] to split out a request into [`http::request::Parts`] and a
/// `body`, for which the conversion _is_ implemented and can be used as follows:
///
/// ```
/// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true);
/// let http_request = http::Request::builder().method("GET").uri("http://example.com").body(vec![0u8]).unwrap();
/// let (http_parts, body) = http_request.into_parts();
/// let request: ureq::Request = http_parts.into();
/// request.send_bytes(&body)?;
/// # Ok(())
/// # }
/// ```
impl From<http::request::Builder> for Request {
fn from(value: http::request::Builder) -> Self {
let mut new_request = crate::agent().request(
Expand Down Expand Up @@ -197,6 +215,43 @@ impl From<http::request::Builder> for Request {
}
}

/// Converts [`http::request::Parts`] into a [`Request`].
///
/// Requires feature `ureq = { version = "*", features = ["http"] }`
///
/// An [`http::Request`] can be split out into its [`http::request::Parts`] and body as follows:
///
/// ```
/// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true);
/// let http_request = http::Request::builder().method("GET").uri("http://example.com").body(vec![0u8]).unwrap();
/// let (http_parts, body) = http_request.into_parts();
/// let request: ureq::Request = http_parts.into();
/// request.send_bytes(&body)?;
/// # Ok(())
/// # }
/// ```
impl From<http::request::Parts> for Request {
fn from(value: http::request::Parts) -> Self {
let mut new_request = crate::agent().request(value.method.as_str(), &value.uri.to_string());

for (name, value) in &value.headers {
// TODO: Aren't complete header values available as raw byte slices?
let mut raw_header: Vec<u8> = name.to_string().into_bytes();
raw_header.extend(b": ");
raw_header.extend(value.as_bytes());

let header = HeaderLine::from(raw_header)
.into_header()
.expect("Unreachable");

crate::header::add_header(&mut new_request.headers, header)
}

new_request
}
}

/// Converts a [Request](crate::Request) into an [http] [Builder](http::request::Builder).
///
/// This will only convert valid UTF-8 header values into headers on the
Expand Down

0 comments on commit df125ac

Please sign in to comment.