aws_smithy_runtime/client/http/
connection_poisoning.rs1use crate::client::retries::classifiers::run_classifiers_on_ctx;
7use aws_smithy_runtime_api::box_error::BoxError;
8use aws_smithy_runtime_api::client::interceptors::context::{
9 AfterDeserializationInterceptorContextRef, BeforeTransmitInterceptorContextMut,
10};
11use aws_smithy_runtime_api::client::interceptors::Intercept;
12use aws_smithy_runtime_api::client::retries::classifiers::RetryAction;
13use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
14use aws_smithy_types::config_bag::ConfigBag;
15use aws_smithy_types::retry::{ReconnectMode, RetryConfig};
16use tracing::{debug, error};
17
18pub use aws_smithy_runtime_api::client::connection::CaptureSmithyConnection;
20
21#[non_exhaustive]
36#[derive(Debug, Default)]
37pub struct ConnectionPoisoningInterceptor {}
38
39impl ConnectionPoisoningInterceptor {
40 pub fn new() -> Self {
42 Self::default()
43 }
44}
45
46impl Intercept for ConnectionPoisoningInterceptor {
47 fn name(&self) -> &'static str {
48 "ConnectionPoisoningInterceptor"
49 }
50
51 fn modify_before_transmit(
52 &self,
53 context: &mut BeforeTransmitInterceptorContextMut<'_>,
54 _runtime_components: &RuntimeComponents,
55 cfg: &mut ConfigBag,
56 ) -> Result<(), BoxError> {
57 let capture_smithy_connection = CaptureSmithyConnection::new();
58 context
59 .request_mut()
60 .add_extension(capture_smithy_connection.clone());
61 cfg.interceptor_state().store_put(capture_smithy_connection);
62
63 Ok(())
64 }
65
66 fn read_after_deserialization(
67 &self,
68 context: &AfterDeserializationInterceptorContextRef<'_>,
69 runtime_components: &RuntimeComponents,
70 cfg: &mut ConfigBag,
71 ) -> Result<(), BoxError> {
72 let reconnect_mode = cfg
73 .load::<RetryConfig>()
74 .map(RetryConfig::reconnect_mode)
75 .unwrap_or(ReconnectMode::ReconnectOnTransientError);
76 let captured_connection = cfg.load::<CaptureSmithyConnection>().cloned();
77 let retry_classifier_result =
78 run_classifiers_on_ctx(runtime_components.retry_classifiers(), context.inner());
79 let error_is_transient = retry_classifier_result == RetryAction::transient_error();
80 let connection_poisoning_is_enabled =
81 reconnect_mode == ReconnectMode::ReconnectOnTransientError;
82
83 if error_is_transient && connection_poisoning_is_enabled {
84 debug!("received a transient error, marking the connection for closure...");
85
86 if let Some(captured_connection) = captured_connection.and_then(|conn| conn.get()) {
87 captured_connection.poison();
88 debug!("the connection was marked for closure")
89 } else {
90 error!(
91 "unable to mark the connection for closure because no connection was found! The underlying HTTP connector never set a connection."
92 );
93 }
94 }
95
96 Ok(())
97 }
98}