Skip to main content

roas_http_validator/adapters/
reqwest.rs

1//! `reqwest::Request` and `reqwest::blocking::Request`.
2//!
3//! The one adapter on the client's side of the exchange. Validating an
4//! *outgoing* request is what a spec-first test suite wants — the
5//! question "does the call I am about to make match the description?"
6//! is the one the Java ecosystem's validator is mostly used to answer —
7//! and it is not the same type as an incoming `http::Request`, since
8//! reqwest keeps a parsed `Url` rather than a `Uri`.
9//!
10//! This is also the one adapter that supplies the body. Everywhere else
11//! a body is a stream and buffering it is the caller's decision; a
12//! reqwest body that is not a stream is already bytes in memory, so
13//! there is nothing to buffer and nothing to decide. A streaming body
14//! still arrives as `None`, and [`RequestView::with_body`] takes over.
15
16use std::borrow::Cow;
17
18use crate::request::{RequestView, ToRequestView};
19
20impl ToRequestView for reqwest::Request {
21    fn request_view(&self) -> RequestView<'_> {
22        view(
23            self.method().as_str(),
24            self.url(),
25            self.headers(),
26            self.body().and_then(reqwest::Body::as_bytes),
27        )
28    }
29}
30
31impl ToRequestView for reqwest::blocking::Request {
32    fn request_view(&self) -> RequestView<'_> {
33        view(
34            self.method().as_str(),
35            self.url(),
36            self.headers(),
37            self.body().and_then(reqwest::blocking::Body::as_bytes),
38        )
39    }
40}
41
42fn view<'r>(
43    method: &'r str,
44    url: &'r reqwest::Url,
45    headers: &'r reqwest::header::HeaderMap,
46    body: Option<&'r [u8]>,
47) -> RequestView<'r> {
48    let mut view =
49        RequestView::new(method, url.path()).with_headers(headers.iter().map(|(name, value)| {
50            let value = match value.to_str() {
51                Ok(text) => Cow::Borrowed(text),
52                Err(_) => Cow::Owned(String::from_utf8_lossy(value.as_bytes()).into_owned()),
53            };
54            (Cow::Borrowed(name.as_str()), value)
55        }));
56    if let Some(query) = url.query() {
57        view = view.with_query(query);
58    }
59    match body {
60        Some(body) => view.with_body(body),
61        None => view,
62    }
63}