1use std::fmt;
7
8use crate::{
9 Layer, Service,
10 error::{BoxError, ErrorContext, OpaqueError},
11 extensions::{ExtensionsMut, ExtensionsRef},
12 http::{Request, Response, StreamingBody},
13 net::client::EstablishedClientConnection,
14 rt::Executor,
15 service::BoxService,
16 telemetry::tracing,
17};
18
19#[doc(inline)]
20pub use ::rama_http_backend::client::*;
21
22pub mod builder;
23#[doc(inline)]
24pub use builder::EasyHttpConnectorBuilder;
25
26#[cfg(feature = "socks5")]
27mod proxy_connector;
28#[cfg(feature = "socks5")]
29#[cfg_attr(docsrs, doc(cfg(feature = "socks5")))]
30#[doc(inline)]
31pub use proxy_connector::{MaybeProxiedConnection, ProxyConnector, ProxyConnectorLayer};
32
33pub struct EasyHttpWebClient<BodyIn, ConnResponse, L> {
45 connector: BoxService<Request<BodyIn>, ConnResponse, BoxError>,
46 jit_layers: L,
47}
48
49impl<BodyIn, ConnResponse, L> fmt::Debug for EasyHttpWebClient<BodyIn, ConnResponse, L> {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.debug_struct("EasyHttpWebClient").finish()
52 }
53}
54
55impl<BodyIn, ConnResponse, L: Clone> Clone for EasyHttpWebClient<BodyIn, ConnResponse, L> {
56 fn clone(&self) -> Self {
57 Self {
58 connector: self.connector.clone(),
59 jit_layers: self.jit_layers.clone(),
60 }
61 }
62}
63
64impl EasyHttpWebClient<(), (), ()> {
65 #[must_use]
67 pub fn connector_builder() -> EasyHttpConnectorBuilder {
68 EasyHttpConnectorBuilder::new()
69 }
70}
71
72impl<Body> Default
73 for EasyHttpWebClient<
74 Body,
75 EstablishedClientConnection<HttpClientService<Body>, Request<Body>>,
76 (),
77 >
78where
79 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
80{
81 #[inline(always)]
82 fn default() -> Self {
83 Self::default_with_executor(Executor::default())
84 }
85}
86
87impl<Body>
88 EasyHttpWebClient<Body, EstablishedClientConnection<HttpClientService<Body>, Request<Body>>, ()>
89where
90 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
91{
92 #[cfg(feature = "boring")]
93 pub fn default_with_executor(exec: Executor) -> Self {
94 let tls_config =
95 rama_tls_boring::client::TlsConnectorDataBuilder::new_http_auto().into_shared_builder();
96
97 EasyHttpConnectorBuilder::new()
98 .with_default_transport_connector()
99 .with_tls_proxy_support_using_boringssl()
100 .with_proxy_support()
101 .with_tls_support_using_boringssl(Some(tls_config))
102 .with_default_http_connector(exec)
103 .build_client()
104 }
105
106 #[cfg(all(feature = "rustls", not(feature = "boring")))]
107 pub fn default_with_executor(exec: Executor) -> Self {
108 let tls_config = rama_tls_rustls::client::TlsConnectorData::try_new_http_auto()
109 .expect("connector data with http auto");
110
111 EasyHttpConnectorBuilder::new()
112 .with_default_transport_connector()
113 .with_tls_proxy_support_using_rustls()
114 .with_proxy_support()
115 .with_tls_support_using_rustls(Some(tls_config))
116 .with_default_http_connector(exec)
117 .build_client()
118 }
119
120 #[cfg(not(any(feature = "rustls", feature = "boring")))]
121 pub fn default_with_executor(exec: Executor) -> Self {
122 EasyHttpConnectorBuilder::new()
123 .with_default_transport_connector()
124 .without_tls_proxy_support()
125 .with_proxy_support()
126 .without_tls_support()
127 .with_default_http_connector(exec)
128 .build_client()
129 }
130}
131
132impl<BodyIn, ConnResponse> EasyHttpWebClient<BodyIn, ConnResponse, ()> {
133 #[must_use]
135 pub fn new(connector: BoxService<Request<BodyIn>, ConnResponse, BoxError>) -> Self {
136 Self {
137 connector,
138 jit_layers: (),
139 }
140 }
141}
142
143impl<BodyIn, ConnResponse, L> EasyHttpWebClient<BodyIn, ConnResponse, L> {
144 #[must_use]
146 pub fn with_connector<BodyInNew, ConnResponseNew>(
147 self,
148 connector: BoxService<Request<BodyInNew>, ConnResponseNew, BoxError>,
149 ) -> EasyHttpWebClient<BodyInNew, ConnResponseNew, L> {
150 EasyHttpWebClient {
151 connector,
152 jit_layers: self.jit_layers,
153 }
154 }
155
156 pub fn with_jit_layer<T>(self, jit_layers: T) -> EasyHttpWebClient<BodyIn, ConnResponse, T> {
164 EasyHttpWebClient {
165 connector: self.connector,
166 jit_layers,
167 }
168 }
169}
170
171impl<Body, ConnectionBody, Connection, L> Service<Request<Body>>
172 for EasyHttpWebClient<Body, EstablishedClientConnection<Connection, Request<ConnectionBody>>, L>
173where
174 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
175 Connection:
176 Service<Request<ConnectionBody>, Output = Response, Error = BoxError> + ExtensionsRef,
177 ConnectionBody:
180 StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
181 L: Layer<
182 Connection,
183 Service: Service<Request<ConnectionBody>, Output = Response, Error = BoxError>,
184 > + Send
185 + Sync
186 + 'static,
187{
188 type Output = Response;
189 type Error = OpaqueError;
190
191 async fn serve(&self, req: Request<Body>) -> Result<Self::Output, Self::Error> {
192 let uri = req.uri().clone();
193
194 let EstablishedClientConnection {
195 input: mut req,
196 conn: http_connection,
197 } = self.connector.serve(req).await?;
198
199 req.extensions_mut()
200 .extend(http_connection.extensions().clone());
201
202 let http_connection = self.jit_layers.layer(http_connection);
203
204 tracing::trace!(url.full = %uri, "send http req to connector stack");
206
207 let result = http_connection.serve(req).await;
208
209 let resp = result
210 .map_err(OpaqueError::from_boxed)
211 .with_context(|| format!("http request failure for uri: {uri}"))?;
212
213 tracing::trace!(url.full = %uri, "response received from connector stack");
214
215 Ok(resp)
216 }
217}