mirror of https://github.com/smithy-lang/smithy-rs
More tidying up of `aws-smithy-runtime-api` (#2869)
This PR finishes documenting `aws-smithy-runtime-api` and also: - Adds `Send + Sync` bounds to `Interceptor`. - Moves `Interceptors` into `aws-smithy-runtime`. - Expands the more complicated macros in the interceptor context wrappers. - Makes the `OrchestratorError` an informative error according to [RFC-22](https://github.com/awslabs/smithy-rs/blob/main/design/src/rfcs/rfc0022_error_context_and_compatibility.md). - Renames `ConnectorError::is_other` to `as_other` and adds an equivalent `is_other` that returns a boolean. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
This commit is contained in:
parent
a5465b79a5
commit
2922f561b0
|
@ -77,7 +77,7 @@ impl<AP> RequestChecksumInterceptor<AP> {
|
||||||
|
|
||||||
impl<AP> Interceptor for RequestChecksumInterceptor<AP>
|
impl<AP> Interceptor for RequestChecksumInterceptor<AP>
|
||||||
where
|
where
|
||||||
AP: Fn(&Input) -> Result<Option<ChecksumAlgorithm>, BoxError>,
|
AP: Fn(&Input) -> Result<Option<ChecksumAlgorithm>, BoxError> + Send + Sync,
|
||||||
{
|
{
|
||||||
fn read_before_serialization(
|
fn read_before_serialization(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl<VE> ResponseChecksumInterceptor<VE> {
|
||||||
|
|
||||||
impl<VE> Interceptor for ResponseChecksumInterceptor<VE>
|
impl<VE> Interceptor for ResponseChecksumInterceptor<VE>
|
||||||
where
|
where
|
||||||
VE: Fn(&Input) -> bool,
|
VE: Fn(&Input) -> bool + Send + Sync,
|
||||||
{
|
{
|
||||||
fn read_before_serialization(
|
fn read_before_serialization(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -69,7 +69,7 @@ where
|
||||||
|
|
||||||
impl<G, T> Interceptor for Route53ResourceIdInterceptor<G, T>
|
impl<G, T> Interceptor for Route53ResourceIdInterceptor<G, T>
|
||||||
where
|
where
|
||||||
G: for<'a> Fn(&'a mut T) -> &'a mut Option<String>,
|
G: for<'a> Fn(&'a mut T) -> &'a mut Option<String> + Send + Sync,
|
||||||
T: fmt::Debug + Send + Sync + 'static,
|
T: fmt::Debug + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn modify_before_serialization(
|
fn modify_before_serialization(
|
||||||
|
|
|
@ -91,7 +91,7 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con
|
||||||
/// ## }
|
/// ## }
|
||||||
/// ## }
|
/// ## }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn interceptor(mut self, interceptor: impl #{Interceptor} + Send + Sync + 'static) -> Self {
|
pub fn interceptor(mut self, interceptor: impl #{Interceptor} + 'static) -> Self {
|
||||||
self.push_interceptor(#{SharedInterceptor}::new(interceptor));
|
self.push_interceptor(#{SharedInterceptor}::new(interceptor));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,7 +199,7 @@ class CustomizableOperationGenerator(
|
||||||
/// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood).
|
/// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood).
|
||||||
/// The order in which those user-specified operation interceptors are invoked should not be relied upon
|
/// The order in which those user-specified operation interceptors are invoked should not be relied upon
|
||||||
/// as it is an implementation detail.
|
/// as it is an implementation detail.
|
||||||
pub fn interceptor(mut self, interceptor: impl #{Interceptor} + #{Send} + #{Sync} + 'static) -> Self {
|
pub fn interceptor(mut self, interceptor: impl #{Interceptor} + 'static) -> Self {
|
||||||
self.interceptors.push(#{SharedInterceptor}::new(interceptor));
|
self.interceptors.push(#{SharedInterceptor}::new(interceptor));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,11 +232,16 @@ impl DispatchFailure {
|
||||||
self.source.is_user()
|
self.source.is_user()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the optional error kind associated with an unclassified error
|
/// Returns true if the error is an unclassified error.
|
||||||
pub fn is_other(&self) -> Option<ErrorKind> {
|
pub fn is_other(&self) -> bool {
|
||||||
self.source.is_other()
|
self.source.is_other()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the optional error kind associated with an unclassified error
|
||||||
|
pub fn as_other(&self) -> Option<ErrorKind> {
|
||||||
|
self.source.as_other()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the inner error if it is a connector error
|
/// Returns the inner error if it is a connector error
|
||||||
pub fn as_connector_error(&self) -> Option<&ConnectorError> {
|
pub fn as_connector_error(&self) -> Option<&ConnectorError> {
|
||||||
Some(&self.source)
|
Some(&self.source)
|
||||||
|
@ -633,8 +638,13 @@ impl ConnectorError {
|
||||||
matches!(self.kind, ConnectorErrorKind::User)
|
matches!(self.kind, ConnectorErrorKind::User)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the error is an unclassified error.
|
||||||
|
pub fn is_other(&self) -> bool {
|
||||||
|
matches!(self.kind, ConnectorErrorKind::Other(..))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the optional error kind associated with an unclassified error
|
/// Returns the optional error kind associated with an unclassified error
|
||||||
pub fn is_other(&self) -> Option<ErrorKind> {
|
pub fn as_other(&self) -> Option<ErrorKind> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ConnectorErrorKind::Other(ek) => *ek,
|
ConnectorErrorKind::Other(ek) => *ek,
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl DefaultResponseRetryClassifier {
|
||||||
Err(SdkError::DispatchFailure(err)) => {
|
Err(SdkError::DispatchFailure(err)) => {
|
||||||
if err.is_timeout() || err.is_io() {
|
if err.is_timeout() || err.is_io() {
|
||||||
Err(RetryKind::Error(ErrorKind::TransientError))
|
Err(RetryKind::Error(ErrorKind::TransientError))
|
||||||
} else if let Some(ek) = err.is_other() {
|
} else if let Some(ek) = err.as_other() {
|
||||||
Err(RetryKind::Error(ek))
|
Err(RetryKind::Error(ek))
|
||||||
} else {
|
} else {
|
||||||
Err(RetryKind::UnretryableFailure)
|
Err(RetryKind::UnretryableFailure)
|
||||||
|
|
|
@ -13,12 +13,10 @@ use crate::client::interceptors::context::{
|
||||||
BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut,
|
BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut,
|
||||||
BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut,
|
BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut,
|
||||||
BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut,
|
BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut,
|
||||||
FinalizerInterceptorContextRef, InterceptorContext,
|
FinalizerInterceptorContextRef,
|
||||||
};
|
};
|
||||||
use crate::client::runtime_components::RuntimeComponents;
|
use crate::client::runtime_components::RuntimeComponents;
|
||||||
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
||||||
use aws_smithy_types::error::display::DisplayErrorContext;
|
|
||||||
use context::{Error, Input, Output};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -38,9 +36,7 @@ macro_rules! interceptor_trait_fn {
|
||||||
runtime_components: &RuntimeComponents,
|
runtime_components: &RuntimeComponents,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
|
||||||
let _rc = runtime_components;
|
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -52,9 +48,7 @@ macro_rules! interceptor_trait_fn {
|
||||||
runtime_components: &RuntimeComponents,
|
runtime_components: &RuntimeComponents,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
|
||||||
let _rc = runtime_components;
|
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -70,7 +64,7 @@ macro_rules! interceptor_trait_fn {
|
||||||
/// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible
|
/// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible
|
||||||
/// to read in-flight request or response messages, or "read/write" hooks, which make it possible
|
/// to read in-flight request or response messages, or "read/write" hooks, which make it possible
|
||||||
/// to modify in-flight request or output messages.
|
/// to modify in-flight request or output messages.
|
||||||
pub trait Interceptor: fmt::Debug {
|
pub trait Interceptor: fmt::Debug + Send + Sync {
|
||||||
/// A hook called at the start of an execution, before the SDK
|
/// A hook called at the start of an execution, before the SDK
|
||||||
/// does anything else.
|
/// does anything else.
|
||||||
///
|
///
|
||||||
|
@ -78,14 +72,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
/// between invocation of this hook and `after_execution` is very close
|
/// between invocation of this hook and `after_execution` is very close
|
||||||
/// to full duration of the execution.
|
/// to full duration of the execution.
|
||||||
///
|
///
|
||||||
/// **Available Information:** The [InterceptorContext::input()] is
|
/// **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input)
|
||||||
/// **ALWAYS** available. Other information **WILL NOT** be available.
|
/// is **ALWAYS** available. Other information **WILL NOT** be available.
|
||||||
///
|
///
|
||||||
/// **Error Behavior:** Errors raised by this hook will be stored
|
/// **Error Behavior:** Errors raised by this hook will be stored
|
||||||
/// until all interceptors have had their `before_execution` invoked.
|
/// until all interceptors have had their `before_execution` invoked.
|
||||||
/// Other hooks will then be skipped and execution will jump to
|
/// Other hooks will then be skipped and execution will jump to
|
||||||
/// `modify_before_completion` with the raised error as the
|
/// `modify_before_completion` with the raised error as the
|
||||||
/// [InterceptorContext::output_or_error()]. If multiple
|
/// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple
|
||||||
/// `before_execution` methods raise errors, the latest
|
/// `before_execution` methods raise errors, the latest
|
||||||
/// will be used and earlier ones will be logged and dropped.
|
/// will be used and earlier ones will be logged and dropped.
|
||||||
fn read_before_execution(
|
fn read_before_execution(
|
||||||
|
@ -93,8 +87,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
context: &BeforeSerializationInterceptorContextRef<'_>,
|
context: &BeforeSerializationInterceptorContextRef<'_>,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _cfg) = (context, cfg);
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,14 +103,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
**When:** This will **ALWAYS** be called once per execution, except when a
|
**When:** This will **ALWAYS** be called once per execution, except when a
|
||||||
failure occurs earlier in the request pipeline.
|
failure occurs earlier in the request pipeline.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()] is
|
**Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is
|
||||||
**ALWAYS** available. This request may have been modified by earlier
|
**ALWAYS** available. This request may have been modified by earlier
|
||||||
`modify_before_serialization` hooks, and may be modified further by
|
`modify_before_serialization` hooks, and may be modified further by
|
||||||
later hooks. Other information **WILL NOT** be available.
|
later hooks. Other information **WILL NOT** be available.
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this hook,
|
**Error Behavior:** If errors are raised by this hook,
|
||||||
execution will jump to `modify_before_completion` with the raised
|
execution will jump to `modify_before_completion` with the raised
|
||||||
error as the [InterceptorContext::output_or_error()].
|
error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
|
|
||||||
**Return Constraints:** The input message returned by this hook
|
**Return Constraints:** The input message returned by this hook
|
||||||
MUST be the same type of input message passed into this hook.
|
MUST be the same type of input message passed into this hook.
|
||||||
|
@ -138,12 +131,12 @@ pub trait Interceptor: fmt::Debug {
|
||||||
duration between invocation of this hook and `after_serialization` is
|
duration between invocation of this hook and `after_serialization` is
|
||||||
very close to the amount of time spent marshalling the request.
|
very close to the amount of time spent marshalling the request.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()] is
|
**Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is
|
||||||
**ALWAYS** available. Other information **WILL NOT** be available.
|
**ALWAYS** available. Other information **WILL NOT** be available.
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this hook,
|
**Error Behavior:** If errors are raised by this hook,
|
||||||
execution will jump to `modify_before_completion` with the raised
|
execution will jump to `modify_before_completion` with the raised
|
||||||
error as the [InterceptorContext::output_or_error()].
|
error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -151,21 +144,20 @@ pub trait Interceptor: fmt::Debug {
|
||||||
read_after_serialization,
|
read_after_serialization,
|
||||||
BeforeTransmitInterceptorContextRef,
|
BeforeTransmitInterceptorContextRef,
|
||||||
"
|
"
|
||||||
/// A hook called after the input message is marshalled into
|
A hook called after the input message is marshalled into
|
||||||
/// a transport message.
|
a transport message.
|
||||||
///
|
|
||||||
/// **When:** This will **ALWAYS** be called once per execution, except when a
|
**When:** This will **ALWAYS** be called once per execution, except when a
|
||||||
/// failure occurs earlier in the request pipeline. The duration
|
failure occurs earlier in the request pipeline. The duration
|
||||||
/// between invocation of this hook and `before_serialization` is very
|
between invocation of this hook and `before_serialization` is very
|
||||||
/// close to the amount of time spent marshalling the request.
|
close to the amount of time spent marshalling the request.
|
||||||
///
|
|
||||||
/// **Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
|
||||||
/// and [InterceptorContext::request()] are **ALWAYS** available.
|
is **ALWAYS** available. Other information **WILL NOT** be available.
|
||||||
/// Other information **WILL NOT** be available.
|
|
||||||
///
|
**Error Behavior:** If errors are raised by this hook,
|
||||||
/// **Error Behavior:** If errors are raised by this hook,
|
execution will jump to `modify_before_completion` with the raised
|
||||||
/// execution will jump to `modify_before_completion` with the raised
|
error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
/// error as the [InterceptorContext::output_or_error()].
|
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -177,13 +169,12 @@ pub trait Interceptor: fmt::Debug {
|
||||||
has the ability to modify and return a new transport request
|
has the ability to modify and return a new transport request
|
||||||
message of the same type, except when a failure occurs earlier in the request pipeline.
|
message of the same type, except when a failure occurs earlier in the request pipeline.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
|
||||||
and [InterceptorContext::request()] are **ALWAYS** available.
|
is **ALWAYS** available. Other information **WILL NOT** be available.
|
||||||
Other information **WILL NOT** be available.
|
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this hook,
|
**Error Behavior:** If errors are raised by this hook,
|
||||||
execution will jump to `modify_before_completion` with the raised
|
execution will jump to `modify_before_completion` with the raised
|
||||||
error as the [InterceptorContext::output_or_error()].
|
error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
|
|
||||||
**Return Constraints:** The transport request message returned by this
|
**Return Constraints:** The transport request message returned by this
|
||||||
hook MUST be the same type of request message passed into this hook
|
hook MUST be the same type of request message passed into this hook
|
||||||
|
@ -202,9 +193,8 @@ pub trait Interceptor: fmt::Debug {
|
||||||
failure occurs earlier in the request pipeline. This method will be
|
failure occurs earlier in the request pipeline. This method will be
|
||||||
called multiple times in the event of retries.
|
called multiple times in the event of retries.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
|
||||||
and [InterceptorContext::request()] are **ALWAYS** available.
|
is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
|
||||||
Other information **WILL NOT** be available. In the event of retries,
|
|
||||||
the `InterceptorContext` will not include changes made in previous
|
the `InterceptorContext` will not include changes made in previous
|
||||||
attempts (e.g. by request signers or other interceptors).
|
attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
|
@ -212,7 +202,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
until all interceptors have had their `before_attempt` invoked.
|
until all interceptors have had their `before_attempt` invoked.
|
||||||
Other hooks will then be skipped and execution will jump to
|
Other hooks will then be skipped and execution will jump to
|
||||||
`modify_before_attempt_completion` with the raised error as the
|
`modify_before_attempt_completion` with the raised error as the
|
||||||
[InterceptorContext::output_or_error()]. If multiple
|
[`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple
|
||||||
`before_attempt` methods raise errors, the latest will be used
|
`before_attempt` methods raise errors, the latest will be used
|
||||||
and earlier ones will be logged and dropped.
|
and earlier ones will be logged and dropped.
|
||||||
"
|
"
|
||||||
|
@ -230,18 +220,16 @@ pub trait Interceptor: fmt::Debug {
|
||||||
failure occurs earlier in the request pipeline. This method may be
|
failure occurs earlier in the request pipeline. This method may be
|
||||||
called multiple times in the event of retries.
|
called multiple times in the event of retries.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
|
||||||
and [InterceptorContext::request()] are **ALWAYS** available.
|
is **ALWAYS** available. The `http::Request` may have been modified by earlier
|
||||||
The `http::Request` may have been modified by earlier
|
|
||||||
`modify_before_signing` hooks, and may be modified further by later
|
`modify_before_signing` hooks, and may be modified further by later
|
||||||
hooks. Other information **WILL NOT** be available. In the event of
|
hooks. Other information **WILL NOT** be available. In the event of
|
||||||
retries, the `InterceptorContext` will not include changes made
|
retries, the `InterceptorContext` will not include changes made
|
||||||
in previous attempts
|
in previous attempts (e.g. by request signers or other interceptors).
|
||||||
(e.g. by request signers or other interceptors).
|
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
|
|
||||||
**Return Constraints:** The transport request message returned by this
|
**Return Constraints:** The transport request message returned by this
|
||||||
hook MUST be the same type of request message passed into this hook
|
hook MUST be the same type of request message passed into this hook
|
||||||
|
@ -262,15 +250,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
invocation of this hook and `after_signing` is very close to
|
invocation of this hook and `after_signing` is very close to
|
||||||
the amount of time spent signing the request.
|
the amount of time spent signing the request.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available.
|
||||||
and [InterceptorContext::request()] are **ALWAYS** available.
|
|
||||||
Other information **WILL NOT** be available. In the event of retries,
|
Other information **WILL NOT** be available. In the event of retries,
|
||||||
the `InterceptorContext` will not include changes made in previous
|
the `InterceptorContext` will not include changes made in previous
|
||||||
attempts (e.g. by request signers or other interceptors).
|
attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -286,15 +273,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
invocation of this hook and `before_signing` is very close to
|
invocation of this hook and `before_signing` is very close to
|
||||||
the amount of time spent signing the request.
|
the amount of time spent signing the request.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available.
|
||||||
and [InterceptorContext::request()] are **ALWAYS** available.
|
|
||||||
Other information **WILL NOT** be available. In the event of retries,
|
Other information **WILL NOT** be available. In the event of retries,
|
||||||
the `InterceptorContext` will not include changes made in previous
|
the `InterceptorContext` will not include changes made in previous
|
||||||
attempts (e.g. by request signers or other interceptors).
|
attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -302,26 +288,25 @@ pub trait Interceptor: fmt::Debug {
|
||||||
mut modify_before_transmit,
|
mut modify_before_transmit,
|
||||||
BeforeTransmitInterceptorContextMut,
|
BeforeTransmitInterceptorContextMut,
|
||||||
"
|
"
|
||||||
/// A hook called before the transport request message is sent to the
|
A hook called before the transport request message is sent to the
|
||||||
/// service. This method has the ability to modify and return
|
service. This method has the ability to modify and return
|
||||||
/// a new transport request message of the same type.
|
a new transport request message of the same type.
|
||||||
///
|
|
||||||
/// **When:** This will **ALWAYS** be called once per attempt, except when a
|
**When:** This will **ALWAYS** be called once per attempt, except when a
|
||||||
/// failure occurs earlier in the request pipeline. This method may be
|
failure occurs earlier in the request pipeline. This method may be
|
||||||
/// called multiple times in the event of retries.
|
called multiple times in the event of retries.
|
||||||
///
|
|
||||||
/// **Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
|
||||||
/// and [InterceptorContext::request()] are **ALWAYS** available.
|
is **ALWAYS** available. The `http::Request` may have been modified by earlier
|
||||||
/// The `http::Request` may have been modified by earlier
|
`modify_before_transmit` hooks, and may be modified further by later
|
||||||
/// `modify_before_transmit` hooks, and may be modified further by later
|
hooks. Other information **WILL NOT** be available.
|
||||||
/// hooks. Other information **WILL NOT** be available.
|
In the event of retries, the `InterceptorContext` will not include
|
||||||
/// In the event of retries, the `InterceptorContext` will not include
|
changes made in previous attempts (e.g. by request signers or
|
||||||
/// changes made in previous attempts (e.g. by request signers or
|
|
||||||
other interceptors).
|
other interceptors).
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
|
|
||||||
**Return Constraints:** The transport request message returned by this
|
**Return Constraints:** The transport request message returned by this
|
||||||
hook MUST be the same type of request message passed into this hook
|
hook MUST be the same type of request message passed into this hook
|
||||||
|
@ -345,16 +330,15 @@ pub trait Interceptor: fmt::Debug {
|
||||||
Depending on the protocol, the duration may not include the
|
Depending on the protocol, the duration may not include the
|
||||||
time spent reading the response data.
|
time spent reading the response data.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()]
|
**Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request)
|
||||||
and [InterceptorContext::request()] are **ALWAYS** available.
|
is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
|
||||||
Other information **WILL NOT** be available. In the event of retries,
|
|
||||||
the `InterceptorContext` will not include changes made in previous
|
the `InterceptorContext` will not include changes made in previous
|
||||||
attempts (e.g. by request signers or other interceptors).
|
attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -373,16 +357,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
Depending on the protocol, the duration may not include the time
|
Depending on the protocol, the duration may not include the time
|
||||||
spent reading the response data.
|
spent reading the response data.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()],
|
**Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
|
||||||
[InterceptorContext::request()] and
|
is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
|
||||||
[InterceptorContext::response()] are **ALWAYS** available.
|
|
||||||
Other information **WILL NOT** be available. In the event of retries,
|
|
||||||
the `InterceptorContext` will not include changes made in previous
|
the `InterceptorContext` will not include changes made in previous
|
||||||
attempts (e.g. by request signers or other interceptors).
|
attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -398,10 +380,8 @@ pub trait Interceptor: fmt::Debug {
|
||||||
failure occurs earlier in the request pipeline. This method may be
|
failure occurs earlier in the request pipeline. This method may be
|
||||||
called multiple times in the event of retries.
|
called multiple times in the event of retries.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()],
|
**Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
|
||||||
[InterceptorContext::request()] and
|
is **ALWAYS** available. The transmit_response may have been modified by earlier
|
||||||
[InterceptorContext::response()] are **ALWAYS** available.
|
|
||||||
The transmit_response may have been modified by earlier
|
|
||||||
`modify_before_deserialization` hooks, and may be modified further by
|
`modify_before_deserialization` hooks, and may be modified further by
|
||||||
later hooks. Other information **WILL NOT** be available. In the event of
|
later hooks. Other information **WILL NOT** be available. In the event of
|
||||||
retries, the `InterceptorContext` will not include changes made in
|
retries, the `InterceptorContext` will not include changes made in
|
||||||
|
@ -410,7 +390,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the
|
the raised error as the
|
||||||
[InterceptorContext::output_or_error()].
|
[`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
|
|
||||||
**Return Constraints:** The transport response message returned by this
|
**Return Constraints:** The transport response message returned by this
|
||||||
hook MUST be the same type of response message passed into
|
hook MUST be the same type of response message passed into
|
||||||
|
@ -432,16 +412,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
Depending on the protocol and operation, the duration may include
|
Depending on the protocol and operation, the duration may include
|
||||||
the time spent downloading the response data.
|
the time spent downloading the response data.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()],
|
**Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
|
||||||
[InterceptorContext::request()] and
|
is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries,
|
||||||
[InterceptorContext::response()] are **ALWAYS** available.
|
|
||||||
Other information **WILL NOT** be available. In the event of retries,
|
|
||||||
the `InterceptorContext` will not include changes made in previous
|
the `InterceptorContext` will not include changes made in previous
|
||||||
attempts (e.g. by request signers or other interceptors).
|
attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion`
|
hook, execution will jump to `modify_before_attempt_completion`
|
||||||
with the raised error as the [InterceptorContext::output_or_error()].
|
with the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -459,16 +437,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
the duration may include the time spent downloading
|
the duration may include the time spent downloading
|
||||||
the response data.
|
the response data.
|
||||||
|
|
||||||
**Available Information:** The [InterceptorContext::input()],
|
**Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response)
|
||||||
[InterceptorContext::request()],
|
and [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error)
|
||||||
[InterceptorContext::response()] and
|
are **ALWAYS** available. In the event of retries, the `InterceptorContext` will not include changes made
|
||||||
[InterceptorContext::output_or_error()] are **ALWAYS** available. In the event
|
|
||||||
of retries, the `InterceptorContext` will not include changes made
|
|
||||||
in previous attempts (e.g. by request signers or other interceptors).
|
in previous attempts (e.g. by request signers or other interceptors).
|
||||||
|
|
||||||
**Error Behavior:** If errors are raised by this
|
**Error Behavior:** If errors are raised by this
|
||||||
hook, execution will jump to `modify_before_attempt_completion` with
|
hook, execution will jump to `modify_before_attempt_completion` with
|
||||||
the raised error as the [InterceptorContext::output_or_error()].
|
the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -480,16 +456,19 @@ pub trait Interceptor: fmt::Debug {
|
||||||
/// failure occurs before `before_attempt`. This method may
|
/// failure occurs before `before_attempt`. This method may
|
||||||
/// be called multiple times in the event of retries.
|
/// be called multiple times in the event of retries.
|
||||||
///
|
///
|
||||||
/// **Available Information:** The [InterceptorContext::input()],
|
/// **Available Information:**
|
||||||
/// [InterceptorContext::request()],
|
/// The [`InterceptorContext::input`](context::InterceptorContext::input),
|
||||||
/// [InterceptorContext::response()] and
|
/// [`InterceptorContext::request`](context::InterceptorContext::request),
|
||||||
/// [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event
|
/// [`InterceptorContext::response`](context::InterceptorContext::response), or
|
||||||
/// of retries, the `InterceptorContext` will not include changes made
|
/// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
|
||||||
|
/// If the operation succeeded, the `output` will be available. Otherwise, any of the other
|
||||||
|
/// pieces of information may be available depending on where in the operation lifecycle it failed.
|
||||||
|
/// In the event of retries, the `InterceptorContext` will not include changes made
|
||||||
/// in previous attempts (e.g. by request signers or other interceptors).
|
/// in previous attempts (e.g. by request signers or other interceptors).
|
||||||
///
|
///
|
||||||
/// **Error Behavior:** If errors are raised by this
|
/// **Error Behavior:** If errors are raised by this
|
||||||
/// hook, execution will jump to `after_attempt` with
|
/// hook, execution will jump to `after_attempt` with
|
||||||
/// the raised error as the [InterceptorContext::output_or_error()].
|
/// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
///
|
///
|
||||||
/// **Return Constraints:** Any output message returned by this
|
/// **Return Constraints:** Any output message returned by this
|
||||||
/// hook MUST match the operation being invoked. Any error type can be
|
/// hook MUST match the operation being invoked. Any error type can be
|
||||||
|
@ -500,9 +479,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
runtime_components: &RuntimeComponents,
|
runtime_components: &RuntimeComponents,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
|
||||||
let _rc = runtime_components;
|
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,14 +488,15 @@ pub trait Interceptor: fmt::Debug {
|
||||||
/// **When:** This will **ALWAYS** be called once per attempt, as long as
|
/// **When:** This will **ALWAYS** be called once per attempt, as long as
|
||||||
/// `before_attempt` has been executed.
|
/// `before_attempt` has been executed.
|
||||||
///
|
///
|
||||||
/// **Available Information:** The [InterceptorContext::input()],
|
/// **Available Information:**
|
||||||
/// [InterceptorContext::request()] and
|
/// The [`InterceptorContext::input`](context::InterceptorContext::input),
|
||||||
/// [InterceptorContext::output_or_error()] are **ALWAYS** available.
|
/// [`InterceptorContext::request`](context::InterceptorContext::request),
|
||||||
/// The [InterceptorContext::response()] is available if a
|
/// [`InterceptorContext::response`](context::InterceptorContext::response), or
|
||||||
/// response was received by the service for this attempt.
|
/// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
|
||||||
/// In the event of retries, the `InterceptorContext` will not include
|
/// If the operation succeeded, the `output` will be available. Otherwise, any of the other
|
||||||
/// changes made in previous attempts (e.g. by request signers or other
|
/// pieces of information may be available depending on where in the operation lifecycle it failed.
|
||||||
/// interceptors).
|
/// In the event of retries, the `InterceptorContext` will not include changes made
|
||||||
|
/// in previous attempts (e.g. by request signers or other interceptors).
|
||||||
///
|
///
|
||||||
/// **Error Behavior:** Errors raised by this hook will be stored
|
/// **Error Behavior:** Errors raised by this hook will be stored
|
||||||
/// until all interceptors have had their `after_attempt` invoked.
|
/// until all interceptors have had their `after_attempt` invoked.
|
||||||
|
@ -527,16 +505,14 @@ pub trait Interceptor: fmt::Debug {
|
||||||
/// retry strategy determines that the execution is retryable,
|
/// retry strategy determines that the execution is retryable,
|
||||||
/// execution will then jump to `before_attempt`. Otherwise,
|
/// execution will then jump to `before_attempt`. Otherwise,
|
||||||
/// execution will jump to `modify_before_attempt_completion` with the
|
/// execution will jump to `modify_before_attempt_completion` with the
|
||||||
/// raised error as the [InterceptorContext::output_or_error()].
|
/// raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
fn read_after_attempt(
|
fn read_after_attempt(
|
||||||
&self,
|
&self,
|
||||||
context: &FinalizerInterceptorContextRef<'_>,
|
context: &FinalizerInterceptorContextRef<'_>,
|
||||||
runtime_components: &RuntimeComponents,
|
runtime_components: &RuntimeComponents,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
|
||||||
let _rc = runtime_components;
|
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,15 +523,19 @@ pub trait Interceptor: fmt::Debug {
|
||||||
///
|
///
|
||||||
/// **When:** This will **ALWAYS** be called once per execution.
|
/// **When:** This will **ALWAYS** be called once per execution.
|
||||||
///
|
///
|
||||||
/// **Available Information:** The [InterceptorContext::input()]
|
/// **Available Information:**
|
||||||
/// and [InterceptorContext::output_or_error()] are **ALWAYS** available. The
|
/// The [`InterceptorContext::input`](context::InterceptorContext::input),
|
||||||
/// [InterceptorContext::request()]
|
/// [`InterceptorContext::request`](context::InterceptorContext::request),
|
||||||
/// and [InterceptorContext::response()] are available if the
|
/// [`InterceptorContext::response`](context::InterceptorContext::response), or
|
||||||
/// execution proceeded far enough for them to be generated.
|
/// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
|
||||||
|
/// If the operation succeeded, the `output` will be available. Otherwise, any of the other
|
||||||
|
/// pieces of information may be available depending on where in the operation lifecycle it failed.
|
||||||
|
/// In the event of retries, the `InterceptorContext` will not include changes made
|
||||||
|
/// in previous attempts (e.g. by request signers or other interceptors).
|
||||||
///
|
///
|
||||||
/// **Error Behavior:** If errors are raised by this
|
/// **Error Behavior:** If errors are raised by this
|
||||||
/// hook , execution will jump to `after_attempt` with
|
/// hook , execution will jump to `after_attempt` with
|
||||||
/// the raised error as the [InterceptorContext::output_or_error()].
|
/// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error).
|
||||||
///
|
///
|
||||||
/// **Return Constraints:** Any output message returned by this
|
/// **Return Constraints:** Any output message returned by this
|
||||||
/// hook MUST match the operation being invoked. Any error type can be
|
/// hook MUST match the operation being invoked. Any error type can be
|
||||||
|
@ -566,9 +546,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
runtime_components: &RuntimeComponents,
|
runtime_components: &RuntimeComponents,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
|
||||||
let _rc = runtime_components;
|
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,17 +556,21 @@ pub trait Interceptor: fmt::Debug {
|
||||||
/// between invocation of this hook and `before_execution` is very
|
/// between invocation of this hook and `before_execution` is very
|
||||||
/// close to the full duration of the execution.
|
/// close to the full duration of the execution.
|
||||||
///
|
///
|
||||||
/// **Available Information:** The [InterceptorContext::input()]
|
/// **Available Information:**
|
||||||
/// and [InterceptorContext::output_or_error()] are **ALWAYS** available. The
|
/// The [`InterceptorContext::input`](context::InterceptorContext::input),
|
||||||
/// [InterceptorContext::request()] and
|
/// [`InterceptorContext::request`](context::InterceptorContext::request),
|
||||||
/// [InterceptorContext::response()] are available if the
|
/// [`InterceptorContext::response`](context::InterceptorContext::response), or
|
||||||
/// execution proceeded far enough for them to be generated.
|
/// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available.
|
||||||
|
/// If the operation succeeded, the `output` will be available. Otherwise, any of the other
|
||||||
|
/// pieces of information may be available depending on where in the operation lifecycle it failed.
|
||||||
|
/// In the event of retries, the `InterceptorContext` will not include changes made
|
||||||
|
/// in previous attempts (e.g. by request signers or other interceptors).
|
||||||
///
|
///
|
||||||
/// **Error Behavior:** Errors raised by this hook will be stored
|
/// **Error Behavior:** Errors raised by this hook will be stored
|
||||||
/// until all interceptors have had their `after_execution` invoked.
|
/// until all interceptors have had their `after_execution` invoked.
|
||||||
/// The error will then be treated as the
|
/// The error will then be treated as the
|
||||||
/// [InterceptorContext::output_or_error()] to the customer. If multiple
|
/// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error)
|
||||||
/// `after_execution` methods raise errors , the latest will be
|
/// to the customer. If multiple `after_execution` methods raise errors , the latest will be
|
||||||
/// used and earlier ones will be logged and dropped.
|
/// used and earlier ones will be logged and dropped.
|
||||||
fn read_after_execution(
|
fn read_after_execution(
|
||||||
&self,
|
&self,
|
||||||
|
@ -596,9 +578,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
runtime_components: &RuntimeComponents,
|
runtime_components: &RuntimeComponents,
|
||||||
cfg: &mut ConfigBag,
|
cfg: &mut ConfigBag,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let _ctx = context;
|
let (_ctx, _rc, _cfg) = (context, runtime_components, cfg);
|
||||||
let _rc = runtime_components;
|
|
||||||
let _cfg = cfg;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -606,7 +586,7 @@ pub trait Interceptor: fmt::Debug {
|
||||||
/// Interceptor wrapper that may be shared
|
/// Interceptor wrapper that may be shared
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SharedInterceptor {
|
pub struct SharedInterceptor {
|
||||||
interceptor: Arc<dyn Interceptor + Send + Sync>,
|
interceptor: Arc<dyn Interceptor>,
|
||||||
check_enabled: Arc<dyn Fn(&ConfigBag) -> bool + Send + Sync>,
|
check_enabled: Arc<dyn Fn(&ConfigBag) -> bool + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,8 +599,8 @@ impl fmt::Debug for SharedInterceptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedInterceptor {
|
impl SharedInterceptor {
|
||||||
/// Create a new `SharedInterceptor` from `Interceptor`
|
/// Create a new `SharedInterceptor` from `Interceptor`.
|
||||||
pub fn new<T: Interceptor + Send + Sync + 'static>(interceptor: T) -> Self {
|
pub fn new<T: Interceptor + 'static>(interceptor: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
interceptor: Arc::new(interceptor),
|
interceptor: Arc::new(interceptor),
|
||||||
check_enabled: Arc::new(|conf: &ConfigBag| {
|
check_enabled: Arc::new(|conf: &ConfigBag| {
|
||||||
|
@ -629,7 +609,8 @@ impl SharedInterceptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enabled(&self, conf: &ConfigBag) -> bool {
|
/// Checks if this interceptor is enabled in the given config.
|
||||||
|
pub fn enabled(&self, conf: &ConfigBag) -> bool {
|
||||||
(self.check_enabled)(conf)
|
(self.check_enabled)(conf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,84 +622,12 @@ impl AsRef<dyn Interceptor> for SharedInterceptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for SharedInterceptor {
|
impl Deref for SharedInterceptor {
|
||||||
type Target = Arc<dyn Interceptor + Send + Sync>;
|
type Target = Arc<dyn Interceptor>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.interceptor
|
&self.interceptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A interceptor wrapper to conditionally enable the interceptor based on [`DisableInterceptor`]
|
|
||||||
struct ConditionallyEnabledInterceptor(SharedInterceptor);
|
|
||||||
impl ConditionallyEnabledInterceptor {
|
|
||||||
fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> {
|
|
||||||
if self.0.enabled(cfg) {
|
|
||||||
Some(self.0.as_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Interceptors<I> {
|
|
||||||
interceptors: I,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! interceptor_impl_fn {
|
|
||||||
(mut $interceptor:ident) => {
|
|
||||||
pub fn $interceptor(
|
|
||||||
self,
|
|
||||||
ctx: &mut InterceptorContext,
|
|
||||||
runtime_components: &RuntimeComponents,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
tracing::trace!(concat!(
|
|
||||||
"running `",
|
|
||||||
stringify!($interceptor),
|
|
||||||
"` interceptors"
|
|
||||||
));
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let mut ctx = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) =
|
|
||||||
interceptor.$interceptor(&mut ctx, runtime_components, cfg)
|
|
||||||
{
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::$interceptor)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(ref $interceptor:ident) => {
|
|
||||||
pub fn $interceptor(
|
|
||||||
self,
|
|
||||||
ctx: &InterceptorContext,
|
|
||||||
runtime_components: &RuntimeComponents,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let ctx = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg)
|
|
||||||
{
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::$interceptor)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generalized interceptor disabling interface
|
/// Generalized interceptor disabling interface
|
||||||
///
|
///
|
||||||
/// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor<T>`](DisableInterceptor) into the config bag
|
/// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor<T>`](DisableInterceptor) into the config bag
|
||||||
|
@ -744,215 +653,3 @@ pub fn disable_interceptor<T: Interceptor>(cause: &'static str) -> DisableInterc
|
||||||
cause,
|
cause,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Interceptors<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = SharedInterceptor>,
|
|
||||||
{
|
|
||||||
pub fn new(interceptors: I) -> Self {
|
|
||||||
Self { interceptors }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_iter(self) -> impl Iterator<Item = ConditionallyEnabledInterceptor> {
|
|
||||||
self.interceptors.map(ConditionallyEnabledInterceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_before_execution(
|
|
||||||
self,
|
|
||||||
operation: bool,
|
|
||||||
ctx: &InterceptorContext<Input, Output, Error>,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
tracing::trace!(
|
|
||||||
"running {} `read_before_execution` interceptors",
|
|
||||||
if operation { "operation" } else { "client" }
|
|
||||||
);
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) {
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::read_before_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
interceptor_impl_fn!(mut modify_before_serialization);
|
|
||||||
interceptor_impl_fn!(ref read_before_serialization);
|
|
||||||
interceptor_impl_fn!(ref read_after_serialization);
|
|
||||||
interceptor_impl_fn!(mut modify_before_retry_loop);
|
|
||||||
interceptor_impl_fn!(ref read_before_attempt);
|
|
||||||
interceptor_impl_fn!(mut modify_before_signing);
|
|
||||||
interceptor_impl_fn!(ref read_before_signing);
|
|
||||||
interceptor_impl_fn!(ref read_after_signing);
|
|
||||||
interceptor_impl_fn!(mut modify_before_transmit);
|
|
||||||
interceptor_impl_fn!(ref read_before_transmit);
|
|
||||||
interceptor_impl_fn!(ref read_after_transmit);
|
|
||||||
interceptor_impl_fn!(mut modify_before_deserialization);
|
|
||||||
interceptor_impl_fn!(ref read_before_deserialization);
|
|
||||||
interceptor_impl_fn!(ref read_after_deserialization);
|
|
||||||
|
|
||||||
pub fn modify_before_attempt_completion(
|
|
||||||
self,
|
|
||||||
ctx: &mut InterceptorContext<Input, Output, Error>,
|
|
||||||
runtime_components: &RuntimeComponents,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
tracing::trace!("running `modify_before_attempt_completion` interceptors");
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) =
|
|
||||||
interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg)
|
|
||||||
{
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::modify_before_attempt_completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_after_attempt(
|
|
||||||
self,
|
|
||||||
ctx: &InterceptorContext<Input, Output, Error>,
|
|
||||||
runtime_components: &RuntimeComponents,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
tracing::trace!("running `read_after_attempt` interceptors");
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let ctx: FinalizerInterceptorContextRef<'_> = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) =
|
|
||||||
interceptor.read_after_attempt(&ctx, runtime_components, cfg)
|
|
||||||
{
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::read_after_attempt)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modify_before_completion(
|
|
||||||
self,
|
|
||||||
ctx: &mut InterceptorContext<Input, Output, Error>,
|
|
||||||
runtime_components: &RuntimeComponents,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
tracing::trace!("running `modify_before_completion` interceptors");
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) =
|
|
||||||
interceptor.modify_before_completion(&mut ctx, runtime_components, cfg)
|
|
||||||
{
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::modify_before_completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_after_execution(
|
|
||||||
self,
|
|
||||||
ctx: &InterceptorContext<Input, Output, Error>,
|
|
||||||
runtime_components: &RuntimeComponents,
|
|
||||||
cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), InterceptorError> {
|
|
||||||
tracing::trace!("running `read_after_execution` interceptors");
|
|
||||||
let mut result: Result<(), BoxError> = Ok(());
|
|
||||||
let ctx: FinalizerInterceptorContextRef<'_> = ctx.into();
|
|
||||||
for interceptor in self.into_iter() {
|
|
||||||
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
|
||||||
if let Err(new_error) =
|
|
||||||
interceptor.read_after_execution(&ctx, runtime_components, cfg)
|
|
||||||
{
|
|
||||||
if let Err(last_error) = result {
|
|
||||||
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
|
||||||
}
|
|
||||||
result = Err(new_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.map_err(InterceptorError::read_after_execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(test, feature = "test-util"))]
|
|
||||||
mod tests {
|
|
||||||
use crate::client::interceptors::context::Input;
|
|
||||||
use crate::client::interceptors::{
|
|
||||||
disable_interceptor, BeforeTransmitInterceptorContextRef, BoxError, Interceptor,
|
|
||||||
InterceptorContext, Interceptors, SharedInterceptor,
|
|
||||||
};
|
|
||||||
use crate::client::runtime_components::{RuntimeComponents, RuntimeComponentsBuilder};
|
|
||||||
use aws_smithy_types::config_bag::ConfigBag;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TestInterceptor;
|
|
||||||
impl Interceptor for TestInterceptor {}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_disable_interceptors() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct PanicInterceptor;
|
|
||||||
impl Interceptor for PanicInterceptor {
|
|
||||||
fn read_before_transmit(
|
|
||||||
&self,
|
|
||||||
_context: &BeforeTransmitInterceptorContextRef<'_>,
|
|
||||||
_rc: &RuntimeComponents,
|
|
||||||
_cfg: &mut ConfigBag,
|
|
||||||
) -> Result<(), BoxError> {
|
|
||||||
Err("boom".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let rc = RuntimeComponentsBuilder::for_tests()
|
|
||||||
.with_interceptor(SharedInterceptor::new(PanicInterceptor))
|
|
||||||
.with_interceptor(SharedInterceptor::new(TestInterceptor))
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut cfg = ConfigBag::base();
|
|
||||||
let interceptors = Interceptors::new(rc.interceptors());
|
|
||||||
assert_eq!(
|
|
||||||
interceptors
|
|
||||||
.into_iter()
|
|
||||||
.filter(|i| i.if_enabled(&cfg).is_some())
|
|
||||||
.count(),
|
|
||||||
2
|
|
||||||
);
|
|
||||||
|
|
||||||
Interceptors::new(rc.interceptors())
|
|
||||||
.read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg)
|
|
||||||
.expect_err("interceptor returns error");
|
|
||||||
cfg.interceptor_state()
|
|
||||||
.store_put(disable_interceptor::<PanicInterceptor>("test"));
|
|
||||||
assert_eq!(
|
|
||||||
Interceptors::new(rc.interceptors())
|
|
||||||
.into_iter()
|
|
||||||
.filter(|i| i.if_enabled(&cfg).is_some())
|
|
||||||
.count(),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
// shouldn't error because interceptors won't run
|
|
||||||
Interceptors::new(rc.interceptors())
|
|
||||||
.read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg)
|
|
||||||
.expect("interceptor is now disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -37,9 +37,13 @@ use std::{fmt, mem};
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
// TODO(enableNewSmithyRuntimeLaunch): New-type `Input`/`Output`/`Error`
|
// TODO(enableNewSmithyRuntimeLaunch): New-type `Input`/`Output`/`Error`
|
||||||
|
/// Type-erased operation input.
|
||||||
pub type Input = TypeErasedBox;
|
pub type Input = TypeErasedBox;
|
||||||
|
/// Type-erased operation output.
|
||||||
pub type Output = TypeErasedBox;
|
pub type Output = TypeErasedBox;
|
||||||
|
/// Type-erased operation error.
|
||||||
pub type Error = TypeErasedError;
|
pub type Error = TypeErasedError;
|
||||||
|
/// Type-erased result for an operation.
|
||||||
pub type OutputOrError = Result<Output, OrchestratorError<Error>>;
|
pub type OutputOrError = Result<Output, OrchestratorError<Error>>;
|
||||||
|
|
||||||
type Request = HttpRequest;
|
type Request = HttpRequest;
|
||||||
|
@ -89,38 +93,7 @@ impl InterceptorContext<Input, Output, Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, O, E: Debug> InterceptorContext<I, O, E> {
|
impl<I, O, E> InterceptorContext<I, O, E> {
|
||||||
/// Decomposes the context into its constituent parts.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn into_parts(
|
|
||||||
self,
|
|
||||||
) -> (
|
|
||||||
Option<I>,
|
|
||||||
Option<Result<O, OrchestratorError<E>>>,
|
|
||||||
Option<Request>,
|
|
||||||
Option<Response>,
|
|
||||||
) {
|
|
||||||
(
|
|
||||||
self.input,
|
|
||||||
self.output_or_error,
|
|
||||||
self.request,
|
|
||||||
self.response,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finalize(self) -> Result<O, SdkError<E, HttpResponse>> {
|
|
||||||
let Self {
|
|
||||||
output_or_error,
|
|
||||||
response,
|
|
||||||
phase,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
output_or_error
|
|
||||||
.expect("output_or_error must always be set before finalize is called.")
|
|
||||||
.map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the input for the operation being invoked.
|
/// Retrieve the input for the operation being invoked.
|
||||||
pub fn input(&self) -> Option<&I> {
|
pub fn input(&self) -> Option<&I> {
|
||||||
self.input.as_ref()
|
self.input.as_ref()
|
||||||
|
@ -188,6 +161,14 @@ impl<I, O, E: Debug> InterceptorContext<I, O, E> {
|
||||||
self.output_or_error.as_mut()
|
self.output_or_error.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if this context's `output_or_error` is an error. Otherwise, return `false`.
|
||||||
|
pub fn is_failed(&self) -> bool {
|
||||||
|
self.output_or_error
|
||||||
|
.as_ref()
|
||||||
|
.map(Result::is_err)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
/// Advance to the Serialization phase.
|
/// Advance to the Serialization phase.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn enter_serialization_phase(&mut self) {
|
pub fn enter_serialization_phase(&mut self) {
|
||||||
|
@ -314,6 +295,44 @@ impl<I, O, E: Debug> InterceptorContext<I, O, E> {
|
||||||
self.output_or_error = None;
|
self.output_or_error = None;
|
||||||
RewindResult::Occurred
|
RewindResult::Occurred
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, O, E> InterceptorContext<I, O, E>
|
||||||
|
where
|
||||||
|
E: Debug,
|
||||||
|
{
|
||||||
|
/// Decomposes the context into its constituent parts.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn into_parts(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
Option<I>,
|
||||||
|
Option<Result<O, OrchestratorError<E>>>,
|
||||||
|
Option<Request>,
|
||||||
|
Option<Response>,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
self.input,
|
||||||
|
self.output_or_error,
|
||||||
|
self.request,
|
||||||
|
self.response,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this context into the final operation result that is returned in client's the public API.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn finalize(self) -> Result<O, SdkError<E, HttpResponse>> {
|
||||||
|
let Self {
|
||||||
|
output_or_error,
|
||||||
|
response,
|
||||||
|
phase,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
output_or_error
|
||||||
|
.expect("output_or_error must always be set before finalize is called.")
|
||||||
|
.map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response))
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark this context as failed due to errors during the operation. Any errors already contained
|
/// Mark this context as failed due to errors during the operation. Any errors already contained
|
||||||
/// by the context will be replaced by the given error.
|
/// by the context will be replaced by the given error.
|
||||||
|
@ -328,14 +347,6 @@ impl<I, O, E: Debug> InterceptorContext<I, O, E> {
|
||||||
error!("orchestrator context received an error but one was already present; Throwing away previous error: {:?}", existing_err);
|
error!("orchestrator context received an error but one was already present; Throwing away previous error: {:?}", existing_err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if this context's `output_or_error` is an error. Otherwise, return `false`.
|
|
||||||
pub fn is_failed(&self) -> bool {
|
|
||||||
self.output_or_error
|
|
||||||
.as_ref()
|
|
||||||
.map(Result::is_err)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of attempting to rewind a request.
|
/// The result of attempting to rewind a request.
|
||||||
|
|
|
@ -8,191 +8,222 @@ use crate::client::interceptors::context::{Request, Response};
|
||||||
use crate::client::orchestrator::OrchestratorError;
|
use crate::client::orchestrator::OrchestratorError;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
macro_rules! output {
|
macro_rules! impl_from_interceptor_context {
|
||||||
(&Option<Result<$o_ty:ty, $e_ty:ty>>) => {
|
(ref $wrapper:ident) => {
|
||||||
Option<Result<&$o_ty, &$e_ty>>
|
impl<'a, I, O, E> From<&'a InterceptorContext<I, O, E>> for $wrapper<'a, I, O, E> {
|
||||||
};
|
|
||||||
(&Option<$ty:ty>) => {
|
|
||||||
Option<&$ty>
|
|
||||||
};
|
|
||||||
(&mut Option<$ty:ty>) => {
|
|
||||||
Option<&mut $ty>
|
|
||||||
};
|
|
||||||
(&Result<$o_ty:ty, $e_ty:ty>) => {
|
|
||||||
Result<&$o_ty, &$e_ty>
|
|
||||||
};
|
|
||||||
(&$($tt:tt)+) => {
|
|
||||||
&$($tt)+
|
|
||||||
};
|
|
||||||
(&mut $($tt:tt)+) => {
|
|
||||||
&mut $($tt)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! declare_method {
|
|
||||||
(&mut $name:ident, $inner_name:ident, $doc:literal, Option<$ty:ty>) => {
|
|
||||||
#[doc=$doc]
|
|
||||||
pub fn $name(&mut self) -> Option<&mut $ty> {
|
|
||||||
self.inner.$inner_name.as_ref()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(&$name:ident, $inner_name:ident, $doc:literal, Option<$ty:ty>) => {
|
|
||||||
#[doc=$doc]
|
|
||||||
pub fn $name(&self) -> Option<$ty> {
|
|
||||||
self.inner.$inner_name.as_mut()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(&mut $name:ident, $doc:literal, $($tt:tt)+) => {
|
|
||||||
#[doc=$doc]
|
|
||||||
pub fn $name(&mut self) -> output!(&mut $($tt)+) {
|
|
||||||
self.inner.$name().expect(concat!("`", stringify!($name), "` wasn't set in the underlying interceptor context. This is a bug."))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(&$name:ident, $doc:literal, $($tt:tt)+) => {
|
|
||||||
#[doc=$doc]
|
|
||||||
pub fn $name(&self) -> output!(&$($tt)+) {
|
|
||||||
self.inner.$name().expect(concat!("`", stringify!($name), "` wasn't set in the underlying interceptor context. This is a bug."))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! declare_known_method {
|
|
||||||
(output_or_error: &mut $($tt:tt)+) => {
|
|
||||||
declare_method!(&mut output_or_error_mut, "Returns a mutable reference to the deserialized output or error.", $($tt)+);
|
|
||||||
};
|
|
||||||
(output_or_error: &$($tt:tt)+) => {
|
|
||||||
declare_method!(&output_or_error, "Returns a reference to the deserialized output or error.", $($tt)+);
|
|
||||||
};
|
|
||||||
(input: &mut $($tt:tt)+) => {
|
|
||||||
declare_method!(&mut input_mut, "Returns a mutable reference to the input.", $($tt)+);
|
|
||||||
};
|
|
||||||
(input: &$($tt:tt)+) => {
|
|
||||||
declare_method!(&input, "Returns a reference to the input.", $($tt)+);
|
|
||||||
};
|
|
||||||
(request: &mut $($tt:tt)+) => {
|
|
||||||
declare_method!(&mut request_mut, "Returns a mutable reference to the transmittable request for the operation being invoked.", $($tt)+);
|
|
||||||
};
|
|
||||||
(request: &$($tt:tt)+) => {
|
|
||||||
declare_method!(&request, "Returns a reference to the transmittable request for the operation being invoked.", $($tt)+);
|
|
||||||
};
|
|
||||||
(response: &mut $($tt:tt)+) => {
|
|
||||||
declare_method!(&mut response_mut, "Returns a mutable reference to the response.", $($tt)+);
|
|
||||||
};
|
|
||||||
(response: &$($tt:tt)+) => {
|
|
||||||
declare_method!(&response, "Returns a reference to the response.", $($tt)+);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! declare_wrapper {
|
|
||||||
(($ref_struct_name:ident readonly)$($tt:tt)+) => {
|
|
||||||
pub struct $ref_struct_name<'a, I = Input, O = Output, E = Error> {
|
|
||||||
inner: &'a InterceptorContext<I, O, E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> From<&'a InterceptorContext<I, O, E>> for $ref_struct_name<'a, I, O, E>
|
|
||||||
{
|
|
||||||
fn from(inner: &'a InterceptorContext<I, O, E>) -> Self {
|
fn from(inner: &'a InterceptorContext<I, O, E>) -> Self {
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> $ref_struct_name<'a, I, O, E> {
|
|
||||||
declare_ref_wrapper_methods!($($tt)+);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
(($ref_struct_name:ident $mut_struct_name:ident)$($tt:tt)+) => {
|
(mut $wrapper:ident) => {
|
||||||
declare_wrapper!(($ref_struct_name readonly) $($tt)+);
|
impl<'a, I, O, E> From<&'a mut InterceptorContext<I, O, E>> for $wrapper<'a, I, O, E> {
|
||||||
|
|
||||||
pub struct $mut_struct_name<'a, I = Input, O = Output, E = Error> {
|
|
||||||
inner: &'a mut InterceptorContext<I, O, E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext<I, O, E>> for $mut_struct_name<'a, I, O, E>
|
|
||||||
{
|
|
||||||
fn from(inner: &'a mut InterceptorContext<I, O, E>) -> Self {
|
fn from(inner: &'a mut InterceptorContext<I, O, E>) -> Self {
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> $mut_struct_name<'a, I, O, E> {
|
|
||||||
declare_ref_wrapper_methods!($($tt)+);
|
|
||||||
declare_mut_wrapper_methods!($($tt)+);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! declare_ref_wrapper_methods {
|
macro_rules! expect {
|
||||||
(($field:ident: $($head:tt)+)$($tail:tt)+) => {
|
($self:ident, $what:ident) => {
|
||||||
declare_known_method!($field: &$($head)+);
|
$self.inner.$what().expect(concat!(
|
||||||
declare_ref_wrapper_methods!($($tail)+);
|
"`",
|
||||||
};
|
stringify!($what),
|
||||||
(($field:ident: $($tt:tt)+)) => {
|
"` wasn't set in the underlying interceptor context. This is a bug."
|
||||||
declare_known_method!($field: &$($tt)+);
|
))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! declare_mut_wrapper_methods {
|
//
|
||||||
(($field:ident: $($head:tt)+)$($tail:tt)+) => {
|
// BeforeSerializationInterceptorContextRef
|
||||||
declare_known_method!($field: &mut $($head)+);
|
//
|
||||||
declare_mut_wrapper_methods!($($tail)+);
|
|
||||||
};
|
/// Interceptor context for the `read_before_execution` and `read_before_serialization` hooks.
|
||||||
(($field:ident: $($tt:tt)+)) => {
|
///
|
||||||
declare_known_method!($field: &mut $($tt)+);
|
/// Only the input is available at this point in the operation.
|
||||||
};
|
#[derive(Debug)]
|
||||||
|
pub struct BeforeSerializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a InterceptorContext<I, O, E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_wrapper!(
|
impl_from_interceptor_context!(ref BeforeSerializationInterceptorContextRef);
|
||||||
(BeforeSerializationInterceptorContextRef BeforeSerializationInterceptorContextMut)
|
|
||||||
(input: I)
|
|
||||||
);
|
|
||||||
|
|
||||||
declare_wrapper!(
|
impl<'a, I, O, E> BeforeSerializationInterceptorContextRef<'a, I, O, E> {
|
||||||
(BeforeTransmitInterceptorContextRef BeforeTransmitInterceptorContextMut)
|
/// Returns a reference to the input.
|
||||||
(request: Request)
|
pub fn input(&self) -> &I {
|
||||||
);
|
expect!(self, input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
declare_wrapper!(
|
//
|
||||||
(BeforeDeserializationInterceptorContextRef BeforeDeserializationInterceptorContextMut)
|
// BeforeSerializationInterceptorContextMut
|
||||||
(input: I)
|
//
|
||||||
(request: Request)
|
|
||||||
(response: Response)
|
/// Interceptor context for the `modify_before_serialization` hook.
|
||||||
);
|
///
|
||||||
|
/// Only the input is available at this point in the operation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BeforeSerializationInterceptorContextMut<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a mut InterceptorContext<I, O, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_interceptor_context!(mut BeforeSerializationInterceptorContextMut);
|
||||||
|
|
||||||
|
impl<'a, I, O, E> BeforeSerializationInterceptorContextMut<'a, I, O, E> {
|
||||||
|
/// Returns a reference to the input.
|
||||||
|
pub fn input(&self) -> &I {
|
||||||
|
expect!(self, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the input.
|
||||||
|
pub fn input_mut(&mut self) -> &mut I {
|
||||||
|
expect!(self, input_mut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BeforeSerializationInterceptorContextRef
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Interceptor context for several hooks in between serialization and transmission.
|
||||||
|
///
|
||||||
|
/// Only the request is available at this point in the operation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BeforeTransmitInterceptorContextRef<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a InterceptorContext<I, O, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_interceptor_context!(ref BeforeTransmitInterceptorContextRef);
|
||||||
|
|
||||||
|
impl<'a, I, O, E> BeforeTransmitInterceptorContextRef<'a, I, O, E> {
|
||||||
|
/// Returns a reference to the transmittable request for the operation being invoked.
|
||||||
|
pub fn request(&self) -> &Request {
|
||||||
|
expect!(self, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BeforeSerializationInterceptorContextMut
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Interceptor context for several hooks in between serialization and transmission.
|
||||||
|
///
|
||||||
|
/// Only the request is available at this point in the operation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BeforeTransmitInterceptorContextMut<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a mut InterceptorContext<I, O, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_interceptor_context!(mut BeforeTransmitInterceptorContextMut);
|
||||||
|
|
||||||
|
impl<'a, I, O, E> BeforeTransmitInterceptorContextMut<'a, I, O, E> {
|
||||||
|
/// Returns a reference to the transmittable request for the operation being invoked.
|
||||||
|
pub fn request(&self) -> &Request {
|
||||||
|
expect!(self, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the transmittable request for the operation being invoked.
|
||||||
|
pub fn request_mut(&mut self) -> &mut Request {
|
||||||
|
expect!(self, request_mut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BeforeDeserializationInterceptorContextRef
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Interceptor context for hooks before deserializing the response.
|
||||||
|
///
|
||||||
|
/// Only the response is available at this point in the operation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BeforeDeserializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a InterceptorContext<I, O, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_interceptor_context!(ref BeforeDeserializationInterceptorContextRef);
|
||||||
|
|
||||||
|
impl<'a, I, O, E> BeforeDeserializationInterceptorContextRef<'a, I, O, E> {
|
||||||
|
/// Returns a reference to the response.
|
||||||
|
pub fn response(&self) -> &Response {
|
||||||
|
expect!(self, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BeforeDeserializationInterceptorContextMut
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Interceptor context for hooks before deserializing the response.
|
||||||
|
///
|
||||||
|
/// Only the response is available at this point in the operation.
|
||||||
|
pub struct BeforeDeserializationInterceptorContextMut<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a mut InterceptorContext<I, O, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_interceptor_context!(mut BeforeDeserializationInterceptorContextMut);
|
||||||
|
|
||||||
|
impl<'a, I, O, E> BeforeDeserializationInterceptorContextMut<'a, I, O, E> {
|
||||||
|
/// Returns a reference to the response.
|
||||||
|
pub fn response(&self) -> &Response {
|
||||||
|
expect!(self, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the response.
|
||||||
|
pub fn response_mut(&mut self) -> &mut Response {
|
||||||
|
expect!(self, response_mut)
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> BeforeDeserializationInterceptorContextMut<'a, I, O, E> {
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Downgrade this helper struct, returning the underlying InterceptorContext. There's no good
|
/// Downgrade this helper struct, returning the underlying InterceptorContext. There's no good
|
||||||
/// reason to use this unless you're writing tests or you have to interact with an API that
|
/// reason to use this unless you're writing tests or you have to interact with an API that
|
||||||
/// doesn't support the helper structs.
|
/// doesn't support the helper structs.
|
||||||
pub fn into_inner(&mut self) -> &'_ mut InterceptorContext<I, O, E> {
|
pub fn inner_mut(&mut self) -> &'_ mut InterceptorContext<I, O, E> {
|
||||||
self.inner
|
self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_wrapper!(
|
//
|
||||||
(AfterDeserializationInterceptorContextRef readonly)
|
// AfterDeserializationInterceptorContextRef
|
||||||
(input: I)
|
//
|
||||||
(request: Request)
|
|
||||||
(response: Response)
|
|
||||||
(output_or_error: Result<O, OrchestratorError<E>>
|
|
||||||
));
|
|
||||||
|
|
||||||
// Why are all the rest of these defined with a macro but these last two aren't? I simply ran out of
|
/// Interceptor context for hooks after deserializing the response.
|
||||||
// time. Consider updating the macros to support these last two if you're looking for a challenge.
|
///
|
||||||
// - Zelda
|
/// The response and the deserialized output or error are available at this point in the operation.
|
||||||
|
pub struct AfterDeserializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> {
|
||||||
|
inner: &'a InterceptorContext<I, O, E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_interceptor_context!(ref AfterDeserializationInterceptorContextRef);
|
||||||
|
|
||||||
|
impl<'a, I, O, E> AfterDeserializationInterceptorContextRef<'a, I, O, E> {
|
||||||
|
/// Returns a reference to the response.
|
||||||
|
pub fn response(&self) -> &Response {
|
||||||
|
expect!(self, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the deserialized output or error.
|
||||||
|
pub fn output_or_error(&self) -> Result<&O, &OrchestratorError<E>> {
|
||||||
|
expect!(self, output_or_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// FinalizerInterceptorContextRef
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Interceptor context for finalization hooks.
|
||||||
|
///
|
||||||
|
/// This context is used by the `read_after_attempt` and `read_after_execution` hooks
|
||||||
|
/// that are all called upon both success and failure, and may have varying levels
|
||||||
|
/// of context available depending on where a failure occurred if the operation failed.
|
||||||
pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> {
|
pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> {
|
||||||
inner: &'a InterceptorContext<I, O, E>,
|
inner: &'a InterceptorContext<I, O, E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> From<&'a InterceptorContext<I, O, E>>
|
impl_from_interceptor_context!(ref FinalizerInterceptorContextRef);
|
||||||
for FinalizerInterceptorContextRef<'a, I, O, E>
|
|
||||||
{
|
|
||||||
fn from(inner: &'a InterceptorContext<I, O, E>) -> Self {
|
|
||||||
Self { inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> {
|
impl<'a, I, O, E> FinalizerInterceptorContextRef<'a, I, O, E> {
|
||||||
/// Returns the operation input.
|
/// Returns the operation input.
|
||||||
pub fn input(&self) -> Option<&I> {
|
pub fn input(&self) -> Option<&I> {
|
||||||
self.inner.input.as_ref()
|
self.inner.input.as_ref()
|
||||||
|
@ -214,19 +245,22 @@ impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// FinalizerInterceptorContextMut
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Interceptor context for finalization hooks.
|
||||||
|
///
|
||||||
|
/// This context is used by the `modify_before_attempt_completion` and `modify_before_completion` hooks
|
||||||
|
/// that are all called upon both success and failure, and may have varying levels
|
||||||
|
/// of context available depending on where a failure occurred if the operation failed.
|
||||||
pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> {
|
pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> {
|
||||||
inner: &'a mut InterceptorContext<I, O, E>,
|
inner: &'a mut InterceptorContext<I, O, E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext<I, O, E>>
|
impl_from_interceptor_context!(mut FinalizerInterceptorContextMut);
|
||||||
for FinalizerInterceptorContextMut<'a, I, O, E>
|
|
||||||
{
|
|
||||||
fn from(inner: &'a mut InterceptorContext<I, O, E>) -> Self {
|
|
||||||
Self { inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, I, O, E: Debug> FinalizerInterceptorContextMut<'a, I, O, E> {
|
impl<'a, I, O, E> FinalizerInterceptorContextMut<'a, I, O, E> {
|
||||||
/// Returns the operation input.
|
/// Returns the operation input.
|
||||||
pub fn input(&self) -> Option<&I> {
|
pub fn input(&self) -> Option<&I> {
|
||||||
self.inner.input.as_ref()
|
self.inner.input.as_ref()
|
||||||
|
|
|
@ -71,62 +71,110 @@ impl Storable for LoadedRequestBody {
|
||||||
type Storer = StoreReplace<Self>;
|
type Storer = StoreReplace<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(enableNewSmithyRuntimeLaunch): Make OrchestratorError adhere to the errors RFC
|
|
||||||
/// Errors that can occur while running the orchestrator.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
enum ErrorKind<E> {
|
||||||
pub enum OrchestratorError<E> {
|
|
||||||
/// An error occurred within an interceptor.
|
/// An error occurred within an interceptor.
|
||||||
Interceptor { err: InterceptorError },
|
Interceptor { source: InterceptorError },
|
||||||
/// An error returned by a service.
|
/// An error returned by a service.
|
||||||
Operation { err: E },
|
Operation { err: E },
|
||||||
/// An error that occurs when a request times out.
|
/// An error that occurs when a request times out.
|
||||||
Timeout { err: BoxError },
|
Timeout { source: BoxError },
|
||||||
/// An error that occurs when request dispatch fails.
|
/// An error that occurs when request dispatch fails.
|
||||||
Connector { err: ConnectorError },
|
Connector { source: ConnectorError },
|
||||||
/// An error that occurs when a response can't be deserialized.
|
/// An error that occurs when a response can't be deserialized.
|
||||||
Response { err: BoxError },
|
Response { source: BoxError },
|
||||||
/// A general orchestrator error.
|
/// A general orchestrator error.
|
||||||
Other { err: BoxError },
|
Other { source: BoxError },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Debug> OrchestratorError<E> {
|
/// Errors that can occur while running the orchestrator.
|
||||||
/// Create a new `OrchestratorError` from a [`BoxError`].
|
#[derive(Debug)]
|
||||||
pub fn other(err: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
|
pub struct OrchestratorError<E> {
|
||||||
let err = err.into();
|
kind: ErrorKind<E>,
|
||||||
Self::Other { err }
|
}
|
||||||
|
|
||||||
|
impl<E> OrchestratorError<E> {
|
||||||
|
/// Create a new `OrchestratorError` from the given source.
|
||||||
|
pub fn other(source: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ErrorKind::Other {
|
||||||
|
source: source.into(),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `OrchestratorError` from an error received from a service.
|
/// Create an operation error.
|
||||||
pub fn operation(err: E) -> Self {
|
pub fn operation(err: E) -> Self {
|
||||||
Self::Operation { err }
|
Self {
|
||||||
|
kind: ErrorKind::Operation { err },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`].
|
/// True if the underlying error is an operation error.
|
||||||
pub fn interceptor(err: InterceptorError) -> Self {
|
pub fn is_operation_error(&self) -> bool {
|
||||||
Self::Interceptor { err }
|
matches!(self.kind, ErrorKind::Operation { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `OrchestratorError::Timeout` from a [`BoxError`].
|
/// Return this orchestrator error as an operation error if possible.
|
||||||
pub fn timeout(err: BoxError) -> Self {
|
|
||||||
Self::Timeout { err }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `OrchestratorError::Response` from a [`BoxError`].
|
|
||||||
pub fn response(err: BoxError) -> Self {
|
|
||||||
Self::Response { err }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `OrchestratorError::Connector` from a [`ConnectorError`].
|
|
||||||
pub fn connector(err: ConnectorError) -> Self {
|
|
||||||
Self::Connector { err }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the `OrchestratorError` into `Some` operation specific error if it is one. Otherwise,
|
|
||||||
/// return `None`.
|
|
||||||
pub fn as_operation_error(&self) -> Option<&E> {
|
pub fn as_operation_error(&self) -> Option<&E> {
|
||||||
match self {
|
match &self.kind {
|
||||||
Self::Operation { err } => Some(err),
|
ErrorKind::Operation { err } => Some(err),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an interceptor error with the given source.
|
||||||
|
pub fn interceptor(source: InterceptorError) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ErrorKind::Interceptor { source },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the underlying error is an interceptor error.
|
||||||
|
pub fn is_interceptor_error(&self) -> bool {
|
||||||
|
matches!(self.kind, ErrorKind::Interceptor { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a timeout error with the given source.
|
||||||
|
pub fn timeout(source: BoxError) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ErrorKind::Timeout { source },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the underlying error is a timeout error.
|
||||||
|
pub fn is_timeout_error(&self) -> bool {
|
||||||
|
matches!(self.kind, ErrorKind::Timeout { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a response error with the given source.
|
||||||
|
pub fn response(source: BoxError) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ErrorKind::Response { source },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the underlying error is a response error.
|
||||||
|
pub fn is_response_error(&self) -> bool {
|
||||||
|
matches!(self.kind, ErrorKind::Response { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a connector error with the given source.
|
||||||
|
pub fn connector(source: ConnectorError) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ErrorKind::Connector { source },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the underlying error is a [`ConnectorError`].
|
||||||
|
pub fn is_connector_error(&self) -> bool {
|
||||||
|
matches!(self.kind, ErrorKind::Connector { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return this orchestrator error as a connector error if possible.
|
||||||
|
pub fn as_connector_error(&self) -> Option<&ConnectorError> {
|
||||||
|
match &self.kind {
|
||||||
|
ErrorKind::Connector { source } => Some(source),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,34 +185,36 @@ impl<E: Debug> OrchestratorError<E> {
|
||||||
phase: &Phase,
|
phase: &Phase,
|
||||||
response: Option<HttpResponse>,
|
response: Option<HttpResponse>,
|
||||||
) -> SdkError<E, HttpResponse> {
|
) -> SdkError<E, HttpResponse> {
|
||||||
match self {
|
match self.kind {
|
||||||
Self::Interceptor { err } => {
|
ErrorKind::Interceptor { source } => {
|
||||||
use Phase::*;
|
use Phase::*;
|
||||||
match phase {
|
match phase {
|
||||||
BeforeSerialization | Serialization => SdkError::construction_failure(err),
|
BeforeSerialization | Serialization => SdkError::construction_failure(source),
|
||||||
BeforeTransmit | Transmit => match response {
|
BeforeTransmit | Transmit => match response {
|
||||||
Some(response) => SdkError::response_error(err, response),
|
Some(response) => SdkError::response_error(source, response),
|
||||||
None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)),
|
None => {
|
||||||
|
SdkError::dispatch_failure(ConnectorError::other(source.into(), None))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
||||||
SdkError::response_error(err, response.expect("phase has a response"))
|
SdkError::response_error(source, response.expect("phase has a response"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Operation { err } => {
|
ErrorKind::Operation { err } => {
|
||||||
debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase.");
|
debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase.");
|
||||||
SdkError::service_error(err, response.expect("phase has a response"))
|
SdkError::service_error(err, response.expect("phase has a response"))
|
||||||
}
|
}
|
||||||
Self::Connector { err } => SdkError::dispatch_failure(err),
|
ErrorKind::Connector { source } => SdkError::dispatch_failure(source),
|
||||||
Self::Timeout { err } => SdkError::timeout_error(err),
|
ErrorKind::Timeout { source } => SdkError::timeout_error(source),
|
||||||
Self::Response { err } => SdkError::response_error(err, response.unwrap()),
|
ErrorKind::Response { source } => SdkError::response_error(source, response.unwrap()),
|
||||||
Self::Other { err } => {
|
ErrorKind::Other { source } => {
|
||||||
use Phase::*;
|
use Phase::*;
|
||||||
match phase {
|
match phase {
|
||||||
BeforeSerialization | Serialization => SdkError::construction_failure(err),
|
BeforeSerialization | Serialization => SdkError::construction_failure(source),
|
||||||
BeforeTransmit | Transmit => convert_dispatch_error(err, response),
|
BeforeTransmit | Transmit => convert_dispatch_error(source, response),
|
||||||
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
||||||
SdkError::response_error(err, response.expect("phase has a response"))
|
SdkError::response_error(source, response.expect("phase has a response"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,12 +252,3 @@ impl From<TypeErasedError> for OrchestratorError<TypeErasedError> {
|
||||||
Self::operation(err)
|
Self::operation(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> From<aws_smithy_http::byte_stream::error::Error> for OrchestratorError<E>
|
|
||||||
where
|
|
||||||
E: Debug + std::error::Error + 'static,
|
|
||||||
{
|
|
||||||
fn from(err: aws_smithy_http::byte_stream::error::Error) -> Self {
|
|
||||||
Self::other(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#![warn(
|
#![warn(
|
||||||
// TODO(enableNewSmithyRuntimeLaunch): Add in remaining missing docs
|
missing_docs,
|
||||||
// missing_docs,
|
|
||||||
rustdoc::missing_crate_level_docs,
|
rustdoc::missing_crate_level_docs,
|
||||||
unreachable_pub,
|
unreachable_pub,
|
||||||
rust_2018_idioms
|
rust_2018_idioms
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl Interceptor for ConnectionPoisoningInterceptor {
|
||||||
.ok_or("retry classifiers are required for connection poisoning to work")?;
|
.ok_or("retry classifiers are required for connection poisoning to work")?;
|
||||||
|
|
||||||
let error_is_transient = retry_classifiers
|
let error_is_transient = retry_classifiers
|
||||||
.classify_retry(context.into_inner())
|
.classify_retry(context.inner_mut())
|
||||||
.map(|reason| reason == RetryReason::Error(ErrorKind::TransientError))
|
.map(|reason| reason == RetryReason::Error(ErrorKind::TransientError))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let connection_poisoning_is_enabled =
|
let connection_poisoning_is_enabled =
|
||||||
|
|
|
@ -5,15 +5,246 @@
|
||||||
|
|
||||||
use aws_smithy_http::body::SdkBody;
|
use aws_smithy_http::body::SdkBody;
|
||||||
use aws_smithy_runtime_api::box_error::BoxError;
|
use aws_smithy_runtime_api::box_error::BoxError;
|
||||||
use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut;
|
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||||
use aws_smithy_runtime_api::client::interceptors::Interceptor;
|
BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut,
|
||||||
|
FinalizerInterceptorContextMut, FinalizerInterceptorContextRef,
|
||||||
|
};
|
||||||
|
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||||
|
Error, Input, InterceptorContext, Output,
|
||||||
|
};
|
||||||
|
use aws_smithy_runtime_api::client::interceptors::{
|
||||||
|
Interceptor, InterceptorError, SharedInterceptor,
|
||||||
|
};
|
||||||
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
|
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
|
||||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||||
use aws_smithy_types::config_bag::ConfigBag;
|
use aws_smithy_types::config_bag::ConfigBag;
|
||||||
|
use aws_smithy_types::error::display::DisplayErrorContext;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
macro_rules! interceptor_impl_fn {
|
||||||
|
(mut $interceptor:ident) => {
|
||||||
|
pub(crate) fn $interceptor(
|
||||||
|
self,
|
||||||
|
ctx: &mut InterceptorContext,
|
||||||
|
runtime_components: &RuntimeComponents,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
tracing::trace!(concat!(
|
||||||
|
"running `",
|
||||||
|
stringify!($interceptor),
|
||||||
|
"` interceptors"
|
||||||
|
));
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let mut ctx = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) =
|
||||||
|
interceptor.$interceptor(&mut ctx, runtime_components, cfg)
|
||||||
|
{
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::$interceptor)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(ref $interceptor:ident) => {
|
||||||
|
pub(crate) fn $interceptor(
|
||||||
|
self,
|
||||||
|
ctx: &InterceptorContext,
|
||||||
|
runtime_components: &RuntimeComponents,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let ctx = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg)
|
||||||
|
{
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::$interceptor)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Interceptors<I> {
|
||||||
|
interceptors: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Interceptors<I>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = SharedInterceptor>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(interceptors: I) -> Self {
|
||||||
|
Self { interceptors }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_iter(self) -> impl Iterator<Item = ConditionallyEnabledInterceptor> {
|
||||||
|
self.interceptors.map(ConditionallyEnabledInterceptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_before_execution(
|
||||||
|
self,
|
||||||
|
operation: bool,
|
||||||
|
ctx: &InterceptorContext<Input, Output, Error>,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
tracing::trace!(
|
||||||
|
"running {} `read_before_execution` interceptors",
|
||||||
|
if operation { "operation" } else { "client" }
|
||||||
|
);
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) {
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::read_before_execution)
|
||||||
|
}
|
||||||
|
|
||||||
|
interceptor_impl_fn!(mut modify_before_serialization);
|
||||||
|
interceptor_impl_fn!(ref read_before_serialization);
|
||||||
|
interceptor_impl_fn!(ref read_after_serialization);
|
||||||
|
interceptor_impl_fn!(mut modify_before_retry_loop);
|
||||||
|
interceptor_impl_fn!(ref read_before_attempt);
|
||||||
|
interceptor_impl_fn!(mut modify_before_signing);
|
||||||
|
interceptor_impl_fn!(ref read_before_signing);
|
||||||
|
interceptor_impl_fn!(ref read_after_signing);
|
||||||
|
interceptor_impl_fn!(mut modify_before_transmit);
|
||||||
|
interceptor_impl_fn!(ref read_before_transmit);
|
||||||
|
interceptor_impl_fn!(ref read_after_transmit);
|
||||||
|
interceptor_impl_fn!(mut modify_before_deserialization);
|
||||||
|
interceptor_impl_fn!(ref read_before_deserialization);
|
||||||
|
interceptor_impl_fn!(ref read_after_deserialization);
|
||||||
|
|
||||||
|
pub(crate) fn modify_before_attempt_completion(
|
||||||
|
self,
|
||||||
|
ctx: &mut InterceptorContext<Input, Output, Error>,
|
||||||
|
runtime_components: &RuntimeComponents,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
tracing::trace!("running `modify_before_attempt_completion` interceptors");
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) =
|
||||||
|
interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg)
|
||||||
|
{
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::modify_before_attempt_completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_after_attempt(
|
||||||
|
self,
|
||||||
|
ctx: &InterceptorContext<Input, Output, Error>,
|
||||||
|
runtime_components: &RuntimeComponents,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
tracing::trace!("running `read_after_attempt` interceptors");
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let ctx: FinalizerInterceptorContextRef<'_> = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) =
|
||||||
|
interceptor.read_after_attempt(&ctx, runtime_components, cfg)
|
||||||
|
{
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::read_after_attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn modify_before_completion(
|
||||||
|
self,
|
||||||
|
ctx: &mut InterceptorContext<Input, Output, Error>,
|
||||||
|
runtime_components: &RuntimeComponents,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
tracing::trace!("running `modify_before_completion` interceptors");
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) =
|
||||||
|
interceptor.modify_before_completion(&mut ctx, runtime_components, cfg)
|
||||||
|
{
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::modify_before_completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_after_execution(
|
||||||
|
self,
|
||||||
|
ctx: &InterceptorContext<Input, Output, Error>,
|
||||||
|
runtime_components: &RuntimeComponents,
|
||||||
|
cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), InterceptorError> {
|
||||||
|
tracing::trace!("running `read_after_execution` interceptors");
|
||||||
|
let mut result: Result<(), BoxError> = Ok(());
|
||||||
|
let ctx: FinalizerInterceptorContextRef<'_> = ctx.into();
|
||||||
|
for interceptor in self.into_iter() {
|
||||||
|
if let Some(interceptor) = interceptor.if_enabled(cfg) {
|
||||||
|
if let Err(new_error) =
|
||||||
|
interceptor.read_after_execution(&ctx, runtime_components, cfg)
|
||||||
|
{
|
||||||
|
if let Err(last_error) = result {
|
||||||
|
tracing::debug!("{}", DisplayErrorContext(&*last_error));
|
||||||
|
}
|
||||||
|
result = Err(new_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.map_err(InterceptorError::read_after_execution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A interceptor wrapper to conditionally enable the interceptor based on
|
||||||
|
/// [`DisableInterceptor`](aws_smithy_runtime_api::client::interceptors::DisableInterceptor)
|
||||||
|
struct ConditionallyEnabledInterceptor(SharedInterceptor);
|
||||||
|
impl ConditionallyEnabledInterceptor {
|
||||||
|
fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> {
|
||||||
|
if self.0.enabled(cfg) {
|
||||||
|
Some(self.0.as_ref())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct MapRequestInterceptor<F, E> {
|
pub struct MapRequestInterceptor<F, E> {
|
||||||
f: F,
|
f: F,
|
||||||
_phantom: PhantomData<E>,
|
_phantom: PhantomData<E>,
|
||||||
|
@ -86,3 +317,71 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "test-util"))]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use aws_smithy_runtime_api::box_error::BoxError;
|
||||||
|
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||||
|
BeforeTransmitInterceptorContextRef, Input, InterceptorContext,
|
||||||
|
};
|
||||||
|
use aws_smithy_runtime_api::client::interceptors::{
|
||||||
|
disable_interceptor, Interceptor, SharedInterceptor,
|
||||||
|
};
|
||||||
|
use aws_smithy_runtime_api::client::runtime_components::{
|
||||||
|
RuntimeComponents, RuntimeComponentsBuilder,
|
||||||
|
};
|
||||||
|
use aws_smithy_types::config_bag::ConfigBag;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TestInterceptor;
|
||||||
|
impl Interceptor for TestInterceptor {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_disable_interceptors() {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PanicInterceptor;
|
||||||
|
impl Interceptor for PanicInterceptor {
|
||||||
|
fn read_before_transmit(
|
||||||
|
&self,
|
||||||
|
_context: &BeforeTransmitInterceptorContextRef<'_>,
|
||||||
|
_rc: &RuntimeComponents,
|
||||||
|
_cfg: &mut ConfigBag,
|
||||||
|
) -> Result<(), BoxError> {
|
||||||
|
Err("boom".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let rc = RuntimeComponentsBuilder::for_tests()
|
||||||
|
.with_interceptor(SharedInterceptor::new(PanicInterceptor))
|
||||||
|
.with_interceptor(SharedInterceptor::new(TestInterceptor))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut cfg = ConfigBag::base();
|
||||||
|
let interceptors = Interceptors::new(rc.interceptors());
|
||||||
|
assert_eq!(
|
||||||
|
interceptors
|
||||||
|
.into_iter()
|
||||||
|
.filter(|i| i.if_enabled(&cfg).is_some())
|
||||||
|
.count(),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
Interceptors::new(rc.interceptors())
|
||||||
|
.read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg)
|
||||||
|
.expect_err("interceptor returns error");
|
||||||
|
cfg.interceptor_state()
|
||||||
|
.store_put(disable_interceptor::<PanicInterceptor>("test"));
|
||||||
|
assert_eq!(
|
||||||
|
Interceptors::new(rc.interceptors())
|
||||||
|
.into_iter()
|
||||||
|
.filter(|i| i.if_enabled(&cfg).is_some())
|
||||||
|
.count(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
// shouldn't error because interceptors won't run
|
||||||
|
Interceptors::new(rc.interceptors())
|
||||||
|
.read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg)
|
||||||
|
.expect("interceptor is now disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#![allow(unknown_lints)]
|
#![allow(unknown_lints)]
|
||||||
|
|
||||||
use self::auth::orchestrate_auth;
|
use self::auth::orchestrate_auth;
|
||||||
|
use crate::client::interceptors::Interceptors;
|
||||||
use crate::client::orchestrator::endpoints::orchestrate_endpoint;
|
use crate::client::orchestrator::endpoints::orchestrate_endpoint;
|
||||||
use crate::client::orchestrator::http::read_body;
|
use crate::client::orchestrator::http::read_body;
|
||||||
use crate::client::timeout::{MaybeTimeout, MaybeTimeoutConfig, TimeoutKind};
|
use crate::client::timeout::{MaybeTimeout, MaybeTimeoutConfig, TimeoutKind};
|
||||||
|
@ -19,7 +20,6 @@ use aws_smithy_runtime_api::client::connectors::HttpConnector;
|
||||||
use aws_smithy_runtime_api::client::interceptors::context::{
|
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||||
Error, Input, InterceptorContext, Output, RewindResult,
|
Error, Input, InterceptorContext, Output, RewindResult,
|
||||||
};
|
};
|
||||||
use aws_smithy_runtime_api::client::interceptors::Interceptors;
|
|
||||||
use aws_smithy_runtime_api::client::orchestrator::{
|
use aws_smithy_runtime_api::client::orchestrator::{
|
||||||
HttpResponse, LoadedRequestBody, OrchestratorError,
|
HttpResponse, LoadedRequestBody, OrchestratorError,
|
||||||
};
|
};
|
||||||
|
@ -200,7 +200,10 @@ async fn try_op(
|
||||||
debug!("loading request body into memory");
|
debug!("loading request body into memory");
|
||||||
let mut body = SdkBody::taken();
|
let mut body = SdkBody::taken();
|
||||||
mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut());
|
mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut());
|
||||||
let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes();
|
let loaded_body = halt_on_err!([ctx] =>
|
||||||
|
ByteStream::new(body).collect().await.map_err(OrchestratorError::other)
|
||||||
|
)
|
||||||
|
.into_bytes();
|
||||||
*ctx.request_mut().as_mut().expect("set above").body_mut() =
|
*ctx.request_mut().as_mut().expect("set above").body_mut() =
|
||||||
SdkBody::from(loaded_body.clone());
|
SdkBody::from(loaded_body.clone());
|
||||||
cfg.interceptor_state()
|
cfg.interceptor_state()
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
|
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
|
||||||
use aws_smithy_runtime_api::client::orchestrator::OrchestratorError;
|
|
||||||
use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason};
|
use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason};
|
||||||
use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind};
|
use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -77,17 +76,16 @@ where
|
||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
};
|
};
|
||||||
|
|
||||||
match error {
|
if error.is_response_error() || error.is_timeout_error() {
|
||||||
OrchestratorError::Response { .. } | OrchestratorError::Timeout { .. } => {
|
Some(RetryReason::Error(ErrorKind::TransientError))
|
||||||
|
} else if let Some(error) = error.as_connector_error() {
|
||||||
|
if error.is_timeout() || error.is_io() {
|
||||||
Some(RetryReason::Error(ErrorKind::TransientError))
|
Some(RetryReason::Error(ErrorKind::TransientError))
|
||||||
|
} else {
|
||||||
|
error.as_other().map(RetryReason::Error)
|
||||||
}
|
}
|
||||||
OrchestratorError::Connector { err } if err.is_timeout() || err.is_io() => {
|
} else {
|
||||||
Some(RetryReason::Error(ErrorKind::TransientError))
|
None
|
||||||
}
|
|
||||||
OrchestratorError::Connector { err } if err.is_other().is_some() => {
|
|
||||||
err.is_other().map(RetryReason::Error)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue