Skip to main content

timvw_rust_runtime/
reqwest_service.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use tower::Service;
6
7use crate::{Body, BoxError};
8
9/// A tower service that sends `http::Request<Body>` with a `reqwest::Client`.
10///
11/// A body held in memory goes to reqwest as bytes (so reqwest can clone the request); a stream is wrapped.
12#[derive(Clone, Debug, Default)]
13pub struct ReqwestService {
14    client: reqwest::Client,
15}
16
17impl ReqwestService {
18    /// A service that sends with `client`.
19    pub fn new(client: reqwest::Client) -> Self {
20        Self { client }
21    }
22
23    /// The reqwest client.
24    pub fn client(&self) -> &reqwest::Client {
25        &self.client
26    }
27}
28
29impl Service<http::Request<Body>> for ReqwestService {
30    type Response = http::Response<reqwest::Body>;
31    type Error = BoxError;
32    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
33
34    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
35        Poll::Ready(Ok(()))
36    }
37
38    fn call(&mut self, request: http::Request<Body>) -> Self::Future {
39        let client = self.client.clone();
40        Box::pin(async move {
41            let request = request.map(|body| match body.as_bytes() {
42                Some(bytes) => reqwest::Body::from(bytes::Bytes::copy_from_slice(bytes)),
43                None => reqwest::Body::wrap(body),
44            });
45            let request = reqwest::Request::try_from(request)?;
46            let response = client.execute(request).await?;
47            Ok(http::Response::from(response))
48        })
49    }
50}