1use std::fmt;
7
8use crate::{
9 Service,
10 error::{BoxError, ErrorContext, OpaqueError},
11 http::{Request, Response, StreamingBody},
12 net::client::EstablishedClientConnection,
13 service::BoxService,
14 telemetry::tracing,
15};
16
17#[doc(inline)]
18pub use ::rama_http_backend::client::*;
19
20pub mod builder;
21#[doc(inline)]
22pub use builder::EasyHttpWebClientBuilder;
23
24#[cfg(feature = "socks5")]
25mod proxy_connector;
26#[cfg(feature = "socks5")]
27#[cfg_attr(docsrs, doc(cfg(feature = "socks5")))]
28#[doc(inline)]
29pub use proxy_connector::{MaybeProxiedConnection, ProxyConnector, ProxyConnectorLayer};
30
31pub struct EasyHttpWebClient<BodyIn, ConnResponse> {
43 connector: BoxService<Request<BodyIn>, ConnResponse, BoxError>,
44}
45
46impl<BodyIn, ConnResponse> fmt::Debug for EasyHttpWebClient<BodyIn, ConnResponse> {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 f.debug_struct("EasyHttpWebClient").finish()
49 }
50}
51
52impl<BodyIn, ConnResponse> Clone for EasyHttpWebClient<BodyIn, ConnResponse> {
53 fn clone(&self) -> Self {
54 Self {
55 connector: self.connector.clone(),
56 }
57 }
58}
59
60impl EasyHttpWebClient<(), ()> {
61 #[must_use]
63 pub fn builder() -> EasyHttpWebClientBuilder {
64 EasyHttpWebClientBuilder::new()
65 }
66}
67
68impl<Body> Default
69 for EasyHttpWebClient<Body, EstablishedClientConnection<HttpClientService<Body>, Request<Body>>>
70where
71 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
72{
73 #[cfg(feature = "boring")]
74 fn default() -> Self {
75 let tls_config =
76 rama_tls_boring::client::TlsConnectorDataBuilder::new_http_auto().into_shared_builder();
77
78 EasyHttpWebClientBuilder::new()
79 .with_default_transport_connector()
80 .with_tls_proxy_support_using_boringssl()
81 .with_proxy_support()
82 .with_tls_support_using_boringssl(Some(tls_config))
83 .with_default_http_connector()
84 .build()
85 }
86
87 #[cfg(all(feature = "rustls", not(feature = "boring")))]
88 fn default() -> Self {
89 let tls_config = rama_tls_rustls::client::TlsConnectorData::new_http_auto()
90 .expect("connector data with http auto");
91
92 EasyHttpWebClientBuilder::new()
93 .with_default_transport_connector()
94 .with_tls_proxy_support_using_rustls()
95 .with_proxy_support()
96 .with_tls_support_using_rustls(Some(tls_config))
97 .with_default_http_connector()
98 .build()
99 }
100
101 #[cfg(not(any(feature = "rustls", feature = "boring")))]
102 fn default() -> Self {
103 EasyHttpWebClientBuilder::new()
104 .with_default_transport_connector()
105 .without_tls_proxy_support()
106 .with_proxy_support()
107 .without_tls_support()
108 .with_default_http_connector()
109 .build()
110 }
111}
112
113impl<BodyIn, ConnResponse> EasyHttpWebClient<BodyIn, ConnResponse> {
114 #[must_use]
116 pub fn new(connector: BoxService<Request<BodyIn>, ConnResponse, BoxError>) -> Self {
117 Self { connector }
118 }
119
120 #[must_use]
122 pub fn with_connector<BodyInNew, ConnResponseNew>(
123 self,
124 connector: BoxService<Request<BodyInNew>, ConnResponseNew, BoxError>,
125 ) -> EasyHttpWebClient<BodyInNew, ConnResponseNew> {
126 EasyHttpWebClient { connector }
127 }
128}
129
130impl<Body, ModifiedBody, ConnResponse> Service<Request<Body>>
131 for EasyHttpWebClient<Body, EstablishedClientConnection<ConnResponse, Request<ModifiedBody>>>
132where
133 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
134 ModifiedBody:
135 StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
136 ConnResponse: Service<Request<ModifiedBody>, Response = Response, Error = BoxError>,
137{
138 type Response = Response;
139
140 type Error = OpaqueError;
141
142 async fn serve(&self, req: Request<Body>) -> Result<Self::Response, Self::Error> {
143 let uri = req.uri().clone();
144
145 let EstablishedClientConnection { req, conn } = self.connector.serve(req).await?;
146 tracing::trace!(url.full = %uri, "send http req to connector stack");
148
149 let result = conn.serve(req).await;
150
151 let resp = result
152 .map_err(OpaqueError::from_boxed)
153 .with_context(|| format!("http request failure for uri: {uri}"))?;
154
155 tracing::trace!(url.full = %uri, "response received from connector stack");
156
157 Ok(resp)
158 }
159}