Skip to main content

aws_smithy_runtime_api/client/
interceptors.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Interceptors for clients.
7//!
8//! Interceptors are operation lifecycle hooks that can read/modify requests and responses.
9
10use crate::box_error::BoxError;
11use crate::client::interceptors::context::{
12    AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut,
13    BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut,
14    BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut,
15    BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut,
16    FinalizerInterceptorContextRef,
17};
18use crate::client::runtime_components::sealed::ValidateConfig;
19use crate::client::runtime_components::RuntimeComponents;
20use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
21use std::fmt;
22use std::marker::PhantomData;
23use std::sync::Arc;
24
25pub mod context;
26pub mod error;
27
28use crate::impl_shared_conversions;
29pub use error::InterceptorError;
30
31macro_rules! interceptor_trait_fn {
32    ($name:ident, $phase:ident, $docs:tt) => {
33        #[doc = $docs]
34        fn $name(
35            &self,
36            context: &$phase<'_>,
37            runtime_components: &RuntimeComponents,
38            cfg: &mut ConfigBag,
39        ) -> Result<(), BoxError> {
40            let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
41            Ok(())
42        }
43    };
44    (mut $name:ident, $phase:ident, $docs:tt) => {
45        #[doc = $docs]
46        fn $name(
47            &self,
48            context: &mut $phase<'_>,
49            runtime_components: &RuntimeComponents,
50            cfg: &mut ConfigBag,
51        ) -> Result<(), BoxError> {
52            let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
53            Ok(())
54        }
55    };
56}
57
58/// An interceptor allows injecting code into the SDK ’s request execution pipeline.
59///
60/// ## Terminology:
61/// - An execution is one end-to-end invocation against an SDK client.
62/// - An attempt is an attempt at performing an execution. By default executions are retried multiple
63///   times based on the client ’s retry strategy.
64/// - A hook is a single method on the interceptor, allowing injection of code into a specific part
65///   of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible
66///   to read in-flight request or response messages, or "read/write" hooks, which make it possible
67///   to modify in-flight request or output messages.
68// If you add hook methods, also update:
69//   - `KNOWN_HOOKS` in `aws-smithy-runtime-api-macros/src/lib.rs`
70//   - `OverriddenHooks` constants (below in this file)
71pub trait Intercept: fmt::Debug + Send + Sync {
72    /// The name of this interceptor, used in error messages for debugging.
73    fn name(&self) -> &'static str;
74
75    /// A hook called at the start of an execution, before the SDK
76    /// does anything else.
77    ///
78    /// **When:** This will **ALWAYS** be called once per execution. The duration
79    /// between invocation of this hook and `after_execution` is very close
80    /// to full duration of the execution.
81    ///
82    /// **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input)
83    /// is **ALWAYS** available. Other information **WILL NOT** be available.
84    ///
85    /// **Error Behavior:** Errors raised by this hook will be stored
86    /// until all interceptors have had their `before_execution` invoked.
87    /// Other hooks will then be skipped and execution will jump to
88    /// `modify_before_completion` with the raised error as the
89    /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple
90    /// `before_execution` methods raise errors, the latest
91    /// will be used and earlier ones will be logged and dropped.
92    fn read_before_execution(
93        &self,
94        context: &BeforeSerializationInterceptorContextRef<'_>,
95        cfg: &mut ConfigBag,
96    ) -> Result<(), BoxError> {
97        let (_ctx, _cfg) = (context, cfg);
98        Ok(())
99    }
100
101    interceptor_trait_fn!(
102        mut modify_before_serialization,
103        BeforeSerializationInterceptorContextMut,
104        "
105        A hook called before the input message is marshalled into a
106        transport message.
107        This method has the ability to modify and return a new
108        request message of the same type.
109
110        **When:** This will **ALWAYS** be called once per execution, except when a
111        failure occurs earlier in the request pipeline.
112
113        **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is
114        **ALWAYS** available. This request may have been modified by earlier
115        `modify_before_serialization` hooks, and may be modified further by
116        later hooks. Other information **WILL NOT** be available.
117
118        **Error Behavior:** If errors are raised by this hook,
119        execution will jump to `modify_before_completion` with the raised
120        error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
121
122        **Return Constraints:** The input message returned by this hook
123        MUST be the same type of input message passed into this hook.
124        If not, an error will immediately be raised.
125        "
126    );
127
128    interceptor_trait_fn!(
129        read_before_serialization,
130        BeforeSerializationInterceptorContextRef,
131        "
132        A hook called before the input message is marshalled
133        into a transport
134        message.
135
136        **When:** This will **ALWAYS** be called once per execution, except when a
137        failure occurs earlier in the request pipeline. The
138        duration between invocation of this hook and `after_serialization` is
139        very close to the amount of time spent marshalling the request.
140
141        **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is
142        **ALWAYS** available. Other information **WILL NOT** be available.
143
144        **Error Behavior:** If errors are raised by this hook,
145        execution will jump to `modify_before_completion` with the raised
146        error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
147        "
148    );
149
150    interceptor_trait_fn!(
151        read_after_serialization,
152        BeforeTransmitInterceptorContextRef,
153        "
154        A hook called after the input message is marshalled into
155        a transport message.
156
157        **When:** This will **ALWAYS** be called once per execution, except when a
158        failure occurs earlier in the request pipeline. The duration
159        between invocation of this hook and `before_serialization` is very
160        close to the amount of time spent marshalling the request.
161
162        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
163        is **ALWAYS** available. Other information **WILL NOT** be available.
164
165        **Error Behavior:** If errors are raised by this hook,
166        execution will jump to `modify_before_completion` with the raised
167        error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
168        "
169    );
170
171    interceptor_trait_fn!(
172        mut modify_before_retry_loop,
173        BeforeTransmitInterceptorContextMut,
174        "
175        A hook called before the retry loop is entered. This method
176        has the ability to modify and return a new transport request
177        message of the same type, except when a failure occurs earlier in the request pipeline.
178
179        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
180        is **ALWAYS** available. Other information **WILL NOT** be available.
181
182        **Error Behavior:** If errors are raised by this hook,
183        execution will jump to `modify_before_completion` with the raised
184        error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
185
186        **Return Constraints:** The transport request message returned by this
187        hook MUST be the same type of request message passed into this hook
188        If not, an error will immediately be raised.
189        "
190    );
191
192    interceptor_trait_fn!(
193        read_before_attempt,
194        BeforeTransmitInterceptorContextRef,
195        "
196        A hook called before each attempt at sending the transmission
197        request message to the service.
198
199        **When:** This will **ALWAYS** be called once per attempt, except when a
200        failure occurs earlier in the request pipeline. This method will be
201        called multiple times in the event of retries.
202
203        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
204        is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
205        the `InterceptorContext` will not include changes made in previous
206        attempts (e.g. by request signers or other interceptors).
207
208        **Error Behavior:** Errors raised by this hook will be stored
209        until all interceptors have had their `before_attempt` invoked.
210        Other hooks will then be skipped and execution will jump to
211        `modify_before_attempt_completion` with the raised error as the
212        [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple
213        `before_attempt` methods raise errors, the latest will be used
214        and earlier ones will be logged and dropped.
215        "
216    );
217
218    interceptor_trait_fn!(
219        mut modify_before_signing,
220        BeforeTransmitInterceptorContextMut,
221        "
222        A hook called before the transport request message is signed.
223        This method has the ability to modify and return a new transport
224        request message of the same type.
225
226        **When:** This will **ALWAYS** be called once per attempt, except when a
227        failure occurs earlier in the request pipeline. This method may be
228        called multiple times in the event of retries.
229
230        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
231        is **ALWAYS** available. The `http::Request` may have been modified by earlier
232        `modify_before_signing` hooks, and may be modified further by later
233        hooks. Other information **WILL NOT** be available. In the event of
234        retries, the `InterceptorContext` will not include changes made
235        in previous attempts (e.g. by request signers or other interceptors).
236
237        **Error Behavior:** If errors are raised by this
238        hook, execution will jump to `modify_before_attempt_completion` with
239        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
240
241        **Return Constraints:** The transport request message returned by this
242        hook MUST be the same type of request message passed into this hook
243
244        If not, an error will immediately be raised.
245        "
246    );
247
248    interceptor_trait_fn!(
249        read_before_signing,
250        BeforeTransmitInterceptorContextRef,
251        "
252        A hook called before the transport request message is signed.
253
254        **When:** This will **ALWAYS** be called once per attempt, except when a
255        failure occurs earlier in the request pipeline. This method may be
256        called multiple times in the event of retries. The duration between
257        invocation of this hook and `after_signing` is very close to
258        the amount of time spent signing the request.
259
260        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available.
261        Other information **WILL NOT** be available. In the event of retries,
262        the `InterceptorContext` will not include changes made in previous
263        attempts (e.g. by request signers or other interceptors).
264
265        **Error Behavior:** If errors are raised by this
266        hook, execution will jump to `modify_before_attempt_completion` with
267        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
268        "
269    );
270
271    interceptor_trait_fn!(
272        read_after_signing,
273        BeforeTransmitInterceptorContextRef,
274        "
275        A hook called after the transport request message is signed.
276
277        **When:** This will **ALWAYS** be called once per attempt, except when a
278        failure occurs earlier in the request pipeline. This method may be
279        called multiple times in the event of retries. The duration between
280        invocation of this hook and `before_signing` is very close to
281        the amount of time spent signing the request.
282
283        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available.
284        Other information **WILL NOT** be available. In the event of retries,
285        the `InterceptorContext` will not include changes made in previous
286        attempts (e.g. by request signers or other interceptors).
287
288        **Error Behavior:** If errors are raised by this
289        hook, execution will jump to `modify_before_attempt_completion` with
290        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
291        "
292    );
293
294    interceptor_trait_fn!(
295        mut modify_before_transmit,
296        BeforeTransmitInterceptorContextMut,
297        "
298        A hook called before the transport request message is sent to the
299        service. This method has the ability to modify and return
300        a new transport request message of the same type.
301
302        **When:** This will **ALWAYS** be called once per attempt, except when a
303        failure occurs earlier in the request pipeline. This method may be
304        called multiple times in the event of retries.
305
306        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
307        is **ALWAYS** available. The `http::Request` may have been modified by earlier
308        `modify_before_transmit` hooks, and may be modified further by later
309        hooks. Other information **WILL NOT** be available.
310        In the event of retries, the `InterceptorContext` will not include
311        changes made in previous attempts (e.g. by request signers or
312        other interceptors).
313
314        **Error Behavior:** If errors are raised by this
315        hook, execution will jump to `modify_before_attempt_completion` with
316        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
317
318        **Return Constraints:** The transport request message returned by this
319        hook MUST be the same type of request message passed into this hook
320
321        If not, an error will immediately be raised.
322        "
323    );
324
325    interceptor_trait_fn!(
326        read_before_transmit,
327        BeforeTransmitInterceptorContextRef,
328        "
329        A hook called before the transport request message is sent to the
330        service.
331
332        **When:** This will **ALWAYS** be called once per attempt, except when a
333        failure occurs earlier in the request pipeline. This method may be
334        called multiple times in the event of retries. The duration between
335        invocation of this hook and `after_transmit` is very close to
336        the amount of time spent communicating with the service.
337        Depending on the protocol, the duration may not include the
338        time spent reading the response data.
339
340        **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
341        is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
342        the `InterceptorContext` will not include changes made in previous
343        attempts (e.g. by request signers or other interceptors).
344
345
346        **Error Behavior:** If errors are raised by this
347        hook, execution will jump to `modify_before_attempt_completion` with
348        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
349        "
350    );
351
352    interceptor_trait_fn!(
353        read_after_transmit,
354        BeforeDeserializationInterceptorContextRef,
355        "
356        A hook called after the transport request message is sent to the
357        service and a transport response message is received.
358
359        **When:** This will **ALWAYS** be called once per attempt, except when a
360        failure occurs earlier in the request pipeline. This method may be
361        called multiple times in the event of retries. The duration between
362        invocation of this hook and `before_transmit` is very close to
363        the amount of time spent communicating with the service.
364        Depending on the protocol, the duration may not include the time
365        spent reading the response data.
366
367        **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
368        is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
369        the `InterceptorContext` will not include changes made in previous
370        attempts (e.g. by request signers or other interceptors).
371
372        **Error Behavior:** If errors are raised by this
373        hook, execution will jump to `modify_before_attempt_completion` with
374        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
375        "
376    );
377
378    interceptor_trait_fn!(
379        mut modify_before_deserialization,
380        BeforeDeserializationInterceptorContextMut,
381        "
382        A hook called before the transport response message is unmarshalled.
383        This method has the ability to modify and return a new transport
384        response message of the same type.
385
386        **When:** This will **ALWAYS** be called once per attempt, except when a
387        failure occurs earlier in the request pipeline. This method may be
388        called multiple times in the event of retries.
389
390        **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
391        is **ALWAYS** available. The transmit_response may have been modified by earlier
392        `modify_before_deserialization` hooks, and may be modified further by
393        later hooks. Other information **WILL NOT** be available. In the event of
394        retries, the `InterceptorContext` will not include changes made in
395        previous attempts (e.g. by request signers or other interceptors).
396
397        **Error Behavior:** If errors are raised by this
398        hook, execution will jump to `modify_before_attempt_completion` with
399        the raised error as the
400        [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
401
402        **Return Constraints:** The transport response message returned by this
403        hook MUST be the same type of response message passed into
404        this hook. If not, an error will immediately be raised.
405        "
406    );
407
408    interceptor_trait_fn!(
409        read_before_deserialization,
410        BeforeDeserializationInterceptorContextRef,
411        "
412        A hook called before the transport response message is unmarshalled
413
414        **When:** This will **ALWAYS** be called once per attempt, except when a
415        failure occurs earlier in the request pipeline. This method may be
416        called multiple times in the event of retries. The duration between
417        invocation of this hook and `after_deserialization` is very close
418        to the amount of time spent unmarshalling the service response.
419        Depending on the protocol and operation, the duration may include
420        the time spent downloading the response data.
421
422        **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
423        is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
424        the `InterceptorContext` will not include changes made in previous
425        attempts (e.g. by request signers or other interceptors).
426
427        **Error Behavior:** If errors are raised by this
428        hook, execution will jump to `modify_before_attempt_completion`
429        with the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
430        "
431    );
432
433    interceptor_trait_fn!(
434        read_after_deserialization,
435        AfterDeserializationInterceptorContextRef,
436        "
437        A hook called after the transport response message is unmarshalled.
438
439        **When:** This will **ALWAYS** be called once per attempt, except when a
440        failure occurs earlier in the request pipeline. The duration
441        between invocation of this hook and `before_deserialization` is
442        very close to the amount of time spent unmarshalling the
443        service response. Depending on the protocol and operation,
444        the duration may include the time spent downloading
445        the response data.
446
447        **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
448        and [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error)
449        are **ALWAYS** available. In the event of retries, the `InterceptorContext` will not include changes made
450        in previous attempts (e.g. by request signers or other interceptors).
451
452        **Error Behavior:** If errors are raised by this
453        hook, execution will jump to `modify_before_attempt_completion` with
454        the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
455        "
456    );
457
458    /// A hook called when an attempt is completed. This method has the
459    /// ability to modify and return a new output message or error
460    /// matching the currently-executing operation.
461    ///
462    /// **When:** This will **ALWAYS** be called once per attempt, except when a
463    /// failure occurs before `before_attempt`. This method may
464    /// be called multiple times in the event of retries.
465    ///
466    /// **Available Information:**
467    /// The [`InterceptorContext::input`](context::InterceptorContext::input),
468    /// [`InterceptorContext::request`](context::InterceptorContext::request),
469    /// [`InterceptorContext::response`](context::InterceptorContext::response), or
470    /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
471    /// If the operation succeeded, the `output` will be available. Otherwise, any of the other
472    /// pieces of information may be available depending on where in the operation lifecycle it failed.
473    /// In the event of retries, the `InterceptorContext` will not include changes made
474    /// in previous attempts (e.g. by request signers or other interceptors).
475    ///
476    /// **Error Behavior:** If errors are raised by this
477    /// hook, execution will jump to `after_attempt` with
478    /// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
479    ///
480    /// **Return Constraints:** Any output message returned by this
481    /// hook MUST match the operation being invoked. Any error type can be
482    /// returned, replacing the response currently in the context.
483    fn modify_before_attempt_completion(
484        &self,
485        context: &mut FinalizerInterceptorContextMut<'_>,
486        runtime_components: &RuntimeComponents,
487        cfg: &mut ConfigBag,
488    ) -> Result<(), BoxError> {
489        let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
490        Ok(())
491    }
492
493    /// A hook called when an attempt is completed.
494    ///
495    /// **When:** This will **ALWAYS** be called once per attempt, as long as
496    /// `before_attempt` has been executed.
497    ///
498    /// **Available Information:**
499    /// The [`InterceptorContext::input`](context::InterceptorContext::input),
500    /// [`InterceptorContext::request`](context::InterceptorContext::request),
501    /// [`InterceptorContext::response`](context::InterceptorContext::response), or
502    /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
503    /// If the operation succeeded, the `output` will be available. Otherwise, any of the other
504    /// pieces of information may be available depending on where in the operation lifecycle it failed.
505    /// In the event of retries, the `InterceptorContext` will not include changes made
506    /// in previous attempts (e.g. by request signers or other interceptors).
507    ///
508    /// **Error Behavior:** Errors raised by this hook will be stored
509    /// until all interceptors have had their `after_attempt` invoked.
510    /// If multiple `after_execution` methods raise errors, the latest
511    /// will be used and earlier ones will be logged and dropped. If the
512    /// retry strategy determines that the execution is retryable,
513    /// execution will then jump to `before_attempt`. Otherwise,
514    /// execution will jump to `modify_before_attempt_completion` with the
515    /// raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
516    fn read_after_attempt(
517        &self,
518        context: &FinalizerInterceptorContextRef<'_>,
519        runtime_components: &RuntimeComponents,
520        cfg: &mut ConfigBag,
521    ) -> Result<(), BoxError> {
522        let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
523        Ok(())
524    }
525
526    /// A hook called when an execution is completed.
527    /// This method has the ability to modify and return a new
528    /// output message or error matching the currently - executing
529    /// operation.
530    ///
531    /// **When:** This will **ALWAYS** be called once per execution.
532    ///
533    /// **Available Information:**
534    /// The [`InterceptorContext::input`](context::InterceptorContext::input),
535    /// [`InterceptorContext::request`](context::InterceptorContext::request),
536    /// [`InterceptorContext::response`](context::InterceptorContext::response), or
537    /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
538    /// If the operation succeeded, the `output` will be available. Otherwise, any of the other
539    /// pieces of information may be available depending on where in the operation lifecycle it failed.
540    /// In the event of retries, the `InterceptorContext` will not include changes made
541    /// in previous attempts (e.g. by request signers or other interceptors).
542    ///
543    /// **Error Behavior:** If errors are raised by this
544    /// hook , execution will jump to `after_attempt` with
545    /// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
546    ///
547    /// **Return Constraints:** Any output message returned by this
548    /// hook MUST match the operation being invoked. Any error type can be
549    /// returned , replacing the response currently in the context.
550    fn modify_before_completion(
551        &self,
552        context: &mut FinalizerInterceptorContextMut<'_>,
553        runtime_components: &RuntimeComponents,
554        cfg: &mut ConfigBag,
555    ) -> Result<(), BoxError> {
556        let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
557        Ok(())
558    }
559
560    /// A hook called when an execution is completed.
561    ///
562    /// **When:** This will **ALWAYS** be called once per execution. The duration
563    /// between invocation of this hook and `before_execution` is very
564    /// close to the full duration of the execution.
565    ///
566    /// **Available Information:**
567    /// The [`InterceptorContext::input`](context::InterceptorContext::input),
568    /// [`InterceptorContext::request`](context::InterceptorContext::request),
569    /// [`InterceptorContext::response`](context::InterceptorContext::response), or
570    /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
571    /// If the operation succeeded, the `output` will be available. Otherwise, any of the other
572    /// pieces of information may be available depending on where in the operation lifecycle it failed.
573    /// In the event of retries, the `InterceptorContext` will not include changes made
574    /// in previous attempts (e.g. by request signers or other interceptors).
575    ///
576    /// **Error Behavior:** Errors raised by this hook will be stored
577    /// until all interceptors have had their `after_execution` invoked.
578    /// The error will then be treated as the
579    /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error)
580    /// to the customer. If multiple `after_execution` methods raise errors , the latest will be
581    /// used and earlier ones will be logged and dropped.
582    fn read_after_execution(
583        &self,
584        context: &FinalizerInterceptorContextRef<'_>,
585        runtime_components: &RuntimeComponents,
586        cfg: &mut ConfigBag,
587    ) -> Result<(), BoxError> {
588        let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
589        Ok(())
590    }
591
592    /// Returns a bitmask of which hooks this interceptor overrides.
593    ///
594    /// The default returns [`OverriddenHooks::all()`], meaning all hooks are
595    /// called (safe, same behavior as before overridden hooks existed). Use
596    /// [`#[dyn_dispatch_hint]`](dyn_dispatch_hint) on your `impl Intercept` block to
597    /// auto-generate this method from the overridden hooks.
598    #[doc(hidden)]
599    fn overridden_hooks(&self) -> OverriddenHooks {
600        OverriddenHooks::all()
601    }
602}
603
604/// Bitmask indicating which interceptor hooks a [`SharedInterceptor`] actually overrides.
605///
606/// When returned from [`Intercept::overridden_hooks`], the interceptor loop can skip
607/// dyn dispatch for hooks that are not in the mask.
608#[doc(hidden)]
609#[derive(Clone, Copy, Debug, PartialEq, Eq)]
610pub struct OverriddenHooks(u32);
611
612impl OverriddenHooks {
613    /// All hooks — the safe default.
614    pub const fn all() -> Self {
615        Self(u32::MAX)
616    }
617    /// No hooks.
618    pub const fn none() -> Self {
619        Self(0)
620    }
621
622    // If you update these constants, also update:
623    //   - `KNOWN_HOOKS` in `aws-smithy-runtime-api-macros/src/lib.rs`
624    //   - Hook methods on the `Intercept` trait (above in this file)
625    /// Hint for [`Intercept::read_before_execution`].
626    pub const READ_BEFORE_EXECUTION: Self = Self(1 << 0);
627    /// Hint for [`Intercept::modify_before_serialization`].
628    pub const MODIFY_BEFORE_SERIALIZATION: Self = Self(1 << 1);
629    /// Hint for [`Intercept::read_before_serialization`].
630    pub const READ_BEFORE_SERIALIZATION: Self = Self(1 << 2);
631    /// Hint for [`Intercept::read_after_serialization`].
632    pub const READ_AFTER_SERIALIZATION: Self = Self(1 << 3);
633    /// Hint for [`Intercept::modify_before_retry_loop`].
634    pub const MODIFY_BEFORE_RETRY_LOOP: Self = Self(1 << 4);
635    /// Hint for [`Intercept::read_before_attempt`].
636    pub const READ_BEFORE_ATTEMPT: Self = Self(1 << 5);
637    /// Hint for [`Intercept::modify_before_signing`].
638    pub const MODIFY_BEFORE_SIGNING: Self = Self(1 << 6);
639    /// Hint for [`Intercept::read_before_signing`].
640    pub const READ_BEFORE_SIGNING: Self = Self(1 << 7);
641    /// Hint for [`Intercept::read_after_signing`].
642    pub const READ_AFTER_SIGNING: Self = Self(1 << 8);
643    /// Hint for [`Intercept::modify_before_transmit`].
644    pub const MODIFY_BEFORE_TRANSMIT: Self = Self(1 << 9);
645    /// Hint for [`Intercept::read_before_transmit`].
646    pub const READ_BEFORE_TRANSMIT: Self = Self(1 << 10);
647    /// Hint for [`Intercept::read_after_transmit`].
648    pub const READ_AFTER_TRANSMIT: Self = Self(1 << 11);
649    /// Hint for [`Intercept::modify_before_deserialization`].
650    pub const MODIFY_BEFORE_DESERIALIZATION: Self = Self(1 << 12);
651    /// Hint for [`Intercept::read_before_deserialization`].
652    pub const READ_BEFORE_DESERIALIZATION: Self = Self(1 << 13);
653    /// Hint for [`Intercept::read_after_deserialization`].
654    pub const READ_AFTER_DESERIALIZATION: Self = Self(1 << 14);
655    /// Hint for [`Intercept::modify_before_attempt_completion`].
656    pub const MODIFY_BEFORE_ATTEMPT_COMPLETION: Self = Self(1 << 15);
657    /// Hint for [`Intercept::read_after_attempt`].
658    pub const READ_AFTER_ATTEMPT: Self = Self(1 << 16);
659    /// Hint for [`Intercept::modify_before_completion`].
660    pub const MODIFY_BEFORE_COMPLETION: Self = Self(1 << 17);
661    /// Hint for [`Intercept::read_after_execution`].
662    pub const READ_AFTER_EXECUTION: Self = Self(1 << 18);
663
664    /// Returns `true` if `self` contains any of the hooks in `other`.
665    pub const fn contains(self, other: Self) -> bool {
666        self.0 & other.0 != 0
667    }
668}
669
670impl std::ops::BitOr for OverriddenHooks {
671    type Output = Self;
672    fn bitor(self, rhs: Self) -> Self {
673        Self(self.0 | rhs.0)
674    }
675}
676
677/// Re-export the proc macro for deriving [`Intercept::overridden_hooks`] automatically.
678#[doc(hidden)]
679pub use aws_smithy_runtime_api_macros::dyn_dispatch_hint;
680
681/// Expands to the canonical list of interceptor hook names, in lifecycle order.
682///
683/// Kept in sync with the `Intercept` trait and the [`OverriddenHooks`] constants; see the comment on
684/// those constants. Intended for tests that must cover every hook.
685#[doc(hidden)]
686pub use aws_smithy_runtime_api_macros::known_hooks;
687
688/// Interceptor wrapper that may be shared
689#[derive(Clone)]
690pub struct SharedInterceptor {
691    interceptor: Arc<dyn Intercept>,
692    /// When `None`, the interceptor is always enabled (permanent mode).
693    #[allow(clippy::type_complexity)]
694    check_enabled: Option<Arc<dyn Fn(&ConfigBag) -> bool + Send + Sync>>,
695    /// Cached bitmask of which [`Intercept`] hooks this interceptor overrides.
696    overridden_hooks: OverriddenHooks,
697    /// In debug builds, asserts that nobody tried to disable a permanent interceptor.
698    #[cfg(debug_assertions)]
699    debug_assert_not_disabled: Option<fn(&ConfigBag) -> bool>,
700}
701
702impl fmt::Debug for SharedInterceptor {
703    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704        f.debug_struct("SharedInterceptor")
705            .field("interceptor", &self.interceptor)
706            .field("permanent", &self.check_enabled.is_none())
707            .finish()
708    }
709}
710
711impl SharedInterceptor {
712    /// Create a new `SharedInterceptor` from `Interceptor`.
713    pub fn new<T: Intercept + 'static>(interceptor: T) -> Self {
714        Self {
715            overridden_hooks: interceptor.overridden_hooks(),
716            interceptor: Arc::new(interceptor),
717            check_enabled: Some(Arc::new(|conf: &ConfigBag| {
718                conf.load::<DisableInterceptor<T>>().is_none()
719            })),
720            #[cfg(debug_assertions)]
721            debug_assert_not_disabled: None,
722        }
723    }
724
725    /// Creates a `SharedInterceptor` that is always enabled.
726    ///
727    /// Unlike [`SharedInterceptor::new`], this skips the per-invocation
728    /// [`DisableInterceptor`] lookup in the config bag.
729    ///
730    /// Note: In debug builds, if [`disable_interceptor`] is called for an
731    /// interceptor wrapped with `permanent`, a panic will occur to flag the
732    /// misconfiguration. Use [`SharedInterceptor::new`] instead if the
733    /// interceptor needs to be disabled.
734    pub fn permanent<T: Intercept + 'static>(interceptor: T) -> Self {
735        Self {
736            overridden_hooks: interceptor.overridden_hooks(),
737            interceptor: Arc::new(interceptor),
738            check_enabled: None,
739            #[cfg(debug_assertions)]
740            debug_assert_not_disabled: Some(|conf: &ConfigBag| {
741                conf.load::<DisableInterceptor<T>>().is_none()
742            }),
743        }
744    }
745
746    /// Checks if this interceptor is enabled in the given config.
747    pub fn enabled(&self, conf: &ConfigBag) -> bool {
748        match &self.check_enabled {
749            Some(check) => check(conf),
750            None => {
751                #[cfg(debug_assertions)]
752                if let Some(check) = &self.debug_assert_not_disabled {
753                    debug_assert!(
754                        check(conf),
755                        "attempted to disable permanent interceptor `{}`; \
756                         use SharedInterceptor::new() instead of ::permanent() \
757                         if this interceptor needs to be disabled",
758                        self.interceptor.name()
759                    );
760                }
761                true
762            }
763        }
764    }
765
766    /// Returns the overridden hooks.
767    pub fn overridden_hooks(&self) -> OverriddenHooks {
768        self.overridden_hooks
769    }
770}
771
772impl ValidateConfig for SharedInterceptor {}
773
774impl Intercept for SharedInterceptor {
775    fn name(&self) -> &'static str {
776        self.interceptor.name()
777    }
778
779    fn modify_before_attempt_completion(
780        &self,
781        context: &mut FinalizerInterceptorContextMut<'_>,
782        runtime_components: &RuntimeComponents,
783        cfg: &mut ConfigBag,
784    ) -> Result<(), BoxError> {
785        self.interceptor
786            .modify_before_attempt_completion(context, runtime_components, cfg)
787    }
788
789    fn modify_before_completion(
790        &self,
791        context: &mut FinalizerInterceptorContextMut<'_>,
792        runtime_components: &RuntimeComponents,
793        cfg: &mut ConfigBag,
794    ) -> Result<(), BoxError> {
795        self.interceptor
796            .modify_before_completion(context, runtime_components, cfg)
797    }
798
799    fn modify_before_deserialization(
800        &self,
801        context: &mut BeforeDeserializationInterceptorContextMut<'_>,
802        runtime_components: &RuntimeComponents,
803        cfg: &mut ConfigBag,
804    ) -> Result<(), BoxError> {
805        self.interceptor
806            .modify_before_deserialization(context, runtime_components, cfg)
807    }
808
809    fn modify_before_retry_loop(
810        &self,
811        context: &mut BeforeTransmitInterceptorContextMut<'_>,
812        runtime_components: &RuntimeComponents,
813        cfg: &mut ConfigBag,
814    ) -> Result<(), BoxError> {
815        self.interceptor
816            .modify_before_retry_loop(context, runtime_components, cfg)
817    }
818
819    fn modify_before_serialization(
820        &self,
821        context: &mut BeforeSerializationInterceptorContextMut<'_>,
822        runtime_components: &RuntimeComponents,
823        cfg: &mut ConfigBag,
824    ) -> Result<(), BoxError> {
825        self.interceptor
826            .modify_before_serialization(context, runtime_components, cfg)
827    }
828
829    fn modify_before_signing(
830        &self,
831        context: &mut BeforeTransmitInterceptorContextMut<'_>,
832        runtime_components: &RuntimeComponents,
833        cfg: &mut ConfigBag,
834    ) -> Result<(), BoxError> {
835        self.interceptor
836            .modify_before_signing(context, runtime_components, cfg)
837    }
838
839    fn modify_before_transmit(
840        &self,
841        context: &mut BeforeTransmitInterceptorContextMut<'_>,
842        runtime_components: &RuntimeComponents,
843        cfg: &mut ConfigBag,
844    ) -> Result<(), BoxError> {
845        self.interceptor
846            .modify_before_transmit(context, runtime_components, cfg)
847    }
848
849    fn read_after_attempt(
850        &self,
851        context: &FinalizerInterceptorContextRef<'_>,
852        runtime_components: &RuntimeComponents,
853        cfg: &mut ConfigBag,
854    ) -> Result<(), BoxError> {
855        self.interceptor
856            .read_after_attempt(context, runtime_components, cfg)
857    }
858
859    fn read_after_deserialization(
860        &self,
861        context: &AfterDeserializationInterceptorContextRef<'_>,
862        runtime_components: &RuntimeComponents,
863        cfg: &mut ConfigBag,
864    ) -> Result<(), BoxError> {
865        self.interceptor
866            .read_after_deserialization(context, runtime_components, cfg)
867    }
868
869    fn read_after_execution(
870        &self,
871        context: &FinalizerInterceptorContextRef<'_>,
872        runtime_components: &RuntimeComponents,
873        cfg: &mut ConfigBag,
874    ) -> Result<(), BoxError> {
875        self.interceptor
876            .read_after_execution(context, runtime_components, cfg)
877    }
878
879    fn read_after_serialization(
880        &self,
881        context: &BeforeTransmitInterceptorContextRef<'_>,
882        runtime_components: &RuntimeComponents,
883        cfg: &mut ConfigBag,
884    ) -> Result<(), BoxError> {
885        self.interceptor
886            .read_after_serialization(context, runtime_components, cfg)
887    }
888
889    fn read_after_signing(
890        &self,
891        context: &BeforeTransmitInterceptorContextRef<'_>,
892        runtime_components: &RuntimeComponents,
893        cfg: &mut ConfigBag,
894    ) -> Result<(), BoxError> {
895        self.interceptor
896            .read_after_signing(context, runtime_components, cfg)
897    }
898
899    fn read_after_transmit(
900        &self,
901        context: &BeforeDeserializationInterceptorContextRef<'_>,
902        runtime_components: &RuntimeComponents,
903        cfg: &mut ConfigBag,
904    ) -> Result<(), BoxError> {
905        self.interceptor
906            .read_after_transmit(context, runtime_components, cfg)
907    }
908
909    fn read_before_attempt(
910        &self,
911        context: &BeforeTransmitInterceptorContextRef<'_>,
912        runtime_components: &RuntimeComponents,
913        cfg: &mut ConfigBag,
914    ) -> Result<(), BoxError> {
915        self.interceptor
916            .read_before_attempt(context, runtime_components, cfg)
917    }
918
919    fn read_before_deserialization(
920        &self,
921        context: &BeforeDeserializationInterceptorContextRef<'_>,
922        runtime_components: &RuntimeComponents,
923        cfg: &mut ConfigBag,
924    ) -> Result<(), BoxError> {
925        self.interceptor
926            .read_before_deserialization(context, runtime_components, cfg)
927    }
928
929    fn read_before_execution(
930        &self,
931        context: &BeforeSerializationInterceptorContextRef<'_>,
932        cfg: &mut ConfigBag,
933    ) -> Result<(), BoxError> {
934        self.interceptor.read_before_execution(context, cfg)
935    }
936
937    fn read_before_serialization(
938        &self,
939        context: &BeforeSerializationInterceptorContextRef<'_>,
940        runtime_components: &RuntimeComponents,
941        cfg: &mut ConfigBag,
942    ) -> Result<(), BoxError> {
943        self.interceptor
944            .read_before_serialization(context, runtime_components, cfg)
945    }
946
947    fn read_before_signing(
948        &self,
949        context: &BeforeTransmitInterceptorContextRef<'_>,
950        runtime_components: &RuntimeComponents,
951        cfg: &mut ConfigBag,
952    ) -> Result<(), BoxError> {
953        self.interceptor
954            .read_before_signing(context, runtime_components, cfg)
955    }
956
957    fn read_before_transmit(
958        &self,
959        context: &BeforeTransmitInterceptorContextRef<'_>,
960        runtime_components: &RuntimeComponents,
961        cfg: &mut ConfigBag,
962    ) -> Result<(), BoxError> {
963        self.interceptor
964            .read_before_transmit(context, runtime_components, cfg)
965    }
966}
967
968impl_shared_conversions!(convert SharedInterceptor from Intercept using SharedInterceptor::new);
969
970/// Generalized interceptor disabling interface
971///
972/// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor<T>`](DisableInterceptor) into the config bag
973#[must_use]
974#[derive(Debug)]
975pub struct DisableInterceptor<T> {
976    _t: PhantomData<T>,
977    #[allow(unused)]
978    cause: &'static str,
979}
980
981impl<T> Storable for DisableInterceptor<T>
982where
983    T: fmt::Debug + Send + Sync + 'static,
984{
985    type Storer = StoreReplace<Self>;
986}
987
988/// Disable an interceptor with a given cause
989pub fn disable_interceptor<T: Intercept>(cause: &'static str) -> DisableInterceptor<T> {
990    DisableInterceptor {
991        _t: PhantomData,
992        cause,
993    }
994}