Skip to main content

timvw_rust_runtime/
reqwest_service.rs

1use std::task::{Context, Poll};
2
3use tower::Service;
4
5use crate::maybe::BoxFuture;
6use crate::{Body, BoxError};
7
8/// A tower service that sends `http::Request<Body>` with a `reqwest::Client`.
9///
10/// A body held in memory goes to reqwest as bytes (so reqwest can clone the request); a stream is wrapped.
11///
12/// On WebAssembly (`target_family = "wasm"`) reqwest sends with the JavaScript host's `fetch`, which takes a request
13/// body whole: a stream (a streamed [`Upload`](crate::Upload), a multipart form with one) is read into memory first.
14/// The response body is still read as it arrives.
15#[derive(Clone, Debug, Default)]
16pub struct ReqwestService {
17    client: reqwest::Client,
18}
19
20impl ReqwestService {
21    /// A service that sends with `client`.
22    pub fn new(client: reqwest::Client) -> Self {
23        Self { client }
24    }
25
26    /// The reqwest client.
27    pub fn client(&self) -> &reqwest::Client {
28        &self.client
29    }
30}
31
32#[cfg(not(target_family = "wasm"))]
33impl Service<http::Request<Body>> for ReqwestService {
34    type Response = http::Response<reqwest::Body>;
35    type Error = BoxError;
36    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
37
38    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
39        Poll::Ready(Ok(()))
40    }
41
42    fn call(&mut self, request: http::Request<Body>) -> Self::Future {
43        let client = self.client.clone();
44        Box::pin(async move {
45            let request = request.map(|body| match body.as_bytes() {
46                Some(bytes) => reqwest::Body::from(bytes::Bytes::copy_from_slice(bytes)),
47                None => reqwest::Body::wrap(body),
48            });
49            let request = reqwest::Request::try_from(request)?;
50            let response = client.execute(request).await?;
51            Ok(http::Response::from(response))
52        })
53    }
54}
55
56#[cfg(target_family = "wasm")]
57impl Service<http::Request<Body>> for ReqwestService {
58    type Response = http::Response<Body>;
59    type Error = BoxError;
60    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
61
62    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
63        Poll::Ready(Ok(()))
64    }
65
66    fn call(&mut self, request: http::Request<Body>) -> Self::Future {
67        let client = self.client.clone();
68        Box::pin(async move {
69            let (parts, body) = request.into_parts();
70            let body = body.collect_bytes().await?;
71            let request = http::Request::from_parts(parts, reqwest::Body::from(body));
72            let request = reqwest::Request::try_from(request)?;
73            let mut response = client.execute(request).await?;
74            let mut builder = http::Response::builder().status(response.status());
75            if let Some(headers) = builder.headers_mut() {
76                *headers = std::mem::take(response.headers_mut());
77            }
78            Ok(builder.body(Body::wrap_stream(response.bytes_stream()))?)
79        })
80    }
81}