aws_smithy_runtime_api/client/
http.rs1pub mod telemetry;
54
55use crate::box_error::BoxError;
56use crate::client::connector_metadata::ConnectorMetadata;
57use crate::client::orchestrator::{HttpRequest, HttpResponse};
58use crate::client::result::ConnectorError;
59use crate::client::runtime_components::sealed::ValidateConfig;
60use crate::client::runtime_components::{RuntimeComponents, RuntimeComponentsBuilder};
61use crate::impl_shared_conversions;
62use aws_smithy_types::config_bag::ConfigBag;
63use std::fmt;
64use std::sync::Arc;
65use std::time::Duration;
66
67new_type_future! {
68 #[doc = "Future for [`HttpConnector::call`]."]
69 pub struct HttpConnectorFuture<'static, HttpResponse, ConnectorError>;
70}
71
72pub trait HttpConnector: Send + Sync + fmt::Debug {
80 fn call(&self, request: HttpRequest) -> HttpConnectorFuture;
82}
83
84#[derive(Clone, Debug)]
86pub struct SharedHttpConnector(Arc<dyn HttpConnector>);
87
88impl SharedHttpConnector {
89 pub fn new(connection: impl HttpConnector + 'static) -> Self {
91 Self(Arc::new(connection))
92 }
93}
94
95impl HttpConnector for SharedHttpConnector {
96 fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
97 (*self.0).call(request)
98 }
99}
100
101impl_shared_conversions!(convert SharedHttpConnector from HttpConnector using SharedHttpConnector::new);
102
103pub fn http_client_fn<F>(connector: F) -> SharedHttpClient
105where
106 F: Fn(&HttpConnectorSettings, &RuntimeComponents) -> SharedHttpConnector
107 + Send
108 + Sync
109 + 'static,
110{
111 struct ConnectorFn<T>(T);
112 impl<T> fmt::Debug for ConnectorFn<T> {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 f.write_str("ConnectorFn")
115 }
116 }
117 impl<T> HttpClient for ConnectorFn<T>
118 where
119 T: (Fn(&HttpConnectorSettings, &RuntimeComponents) -> SharedHttpConnector) + Send + Sync,
120 {
121 fn http_connector(
122 &self,
123 settings: &HttpConnectorSettings,
124 components: &RuntimeComponents,
125 ) -> SharedHttpConnector {
126 (self.0)(settings, components)
127 }
128 }
129
130 SharedHttpClient::new(ConnectorFn(connector))
131}
132
133pub trait HttpClient: Send + Sync + fmt::Debug {
138 fn http_connector(
147 &self,
148 settings: &HttpConnectorSettings,
149 components: &RuntimeComponents,
150 ) -> SharedHttpConnector;
151
152 #[doc = include_str!("../../rustdoc/validate_base_client_config.md")]
153 fn validate_base_client_config(
154 &self,
155 runtime_components: &RuntimeComponentsBuilder,
156 cfg: &ConfigBag,
157 ) -> Result<(), BoxError> {
158 let _ = (runtime_components, cfg);
159 Ok(())
160 }
161
162 #[doc = include_str!("../../rustdoc/validate_final_config.md")]
163 fn validate_final_config(
164 &self,
165 runtime_components: &RuntimeComponents,
166 cfg: &ConfigBag,
167 ) -> Result<(), BoxError> {
168 let _ = (runtime_components, cfg);
169 Ok(())
170 }
171
172 fn connector_metadata(&self) -> Option<ConnectorMetadata> {
178 None
179 }
180}
181
182#[derive(Clone, Debug)]
184pub struct SharedHttpClient {
185 selector: Arc<dyn HttpClient>,
186}
187
188impl SharedHttpClient {
189 pub fn new(selector: impl HttpClient + 'static) -> Self {
191 Self {
192 selector: Arc::new(selector),
193 }
194 }
195}
196
197impl HttpClient for SharedHttpClient {
198 fn http_connector(
199 &self,
200 settings: &HttpConnectorSettings,
201 components: &RuntimeComponents,
202 ) -> SharedHttpConnector {
203 self.selector.http_connector(settings, components)
204 }
205
206 fn validate_base_client_config(
207 &self,
208 runtime_components: &RuntimeComponentsBuilder,
209 cfg: &ConfigBag,
210 ) -> Result<(), BoxError> {
211 self.selector
212 .validate_base_client_config(runtime_components, cfg)
213 }
214
215 fn validate_final_config(
216 &self,
217 runtime_components: &RuntimeComponents,
218 cfg: &ConfigBag,
219 ) -> Result<(), BoxError> {
220 self.selector.validate_final_config(runtime_components, cfg)
221 }
222
223 fn connector_metadata(&self) -> Option<ConnectorMetadata> {
224 self.selector.connector_metadata()
225 }
226}
227
228impl ValidateConfig for SharedHttpClient {
229 fn validate_base_client_config(
230 &self,
231 runtime_components: &RuntimeComponentsBuilder,
232 cfg: &ConfigBag,
233 ) -> Result<(), BoxError> {
234 HttpClient::validate_base_client_config(self, runtime_components, cfg)
235 }
236
237 fn validate_final_config(
238 &self,
239 runtime_components: &RuntimeComponents,
240 cfg: &ConfigBag,
241 ) -> Result<(), BoxError> {
242 HttpClient::validate_final_config(self, runtime_components, cfg)
243 }
244}
245
246impl_shared_conversions!(convert SharedHttpClient from HttpClient using SharedHttpClient::new);
247
248#[non_exhaustive]
250#[derive(Default, Debug)]
251pub struct HttpConnectorSettingsBuilder {
252 connect_timeout: Option<Duration>,
253 read_timeout: Option<Duration>,
254}
255
256impl HttpConnectorSettingsBuilder {
257 pub fn new() -> Self {
259 Default::default()
260 }
261
262 pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self {
266 self.connect_timeout = Some(connect_timeout);
267 self
268 }
269
270 pub fn set_connect_timeout(&mut self, connect_timeout: Option<Duration>) -> &mut Self {
274 self.connect_timeout = connect_timeout;
275 self
276 }
277
278 pub fn read_timeout(mut self, read_timeout: Duration) -> Self {
283 self.read_timeout = Some(read_timeout);
284 self
285 }
286
287 pub fn set_read_timeout(&mut self, read_timeout: Option<Duration>) -> &mut Self {
292 self.read_timeout = read_timeout;
293 self
294 }
295
296 pub fn build(self) -> HttpConnectorSettings {
298 HttpConnectorSettings {
299 connect_timeout: self.connect_timeout,
300 read_timeout: self.read_timeout,
301 }
302 }
303}
304
305#[non_exhaustive]
307#[derive(Clone, Default, Debug)]
308pub struct HttpConnectorSettings {
309 connect_timeout: Option<Duration>,
310 read_timeout: Option<Duration>,
311}
312
313impl HttpConnectorSettings {
314 pub fn builder() -> HttpConnectorSettingsBuilder {
316 Default::default()
317 }
318
319 pub fn connect_timeout(&self) -> Option<Duration> {
323 self.connect_timeout
324 }
325
326 pub fn read_timeout(&self) -> Option<Duration> {
331 self.read_timeout
332 }
333}
334
335#[cfg(all(test, feature = "test-util"))]
336mod tests {
337 use super::*;
338 use crate::client::runtime_components::RuntimeComponentsBuilder;
339 use aws_smithy_types::config_bag::ConfigBag;
340 use std::sync::atomic::{AtomicUsize, Ordering};
341
342 #[derive(Debug, Default)]
344 struct CountingSelector {
345 base: AtomicUsize,
346 final_: AtomicUsize,
347 }
348
349 impl HttpClient for CountingSelector {
350 fn http_connector(
351 &self,
352 _settings: &HttpConnectorSettings,
353 _components: &RuntimeComponents,
354 ) -> SharedHttpConnector {
355 unreachable!("http_connector is not exercised by these tests")
356 }
357
358 fn validate_base_client_config(
359 &self,
360 _runtime_components: &RuntimeComponentsBuilder,
361 _cfg: &ConfigBag,
362 ) -> Result<(), BoxError> {
363 self.base.fetch_add(1, Ordering::SeqCst);
364 Ok(())
365 }
366
367 fn validate_final_config(
368 &self,
369 _runtime_components: &RuntimeComponents,
370 _cfg: &ConfigBag,
371 ) -> Result<(), BoxError> {
372 self.final_.fetch_add(1, Ordering::SeqCst);
373 Ok(())
374 }
375 }
376
377 #[derive(Debug)]
382 struct DecoratingClient {
383 inner: SharedHttpClient,
384 }
385
386 impl HttpClient for DecoratingClient {
387 fn http_connector(
388 &self,
389 settings: &HttpConnectorSettings,
390 components: &RuntimeComponents,
391 ) -> SharedHttpConnector {
392 self.inner.http_connector(settings, components)
393 }
394
395 fn validate_base_client_config(
396 &self,
397 runtime_components: &RuntimeComponentsBuilder,
398 cfg: &ConfigBag,
399 ) -> Result<(), BoxError> {
400 HttpClient::validate_base_client_config(&self.inner, runtime_components, cfg)
401 }
402
403 fn validate_final_config(
404 &self,
405 runtime_components: &RuntimeComponents,
406 cfg: &ConfigBag,
407 ) -> Result<(), BoxError> {
408 HttpClient::validate_final_config(&self.inner, runtime_components, cfg)
409 }
410 }
411
412 #[test]
413 fn shared_http_client_forwards_validation_to_selector() {
414 let selector = Arc::new(CountingSelector::default());
415 let shared = SharedHttpClient {
416 selector: selector.clone(),
417 };
418 let cfg = ConfigBag::base();
419 let builder = RuntimeComponentsBuilder::for_tests();
420 let components = RuntimeComponentsBuilder::for_tests().build().unwrap();
421
422 HttpClient::validate_base_client_config(&shared, &builder, &cfg).unwrap();
424 HttpClient::validate_final_config(&shared, &components, &cfg).unwrap();
425
426 ValidateConfig::validate_base_client_config(&shared, &builder, &cfg).unwrap();
428 ValidateConfig::validate_final_config(&shared, &components, &cfg).unwrap();
429
430 assert_eq!(2, selector.base.load(Ordering::SeqCst));
434 assert_eq!(2, selector.final_.load(Ordering::SeqCst));
435 }
436
437 #[test]
438 fn external_decorator_reaches_selector_through_shared_client() {
439 let selector = Arc::new(CountingSelector::default());
440 let decorator = DecoratingClient {
441 inner: SharedHttpClient {
442 selector: selector.clone(),
443 },
444 };
445 let cfg = ConfigBag::base();
446 let builder = RuntimeComponentsBuilder::for_tests();
447 let components = RuntimeComponentsBuilder::for_tests().build().unwrap();
448
449 decorator
450 .validate_base_client_config(&builder, &cfg)
451 .unwrap();
452 decorator.validate_final_config(&components, &cfg).unwrap();
453
454 assert_eq!(1, selector.base.load(Ordering::SeqCst));
455 assert_eq!(1, selector.final_.load(Ordering::SeqCst));
456 }
457}