1use std::any::Any;
14use std::fmt;
15use std::sync::Arc;
16
17use bytes::Bytes;
18use http::{Request, Response};
19
20use super::middleware::HttpMiddleware;
21use super::{HttpClientExt, LazyBody, MultipartForm, Result, StreamingResponse};
22use crate::wasm_compat::{WasmBoxedFuture, WasmCompatSend, WasmCompatSync};
23
24pub(crate) trait ErasedHttpClient: WasmCompatSend + WasmCompatSync {
28 fn send_bytes(
29 &self,
30 req: Request<Bytes>,
31 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>>;
32
33 fn send_multipart_bytes(
34 &self,
35 req: Request<MultipartForm>,
36 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>>;
37
38 fn send_streaming_bytes(
41 &self,
42 req: Request<Bytes>,
43 ) -> WasmBoxedFuture<'_, Result<StreamingResponse>>;
44}
45
46impl<H> ErasedHttpClient for H
47where
48 H: HttpClientExt + 'static,
49{
50 fn send_bytes(
51 &self,
52 req: Request<Bytes>,
53 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>> {
54 Box::pin(self.send::<Bytes, Bytes>(req))
55 }
56
57 fn send_multipart_bytes(
58 &self,
59 req: Request<MultipartForm>,
60 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>> {
61 Box::pin(self.send_multipart::<Bytes>(req))
62 }
63
64 fn send_streaming_bytes(
65 &self,
66 req: Request<Bytes>,
67 ) -> WasmBoxedFuture<'_, Result<StreamingResponse>> {
68 Box::pin(self.send_streaming::<Bytes>(req))
69 }
70}
71
72#[derive(Clone)]
84pub struct DynHttpClient {
85 inner: Arc<dyn ErasedHttpClient>,
86 middleware: Vec<Arc<dyn HttpMiddleware>>,
89}
90
91impl DynHttpClient {
92 pub fn new<H>(http: H) -> Self
95 where
96 H: HttpClientExt + 'static,
97 {
98 if let Some(already) = (&http as &dyn Any).downcast_ref::<DynHttpClient>() {
99 return already.clone();
100 }
101 Self {
102 inner: Arc::new(http),
103 middleware: Vec::new(),
104 }
105 }
106
107 pub fn with_middleware<M>(mut self, middleware: M) -> Self
113 where
114 M: HttpMiddleware + 'static,
115 {
116 self.middleware.push(Arc::new(middleware));
117 self
118 }
119
120 pub fn ptr_eq(&self, other: &Self) -> bool {
125 Arc::ptr_eq(&self.inner, &other.inner)
126 }
127
128 async fn apply_request_middleware(
131 &self,
132 parts: &mut http::request::Parts,
133 body: Bytes,
134 ) -> Result<Bytes> {
135 for mw in &self.middleware {
136 mw.before_request_headers(&parts.method, &parts.uri, &mut parts.headers)
137 .await?;
138 }
139 let mut body = body;
140 for mw in &self.middleware {
141 body = mw
142 .before_request_body(&parts.method, &parts.uri, &parts.headers, body)
143 .await?;
144 }
145 Ok(body)
146 }
147
148 async fn apply_header_middleware(&self, parts: &mut http::request::Parts) -> Result<()> {
151 for mw in &self.middleware {
152 mw.before_request_headers(&parts.method, &parts.uri, &mut parts.headers)
153 .await?;
154 }
155 Ok(())
156 }
157
158 async fn apply_response_middleware(
160 &self,
161 method: &http::Method,
162 uri: &http::Uri,
163 status: http::StatusCode,
164 headers: &http::HeaderMap,
165 ) -> Result<()> {
166 for mw in &self.middleware {
167 mw.after_response(method, uri, status, headers).await?;
168 }
169 Ok(())
170 }
171}
172
173impl fmt::Debug for DynHttpClient {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 f.write_str("DynHttpClient")
176 }
177}
178
179impl HttpClientExt for DynHttpClient {
180 fn send<T, U>(
181 &self,
182 req: Request<T>,
183 ) -> impl Future<Output = Result<Response<LazyBody<U>>>> + WasmCompatSend + 'static
184 where
185 T: Into<Bytes>,
186 T: WasmCompatSend,
187 U: From<Bytes>,
188 U: WasmCompatSend + 'static,
189 {
190 let this = self.clone();
191 let (mut parts, body) = req.map(Into::into).into_parts();
192 async move {
193 let body = this.apply_request_middleware(&mut parts, body).await?;
194 let (method, uri) = (parts.method.clone(), parts.uri.clone());
195 let response = this
196 .inner
197 .send_bytes(Request::from_parts(parts, body))
198 .await?;
199 this.apply_response_middleware(&method, &uri, response.status(), response.headers())
200 .await?;
201 Ok(convert_body::<U>(response))
202 }
203 }
204
205 fn send_multipart<U>(
206 &self,
207 req: Request<MultipartForm>,
208 ) -> impl Future<Output = Result<Response<LazyBody<U>>>> + WasmCompatSend + 'static
209 where
210 U: From<Bytes>,
211 U: WasmCompatSend + 'static,
212 {
213 let this = self.clone();
214 let (mut parts, body) = req.into_parts();
215 async move {
216 this.apply_header_middleware(&mut parts).await?;
217 let (method, uri) = (parts.method.clone(), parts.uri.clone());
218 let response = this
219 .inner
220 .send_multipart_bytes(Request::from_parts(parts, body))
221 .await?;
222 this.apply_response_middleware(&method, &uri, response.status(), response.headers())
223 .await?;
224 Ok(convert_body::<U>(response))
225 }
226 }
227
228 fn send_streaming<T>(
229 &self,
230 req: Request<T>,
231 ) -> impl Future<Output = Result<StreamingResponse>> + WasmCompatSend
232 where
233 T: Into<Bytes> + WasmCompatSend,
234 {
235 let (mut parts, body) = req.map(Into::into).into_parts();
236 async move {
237 let body = self.apply_request_middleware(&mut parts, body).await?;
238 let (method, uri) = (parts.method.clone(), parts.uri.clone());
239 let response = self
240 .inner
241 .send_streaming_bytes(Request::from_parts(parts, body))
242 .await?;
243 self.apply_response_middleware(&method, &uri, response.status(), response.headers())
244 .await?;
245 Ok(response)
246 }
247 }
248}
249
250fn convert_body<U>(response: Response<LazyBody<Bytes>>) -> Response<LazyBody<U>>
251where
252 U: From<Bytes> + WasmCompatSend + 'static,
253{
254 response.map(|body| -> LazyBody<U> { Box::pin(async move { body.await.map(U::from) }) })
255}
256
257#[cfg(test)]
258mod tests;