-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Fahad Zubair
committed
Oct 10, 2023
1 parent
8f918ed
commit 19c34c3
Showing
2 changed files
with
195 additions
and
0 deletions.
There are no files selected for viewing
179 changes: 179 additions & 0 deletions
179
examples/pokemon-service-client-usage/examples/interceptor-errors.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
use aws_smithy_http::{body::SdkBody, result::ConnectorError}; | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
/// This example demonstrates how different interceptor can use a property bag to pass | ||
/// state from one interceptor to the next. | ||
/// | ||
/// The example assumes that the Pokemon service is running on the localhost on TCP port 13734. | ||
/// Refer to the [README.md](https://github.com/awslabs/smithy-rs/tree/main/examples/pokemon-service-client-usage/README.md) | ||
/// file for instructions on how to launch the service locally. | ||
/// | ||
/// The example can be run using `cargo run --example use-config-bag`. | ||
/// | ||
use tracing::{debug, error, info}; | ||
|
||
use aws_smithy_client::SdkError; | ||
use pokemon_service_client::{config::Interceptor, Client as PokemonClient}; | ||
|
||
static BASE_URL: &str = "http://localhost:13734"; | ||
|
||
// Header to check that each request has this set. | ||
const HEADER_TO_CHECK: hyper::header::HeaderName = | ||
hyper::header::HeaderName::from_static("x-amzn-date"); | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
enum CustomErrors { | ||
#[error("A required header `{0}` has not been set on the request")] | ||
HeaderMissing(String), | ||
} | ||
|
||
#[derive(Debug, Default)] | ||
pub struct CheckHeaderInterceptor; | ||
|
||
impl CheckHeaderInterceptor { | ||
/// Creates a new `CheckHeaderInterceptor` | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
} | ||
|
||
impl Interceptor for CheckHeaderInterceptor { | ||
fn name(&self) -> &'static str { | ||
"CheckHeaderInterceptor" | ||
} | ||
|
||
fn read_before_signing( | ||
&self, | ||
context: &pokemon_service_client::config::interceptors::BeforeTransmitInterceptorContextRef< | ||
'_, | ||
>, | ||
_runtime_components: &pokemon_service_client::config::RuntimeComponents, | ||
_cfg: &mut aws_smithy_types::config_bag::ConfigBag, | ||
) -> Result<(), pokemon_service_client::error::BoxError> { | ||
if !context | ||
.request() | ||
.headers() | ||
.iter() | ||
.any(|(ref header_name, _)| *header_name == HEADER_TO_CHECK) | ||
{ | ||
debug!("Header not found in request, raising an error"); | ||
|
||
return Err(Box::new(CustomErrors::HeaderMissing( | ||
HEADER_TO_CHECK.to_string(), | ||
))); | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
/// Creates a new Smithy client that is configured to communicate with a locally running Pokemon service on TCP port 13734. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Basic usage: | ||
/// | ||
/// ``` | ||
/// let client = create_client(); | ||
/// ``` | ||
fn create_client() -> PokemonClient { | ||
// The generated client has a type config::Builder that can be used to build a Config, which | ||
// allows configuring endpoint-resolver, timeouts, retries etc. | ||
let config = pokemon_service_client::Config::builder() | ||
.endpoint_resolver(BASE_URL) | ||
.interceptor(CheckHeaderInterceptor {}) | ||
.build(); | ||
|
||
// Apply the configuration on the Client, and return that. | ||
PokemonClient::from_conf(config) | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
tracing_setup(); | ||
|
||
// Create a configured Smithy client. | ||
let client = create_client(); | ||
|
||
// Call an operation `get_server_statistics` on Pokemon service. | ||
let response_result = client.get_server_statistics().send().await; | ||
|
||
match response_result { | ||
Ok(response) => { | ||
info!(%BASE_URL, ?response, "Response received"); | ||
} | ||
Err(e) => match e { | ||
SdkError::ServiceError(ref e) => { | ||
error!(?e, "Service error"); | ||
} | ||
SdkError::DispatchFailure(_) => handle_dispatch_failure(e), | ||
some_error => { | ||
error!(?some_error, "A generic error occurred"); | ||
} | ||
}, | ||
} | ||
} | ||
|
||
fn handle_dispatch_failure<E>(error: SdkError<E, hyper::Response<SdkBody>>) | ||
where | ||
E: std::error::Error + Send + Sync + 'static, | ||
{ | ||
match error.into_source() { | ||
// Convert the DispatchFailure into a ConnectorError, and then convert that | ||
// into the CustomError that has been raised by the CheckHeaderInterceptor. | ||
Ok(dispatch_failure_source) => { | ||
match dispatch_failure_source.downcast::<ConnectorError>() { | ||
// ConnectorError can be due to Io, Timeout, User or Other. | ||
Ok(ce) => { | ||
// Any error raised by an Interceptor is available as the source of the ConnectorError. | ||
let connector_error = ce.into_source(); | ||
match connector_error.source() { | ||
// We can downcast from the source error to the CustomError that was raised | ||
// by the Interceptor. | ||
Some(source_of_error) => { | ||
match source_of_error.downcast_ref::<CustomErrors>() { | ||
Some(custom_error) => { | ||
// Specific match statements can be written here in case | ||
// you want to find out which variant was raised. | ||
error!("Request could not be dispatched. {}", custom_error); | ||
} | ||
None => { | ||
error!( | ||
?source_of_error, | ||
"A ConnectorError has occurred. Source:" | ||
) | ||
} | ||
} | ||
} | ||
None => { | ||
error!(?connector_error, "ConnectorError"); | ||
} | ||
} | ||
} | ||
Err(non_connect_error) => { | ||
error!(?non_connect_error, "Request failed to dispatch"); | ||
} | ||
} | ||
} | ||
Err(org_error) => { | ||
// The original error is returned when into_source() fails. | ||
error!(?org_error); | ||
} | ||
} | ||
} | ||
|
||
fn tracing_setup() { | ||
if std::env::var("RUST_LIB_BACKTRACE").is_err() { | ||
std::env::set_var("RUST_LIB_BACKTRACE", "1"); | ||
} | ||
let _ = color_eyre::install(); | ||
|
||
if std::env::var("RUST_LOG").is_err() { | ||
std::env::set_var("RUST_LOG", "info"); | ||
} | ||
tracing_subscriber::fmt::fmt() | ||
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) | ||
.init(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters