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