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/// Interceptor wrapper that may be shared
682#[derive(Clone)]
683pub struct SharedInterceptor {
684    interceptor: Arc<dyn Intercept>,
685    /// When `None`, the interceptor is always enabled (permanent mode).
686    #[allow(clippy::type_complexity)]
687    check_enabled: Option<Arc<dyn Fn(&ConfigBag) -> bool + Send + Sync>>,
688    /// Cached bitmask of which [`Intercept`] hooks this interceptor overrides.
689    overridden_hooks: OverriddenHooks,
690    /// In debug builds, asserts that nobody tried to disable a permanent interceptor.
691    #[cfg(debug_assertions)]
692    debug_assert_not_disabled: Option<fn(&ConfigBag) -> bool>,
693}
694
695impl fmt::Debug for SharedInterceptor {
696    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
697        f.debug_struct("SharedInterceptor")
698            .field("interceptor", &self.interceptor)
699            .field("permanent", &self.check_enabled.is_none())
700            .finish()
701    }
702}
703
704impl SharedInterceptor {
705    /// Create a new `SharedInterceptor` from `Interceptor`.
706    pub fn new<T: Intercept + 'static>(interceptor: T) -> Self {
707        Self {
708            overridden_hooks: interceptor.overridden_hooks(),
709            interceptor: Arc::new(interceptor),
710            check_enabled: Some(Arc::new(|conf: &ConfigBag| {
711                conf.load::<DisableInterceptor<T>>().is_none()
712            })),
713            #[cfg(debug_assertions)]
714            debug_assert_not_disabled: None,
715        }
716    }
717
718    /// Creates a `SharedInterceptor` that is always enabled.
719    ///
720    /// Unlike [`SharedInterceptor::new`], this skips the per-invocation
721    /// [`DisableInterceptor`] lookup in the config bag.
722    ///
723    /// Note: In debug builds, if [`disable_interceptor`] is called for an
724    /// interceptor wrapped with `permanent`, a panic will occur to flag the
725    /// misconfiguration. Use [`SharedInterceptor::new`] instead if the
726    /// interceptor needs to be disabled.
727    pub fn permanent<T: Intercept + 'static>(interceptor: T) -> Self {
728        Self {
729            overridden_hooks: interceptor.overridden_hooks(),
730            interceptor: Arc::new(interceptor),
731            check_enabled: None,
732            #[cfg(debug_assertions)]
733            debug_assert_not_disabled: Some(|conf: &ConfigBag| {
734                conf.load::<DisableInterceptor<T>>().is_none()
735            }),
736        }
737    }
738
739    /// Checks if this interceptor is enabled in the given config.
740    pub fn enabled(&self, conf: &ConfigBag) -> bool {
741        match &self.check_enabled {
742            Some(check) => check(conf),
743            None => {
744                #[cfg(debug_assertions)]
745                if let Some(check) = &self.debug_assert_not_disabled {
746                    debug_assert!(
747                        check(conf),
748                        "attempted to disable permanent interceptor `{}`; \
749                         use SharedInterceptor::new() instead of ::permanent() \
750                         if this interceptor needs to be disabled",
751                        self.interceptor.name()
752                    );
753                }
754                true
755            }
756        }
757    }
758
759    /// Returns the overridden hooks.
760    pub fn overridden_hooks(&self) -> OverriddenHooks {
761        self.overridden_hooks
762    }
763}
764
765impl ValidateConfig for SharedInterceptor {}
766
767impl Intercept for SharedInterceptor {
768    fn name(&self) -> &'static str {
769        self.interceptor.name()
770    }
771
772    fn modify_before_attempt_completion(
773        &self,
774        context: &mut FinalizerInterceptorContextMut<'_>,
775        runtime_components: &RuntimeComponents,
776        cfg: &mut ConfigBag,
777    ) -> Result<(), BoxError> {
778        self.interceptor
779            .modify_before_attempt_completion(context, runtime_components, cfg)
780    }
781
782    fn modify_before_completion(
783        &self,
784        context: &mut FinalizerInterceptorContextMut<'_>,
785        runtime_components: &RuntimeComponents,
786        cfg: &mut ConfigBag,
787    ) -> Result<(), BoxError> {
788        self.interceptor
789            .modify_before_completion(context, runtime_components, cfg)
790    }
791
792    fn modify_before_deserialization(
793        &self,
794        context: &mut BeforeDeserializationInterceptorContextMut<'_>,
795        runtime_components: &RuntimeComponents,
796        cfg: &mut ConfigBag,
797    ) -> Result<(), BoxError> {
798        self.interceptor
799            .modify_before_deserialization(context, runtime_components, cfg)
800    }
801
802    fn modify_before_retry_loop(
803        &self,
804        context: &mut BeforeTransmitInterceptorContextMut<'_>,
805        runtime_components: &RuntimeComponents,
806        cfg: &mut ConfigBag,
807    ) -> Result<(), BoxError> {
808        self.interceptor
809            .modify_before_retry_loop(context, runtime_components, cfg)
810    }
811
812    fn modify_before_serialization(
813        &self,
814        context: &mut BeforeSerializationInterceptorContextMut<'_>,
815        runtime_components: &RuntimeComponents,
816        cfg: &mut ConfigBag,
817    ) -> Result<(), BoxError> {
818        self.interceptor
819            .modify_before_serialization(context, runtime_components, cfg)
820    }
821
822    fn modify_before_signing(
823        &self,
824        context: &mut BeforeTransmitInterceptorContextMut<'_>,
825        runtime_components: &RuntimeComponents,
826        cfg: &mut ConfigBag,
827    ) -> Result<(), BoxError> {
828        self.interceptor
829            .modify_before_signing(context, runtime_components, cfg)
830    }
831
832    fn modify_before_transmit(
833        &self,
834        context: &mut BeforeTransmitInterceptorContextMut<'_>,
835        runtime_components: &RuntimeComponents,
836        cfg: &mut ConfigBag,
837    ) -> Result<(), BoxError> {
838        self.interceptor
839            .modify_before_transmit(context, runtime_components, cfg)
840    }
841
842    fn read_after_attempt(
843        &self,
844        context: &FinalizerInterceptorContextRef<'_>,
845        runtime_components: &RuntimeComponents,
846        cfg: &mut ConfigBag,
847    ) -> Result<(), BoxError> {
848        self.interceptor
849            .read_after_attempt(context, runtime_components, cfg)
850    }
851
852    fn read_after_deserialization(
853        &self,
854        context: &AfterDeserializationInterceptorContextRef<'_>,
855        runtime_components: &RuntimeComponents,
856        cfg: &mut ConfigBag,
857    ) -> Result<(), BoxError> {
858        self.interceptor
859            .read_after_deserialization(context, runtime_components, cfg)
860    }
861
862    fn read_after_execution(
863        &self,
864        context: &FinalizerInterceptorContextRef<'_>,
865        runtime_components: &RuntimeComponents,
866        cfg: &mut ConfigBag,
867    ) -> Result<(), BoxError> {
868        self.interceptor
869            .read_after_execution(context, runtime_components, cfg)
870    }
871
872    fn read_after_serialization(
873        &self,
874        context: &BeforeTransmitInterceptorContextRef<'_>,
875        runtime_components: &RuntimeComponents,
876        cfg: &mut ConfigBag,
877    ) -> Result<(), BoxError> {
878        self.interceptor
879            .read_after_serialization(context, runtime_components, cfg)
880    }
881
882    fn read_after_signing(
883        &self,
884        context: &BeforeTransmitInterceptorContextRef<'_>,
885        runtime_components: &RuntimeComponents,
886        cfg: &mut ConfigBag,
887    ) -> Result<(), BoxError> {
888        self.interceptor
889            .read_after_signing(context, runtime_components, cfg)
890    }
891
892    fn read_after_transmit(
893        &self,
894        context: &BeforeDeserializationInterceptorContextRef<'_>,
895        runtime_components: &RuntimeComponents,
896        cfg: &mut ConfigBag,
897    ) -> Result<(), BoxError> {
898        self.interceptor
899            .read_after_transmit(context, runtime_components, cfg)
900    }
901
902    fn read_before_attempt(
903        &self,
904        context: &BeforeTransmitInterceptorContextRef<'_>,
905        runtime_components: &RuntimeComponents,
906        cfg: &mut ConfigBag,
907    ) -> Result<(), BoxError> {
908        self.interceptor
909            .read_before_attempt(context, runtime_components, cfg)
910    }
911
912    fn read_before_deserialization(
913        &self,
914        context: &BeforeDeserializationInterceptorContextRef<'_>,
915        runtime_components: &RuntimeComponents,
916        cfg: &mut ConfigBag,
917    ) -> Result<(), BoxError> {
918        self.interceptor
919            .read_before_deserialization(context, runtime_components, cfg)
920    }
921
922    fn read_before_execution(
923        &self,
924        context: &BeforeSerializationInterceptorContextRef<'_>,
925        cfg: &mut ConfigBag,
926    ) -> Result<(), BoxError> {
927        self.interceptor.read_before_execution(context, cfg)
928    }
929
930    fn read_before_serialization(
931        &self,
932        context: &BeforeSerializationInterceptorContextRef<'_>,
933        runtime_components: &RuntimeComponents,
934        cfg: &mut ConfigBag,
935    ) -> Result<(), BoxError> {
936        self.interceptor
937            .read_before_serialization(context, runtime_components, cfg)
938    }
939
940    fn read_before_signing(
941        &self,
942        context: &BeforeTransmitInterceptorContextRef<'_>,
943        runtime_components: &RuntimeComponents,
944        cfg: &mut ConfigBag,
945    ) -> Result<(), BoxError> {
946        self.interceptor
947            .read_before_signing(context, runtime_components, cfg)
948    }
949
950    fn read_before_transmit(
951        &self,
952        context: &BeforeTransmitInterceptorContextRef<'_>,
953        runtime_components: &RuntimeComponents,
954        cfg: &mut ConfigBag,
955    ) -> Result<(), BoxError> {
956        self.interceptor
957            .read_before_transmit(context, runtime_components, cfg)
958    }
959}
960
961impl_shared_conversions!(convert SharedInterceptor from Intercept using SharedInterceptor::new);
962
963/// Generalized interceptor disabling interface
964///
965/// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor<T>`](DisableInterceptor) into the config bag
966#[must_use]
967#[derive(Debug)]
968pub struct DisableInterceptor<T> {
969    _t: PhantomData<T>,
970    #[allow(unused)]
971    cause: &'static str,
972}
973
974impl<T> Storable for DisableInterceptor<T>
975where
976    T: fmt::Debug + Send + Sync + 'static,
977{
978    type Storer = StoreReplace<Self>;
979}
980
981/// Disable an interceptor with a given cause
982pub fn disable_interceptor<T: Intercept>(cause: &'static str) -> DisableInterceptor<T> {
983    DisableInterceptor {
984        _t: PhantomData,
985        cause,
986    }
987}