-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow for configuring interceptors on generic client (#2697)
## Description This PR allows users to pass-in interceptors to a generic client. Client-level configured interceptors are eventually added to `client_interceptors` and operation-level interceptors to `operation_interceptors`, both of which are fields in the `aws-smithy-runtime-api::client::interceptors::Interceptors`. The relevant code is generated only in the orchestrator mode. The SDK registers a default set of (client-level & operation-level) interceptors, and the passed-in interceptors by a user will run _after_ those default interceptors. ## Testing - Added integration tests `operation_interceptor_test` and `interceptor_priority` in `sra_test`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito <[email protected]> Co-authored-by: John DiSanti <[email protected]>
- Loading branch information
1 parent
14a4aae
commit 587e654
Showing
17 changed files
with
518 additions
and
87 deletions.
There are no files selected for viewing
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
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
120 changes: 120 additions & 0 deletions
120
aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.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,120 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
mod util; | ||
|
||
use aws_sdk_s3::config::{Credentials, Region}; | ||
use aws_sdk_s3::Client; | ||
use aws_smithy_client::dvr; | ||
use aws_smithy_client::dvr::MediaType; | ||
use aws_smithy_client::erase::DynConnector; | ||
use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; | ||
use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; | ||
use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; | ||
use aws_smithy_runtime_api::client::orchestrator::RequestTime; | ||
use aws_smithy_runtime_api::config_bag::ConfigBag; | ||
use std::time::{Duration, SystemTime, UNIX_EPOCH}; | ||
|
||
const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; | ||
|
||
#[tokio::test] | ||
async fn operation_interceptor_test() { | ||
tracing_subscriber::fmt::init(); | ||
|
||
let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); | ||
|
||
// Not setting `TestUserAgentInterceptor` here, expecting it to be set later by the | ||
// operation-level config. | ||
let config = aws_sdk_s3::Config::builder() | ||
.credentials_provider(Credentials::for_tests()) | ||
.region(Region::new("us-east-1")) | ||
.http_connector(DynConnector::new(conn.clone())) | ||
.build(); | ||
let client = Client::from_conf(config); | ||
let fixup = util::FixupPlugin { | ||
timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), | ||
}; | ||
|
||
let resp = dbg!( | ||
client | ||
.list_objects_v2() | ||
.config_override( | ||
aws_sdk_s3::Config::builder().interceptor(util::TestUserAgentInterceptor) | ||
) | ||
.bucket("test-bucket") | ||
.prefix("prefix~") | ||
.send_orchestrator_with_plugin(Some(fixup)) | ||
.await | ||
); | ||
let resp = resp.expect("valid e2e test"); | ||
assert_eq!(resp.name(), Some("test-bucket")); | ||
conn.full_validate(MediaType::Xml).await.expect("success") | ||
} | ||
|
||
#[derive(Debug)] | ||
struct RequestTimeResetInterceptor; | ||
impl Interceptor for RequestTimeResetInterceptor { | ||
fn modify_before_signing( | ||
&self, | ||
_context: &mut InterceptorContext<BeforeTransmit>, | ||
cfg: &mut ConfigBag, | ||
) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { | ||
cfg.set_request_time(RequestTime::new(UNIX_EPOCH)); | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
struct RequestTimeAdvanceInterceptor(Duration); | ||
impl Interceptor for RequestTimeAdvanceInterceptor { | ||
fn modify_before_signing( | ||
&self, | ||
_context: &mut InterceptorContext<BeforeTransmit>, | ||
cfg: &mut ConfigBag, | ||
) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { | ||
let request_time = cfg.request_time().unwrap(); | ||
let request_time = RequestTime::new(request_time.system_time() + self.0); | ||
cfg.set_request_time(request_time); | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
#[tokio::test] | ||
async fn interceptor_priority() { | ||
let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); | ||
|
||
// `RequestTimeResetInterceptor` will reset a `RequestTime` to `UNIX_EPOCH`, whose previous | ||
// value should be `SystemTime::now()` set by `FixupPlugin`. | ||
let config = aws_sdk_s3::Config::builder() | ||
.credentials_provider(Credentials::for_tests()) | ||
.region(Region::new("us-east-1")) | ||
.http_connector(DynConnector::new(conn.clone())) | ||
.interceptor(util::TestUserAgentInterceptor) | ||
.interceptor(RequestTimeResetInterceptor) | ||
.build(); | ||
let client = Client::from_conf(config); | ||
let fixup = util::FixupPlugin { | ||
timestamp: SystemTime::now(), | ||
}; | ||
|
||
// `RequestTimeAdvanceInterceptor` configured at the operation level should run after, | ||
// expecting the `RequestTime` to move forward by the specified amount since `UNIX_EPOCH`. | ||
let resp = dbg!( | ||
client | ||
.list_objects_v2() | ||
.config_override(aws_sdk_s3::Config::builder().interceptor( | ||
RequestTimeAdvanceInterceptor(Duration::from_secs(1624036048)) | ||
)) | ||
.bucket("test-bucket") | ||
.prefix("prefix~") | ||
.send_orchestrator_with_plugin(Some(fixup)) | ||
.await | ||
); | ||
let resp = resp.expect("valid e2e test"); | ||
assert_eq!(resp.name(), Some("test-bucket")); | ||
conn.full_validate(MediaType::Xml).await.expect("success") | ||
} |
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
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,56 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
use aws_http::user_agent::AwsUserAgent; | ||
use aws_runtime::invocation_id::InvocationId; | ||
use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; | ||
use aws_smithy_runtime_api::client::interceptors::{ | ||
Interceptor, InterceptorContext, InterceptorRegistrar, | ||
}; | ||
use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; | ||
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; | ||
use aws_smithy_runtime_api::config_bag::ConfigBag; | ||
use http::header::USER_AGENT; | ||
use http::{HeaderName, HeaderValue}; | ||
use std::time::SystemTime; | ||
|
||
pub const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); | ||
|
||
#[derive(Debug)] | ||
pub struct FixupPlugin { | ||
pub timestamp: SystemTime, | ||
} | ||
impl RuntimePlugin for FixupPlugin { | ||
fn configure( | ||
&self, | ||
cfg: &mut ConfigBag, | ||
_interceptors: &mut InterceptorRegistrar, | ||
) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { | ||
cfg.set_request_time(RequestTime::new(self.timestamp.clone())); | ||
cfg.put(InvocationId::for_tests()); | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct TestUserAgentInterceptor; | ||
impl Interceptor for TestUserAgentInterceptor { | ||
fn modify_before_signing( | ||
&self, | ||
context: &mut InterceptorContext<BeforeTransmit>, | ||
_cfg: &mut ConfigBag, | ||
) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { | ||
let headers = context.request_mut().headers_mut(); | ||
let user_agent = AwsUserAgent::for_tests(); | ||
// Overwrite user agent header values provided by `UserAgentInterceptor` | ||
headers.insert(USER_AGENT, HeaderValue::try_from(user_agent.ua_header())?); | ||
headers.insert( | ||
X_AMZ_USER_AGENT, | ||
HeaderValue::try_from(user_agent.aws_ua_header())?, | ||
); | ||
|
||
Ok(()) | ||
} | ||
} |
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
Oops, something went wrong.