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