mirror of https://github.com/smithy-lang/smithy-rs
Tidy up `aws-smithy-runtime-api` a bit (#2867)
This PR makes some progress towards documenting the `aws-smithy-runtime-api` crate, as well as some additional work to make it more stable: - Places the `client` module behind a `client` feature so that it will be possible to add a `server` module/feature in the future. - Deletes `ConfigBagAccessors`. - Renames auth types to reflect changes in the internal spec. - Moves `RequestAttempts` into the `client::retries` module. - Moves several types that were in floating around in `client::orchestrator` to their own modules. - Renames `Connector` to `HttpConnector`. - Changes `DynResponseDeserializer` to `SharedResponseDeserializer` for consistency with serialization, and also so that it could be moved into runtime components or config in the future (since those need to implement `Clone`). - Updates the identity and auth design doc. - Updates READMEs and crate-level documentation. - Fixes most, but not all, the missing documentation in the crate. - Hides the builder macros. ---- _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
e060135e01
commit
666c474f4a
|
@ -11,7 +11,6 @@ use aws_sigv4::http_request::SignableBody;
|
|||
use aws_smithy_http::body::SdkBody;
|
||||
use aws_smithy_http::byte_stream;
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||
BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut,
|
||||
};
|
||||
|
@ -126,7 +125,7 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor {
|
|||
// Request the request body to be loaded into memory immediately after serialization
|
||||
// so that it can be checksummed before signing and transmit
|
||||
cfg.interceptor_state()
|
||||
.set_loaded_request_body(LoadedRequestBody::Requested);
|
||||
.store_put(LoadedRequestBody::Requested);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ aws-sigv4 = { path = "../aws-sigv4" }
|
|||
aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" }
|
||||
aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true }
|
||||
aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" }
|
||||
aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" }
|
||||
aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" }
|
||||
aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", features = ["client"] }
|
||||
aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] }
|
||||
aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" }
|
||||
aws-types = { path = "../aws-types" }
|
||||
fastrand = "2.0.0"
|
||||
|
|
|
@ -10,7 +10,7 @@ use aws_sigv4::http_request::{
|
|||
};
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::auth::{
|
||||
AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner,
|
||||
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Signer,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver};
|
||||
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
|
||||
|
@ -79,18 +79,18 @@ impl StdError for SigV4SigningError {
|
|||
|
||||
/// SigV4 auth scheme.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SigV4HttpAuthScheme {
|
||||
signer: SigV4HttpRequestSigner,
|
||||
pub struct SigV4AuthScheme {
|
||||
signer: SigV4Signer,
|
||||
}
|
||||
|
||||
impl SigV4HttpAuthScheme {
|
||||
/// Creates a new `SigV4HttpAuthScheme`.
|
||||
impl SigV4AuthScheme {
|
||||
/// Creates a new `SigV4AuthScheme`.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for SigV4HttpAuthScheme {
|
||||
impl AuthScheme for SigV4AuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
SCHEME_ID
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ impl HttpAuthScheme for SigV4HttpAuthScheme {
|
|||
identity_resolvers.identity_resolver(self.scheme_id())
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
@ -174,11 +174,11 @@ impl Storable for SigV4OperationSigningConfig {
|
|||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
/// SigV4 HTTP request signer.
|
||||
/// SigV4 signer.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SigV4HttpRequestSigner;
|
||||
pub struct SigV4Signer;
|
||||
|
||||
impl SigV4HttpRequestSigner {
|
||||
impl SigV4Signer {
|
||||
/// Creates a new signer instance.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
|
@ -291,7 +291,7 @@ impl SigV4HttpRequestSigner {
|
|||
endpoint_config: AuthSchemeEndpointConfig<'_>,
|
||||
) -> Result<EndpointAuthSchemeConfig, SigV4SigningError> {
|
||||
let (mut signing_region_override, mut signing_service_override) = (None, None);
|
||||
if let Some(config) = endpoint_config.config().and_then(Document::as_object) {
|
||||
if let Some(config) = endpoint_config.as_document().and_then(Document::as_object) {
|
||||
use SigV4SigningError::BadTypeInEndpointAuthSchemeConfig as UnexpectedType;
|
||||
signing_region_override = match config.get("signingRegion") {
|
||||
Some(Document::String(s)) => Some(SigningRegion::from(Region::new(s.clone()))),
|
||||
|
@ -311,8 +311,8 @@ impl SigV4HttpRequestSigner {
|
|||
}
|
||||
}
|
||||
|
||||
impl HttpRequestSigner for SigV4HttpRequestSigner {
|
||||
fn sign_request(
|
||||
impl Signer for SigV4Signer {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
|
@ -550,15 +550,13 @@ mod tests {
|
|||
payload_override: None,
|
||||
},
|
||||
};
|
||||
SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now)
|
||||
.unwrap();
|
||||
SigV4Signer::signing_params(settings, &credentials, &operation_config, now).unwrap();
|
||||
assert!(!logs_contain(EXPIRATION_WARNING));
|
||||
|
||||
let mut settings = SigningSettings::default();
|
||||
settings.expires_in = Some(creds_expire_in + Duration::from_secs(10));
|
||||
|
||||
SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now)
|
||||
.unwrap();
|
||||
SigV4Signer::signing_params(settings, &credentials, &operation_config, now).unwrap();
|
||||
assert!(logs_contain(EXPIRATION_WARNING));
|
||||
}
|
||||
|
||||
|
@ -583,11 +581,10 @@ mod tests {
|
|||
);
|
||||
out
|
||||
});
|
||||
let config = AuthSchemeEndpointConfig::new(Some(&config));
|
||||
let config = AuthSchemeEndpointConfig::from(Some(&config));
|
||||
|
||||
let cfg = ConfigBag::of_layers(vec![layer]);
|
||||
let result =
|
||||
SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success");
|
||||
let result = SigV4Signer::extract_operation_config(config, &cfg).expect("success");
|
||||
|
||||
assert_eq!(
|
||||
result.region,
|
||||
|
@ -611,8 +608,7 @@ mod tests {
|
|||
let cfg = ConfigBag::of_layers(vec![layer]);
|
||||
let config = AuthSchemeEndpointConfig::empty();
|
||||
|
||||
let result =
|
||||
SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success");
|
||||
let result = SigV4Signer::extract_operation_config(config, &cfg).expect("success");
|
||||
|
||||
assert_eq!(
|
||||
result.region,
|
||||
|
|
|
@ -7,7 +7,7 @@ use aws_smithy_runtime::client::orchestrator::interceptors::ServiceClockSkew;
|
|||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut;
|
||||
use aws_smithy_runtime_api::client::interceptors::Interceptor;
|
||||
use aws_smithy_runtime_api::client::request_attempts::RequestAttempts;
|
||||
use aws_smithy_runtime_api::client::retries::RequestAttempts;
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
use aws_smithy_types::config_bag::ConfigBag;
|
||||
use aws_smithy_types::date_time::Format;
|
||||
|
|
|
@ -22,7 +22,6 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) :
|
|||
"AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent::AwsUserAgent"),
|
||||
"BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig),
|
||||
"ConfigBag" to RuntimeType.configBag(runtimeConfig),
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig),
|
||||
"http" to CargoDependency.Http.toType(),
|
||||
"InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig),
|
||||
"RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig),
|
||||
|
|
|
@ -364,21 +364,19 @@ class AwsPresignedFluentBuilderMethod(
|
|||
struct AlternatePresigningSerializerRuntimePlugin;
|
||||
impl #{RuntimePlugin} for AlternatePresigningSerializerRuntimePlugin {
|
||||
fn config(&self) -> #{Option}<#{FrozenLayer}> {
|
||||
use #{ConfigBagAccessors};
|
||||
let mut cfg = #{Layer}::new("presigning_serializer");
|
||||
cfg.set_request_serializer(#{SharedRequestSerializer}::new(#{AlternateSerializer}));
|
||||
cfg.store_put(#{SharedRequestSerializer}::new(#{AlternateSerializer}));
|
||||
#{Some}(cfg.freeze())
|
||||
}
|
||||
}
|
||||
""",
|
||||
*preludeScope,
|
||||
"AlternateSerializer" to alternateSerializer(operationShape),
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig),
|
||||
"FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"),
|
||||
"Layer" to smithyTypes.resolve("config_bag::Layer"),
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig),
|
||||
"SharedRequestSerializer" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig)
|
||||
.resolve("client::orchestrator::SharedRequestSerializer"),
|
||||
.resolve("client::ser_de::SharedRequestSerializer"),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait
|
|||
import software.amazon.smithy.model.knowledge.ServiceIndex
|
||||
import software.amazon.smithy.model.shapes.OperationShape
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection
|
||||
|
@ -33,9 +33,9 @@ class SigV4AuthDecorator : ClientCodegenDecorator {
|
|||
override fun authOptions(
|
||||
codegenContext: ClientCodegenContext,
|
||||
operationShape: OperationShape,
|
||||
baseAuthOptions: List<AuthOption>,
|
||||
): List<AuthOption> = baseAuthOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) {
|
||||
it + AuthOption.StaticAuthOption(SigV4Trait.ID) {
|
||||
baseAuthSchemeOptions: List<AuthSchemeOption>,
|
||||
): List<AuthSchemeOption> = baseAuthSchemeOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) {
|
||||
it + AuthSchemeOption.StaticAuthSchemeOption(SigV4Trait.ID) {
|
||||
rustTemplate(
|
||||
"#{scheme_id},",
|
||||
"scheme_id" to AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig)
|
||||
|
@ -69,10 +69,10 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext:
|
|||
val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig)
|
||||
arrayOf(
|
||||
"SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"),
|
||||
"SigV4HttpAuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4HttpAuthScheme"),
|
||||
"SigV4AuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4AuthScheme"),
|
||||
"SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"),
|
||||
"SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"),
|
||||
"SharedHttpAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedHttpAuthScheme"),
|
||||
"SharedAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedAuthScheme"),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -84,8 +84,8 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext:
|
|||
// enable the aws-runtime `sign-eventstream` feature
|
||||
addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol())
|
||||
}
|
||||
section.registerHttpAuthScheme(this) {
|
||||
rustTemplate("#{SharedHttpAuthScheme}::new(#{SigV4HttpAuthScheme}::new())", *codegenScope)
|
||||
section.registerAuthScheme(this) {
|
||||
rustTemplate("#{SharedAuthScheme}::new(#{SigV4AuthScheme}::new())", *codegenScope)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import software.amazon.smithy.model.shapes.ShapeId
|
|||
import software.amazon.smithy.model.shapes.ToShapeId
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization
|
||||
|
@ -63,9 +63,9 @@ class ServiceSpecificDecorator(
|
|||
override fun authOptions(
|
||||
codegenContext: ClientCodegenContext,
|
||||
operationShape: OperationShape,
|
||||
baseAuthOptions: List<AuthOption>,
|
||||
): List<AuthOption> = baseAuthOptions.maybeApply(codegenContext.serviceShape) {
|
||||
delegateTo.authOptions(codegenContext, operationShape, baseAuthOptions)
|
||||
baseAuthSchemeOptions: List<AuthSchemeOption>,
|
||||
): List<AuthSchemeOption> = baseAuthSchemeOptions.maybeApply(codegenContext.serviceShape) {
|
||||
delegateTo.authOptions(codegenContext, operationShape, baseAuthSchemeOptions)
|
||||
}
|
||||
|
||||
override fun builderCustomizations(
|
||||
|
|
|
@ -14,8 +14,8 @@ import software.amazon.smithy.model.traits.HttpBearerAuthTrait
|
|||
import software.amazon.smithy.model.traits.HttpDigestAuthTrait
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption.StaticAuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption.StaticAuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection
|
||||
|
@ -52,7 +52,7 @@ private fun codegenScope(runtimeConfig: RuntimeConfig): Array<Pair<String, Any>>
|
|||
"IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"),
|
||||
"Login" to smithyRuntimeApi.resolve("client::identity::http::Login"),
|
||||
"PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"),
|
||||
"SharedHttpAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedHttpAuthScheme"),
|
||||
"SharedAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedAuthScheme"),
|
||||
"SharedIdentityResolver" to smithyRuntimeApi.resolve("client::identity::SharedIdentityResolver"),
|
||||
"Token" to smithyRuntimeApi.resolve("client::identity::http::Token"),
|
||||
)
|
||||
|
@ -89,16 +89,16 @@ class HttpAuthDecorator : ClientCodegenDecorator {
|
|||
override fun authOptions(
|
||||
codegenContext: ClientCodegenContext,
|
||||
operationShape: OperationShape,
|
||||
baseAuthOptions: List<AuthOption>,
|
||||
): List<AuthOption> {
|
||||
baseAuthSchemeOptions: List<AuthSchemeOption>,
|
||||
): List<AuthSchemeOption> {
|
||||
val serviceIndex = ServiceIndex.of(codegenContext.model)
|
||||
val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape)
|
||||
val codegenScope = codegenScope(codegenContext.runtimeConfig)
|
||||
val options = ArrayList<AuthOption>()
|
||||
val options = ArrayList<AuthSchemeOption>()
|
||||
for (authScheme in authSchemes.keys) {
|
||||
fun addOption(schemeShapeId: ShapeId, name: String) {
|
||||
options.add(
|
||||
StaticAuthOption(
|
||||
StaticAuthSchemeOption(
|
||||
schemeShapeId,
|
||||
writable {
|
||||
rustTemplate("$name,", *codegenScope)
|
||||
|
@ -114,7 +114,7 @@ class HttpAuthDecorator : ClientCodegenDecorator {
|
|||
else -> {}
|
||||
}
|
||||
}
|
||||
return baseAuthOptions + options
|
||||
return baseAuthSchemeOptions + options
|
||||
}
|
||||
|
||||
override fun configCustomizations(
|
||||
|
@ -164,8 +164,8 @@ private class HttpAuthServiceRuntimePluginCustomization(
|
|||
when (section) {
|
||||
is ServiceRuntimePluginSection.RegisterRuntimeComponents -> {
|
||||
fun registerAuthScheme(scheme: Writable) {
|
||||
section.registerHttpAuthScheme(this) {
|
||||
rustTemplate("#{SharedHttpAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme)
|
||||
section.registerAuthScheme(this) {
|
||||
rustTemplate("#{SharedAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme)
|
||||
}
|
||||
}
|
||||
fun registerNamedAuthScheme(name: String) {
|
||||
|
|
|
@ -45,7 +45,7 @@ private class HttpConnectorConfigCustomization(
|
|||
"HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"),
|
||||
"Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"),
|
||||
"SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"),
|
||||
"SharedConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::SharedConnector"),
|
||||
"SharedHttpConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::SharedHttpConnector"),
|
||||
"TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"),
|
||||
)
|
||||
|
||||
|
@ -101,9 +101,9 @@ private class HttpConnectorConfigCustomization(
|
|||
http_connector
|
||||
.and_then(|c| c.connector(&connector_settings, sleep_impl.clone()))
|
||||
.or_else(|| #{default_connector}(&connector_settings, sleep_impl))
|
||||
.map(|c| #{SharedConnector}::new(#{DynConnectorAdapter}::new(c)));
|
||||
.map(|c| #{SharedHttpConnector}::new(#{DynConnectorAdapter}::new(c)));
|
||||
|
||||
resolver.runtime_components_mut().set_connector(connector);
|
||||
resolver.runtime_components_mut().set_http_connector(connector);
|
||||
}
|
||||
}
|
||||
""",
|
||||
|
@ -124,15 +124,9 @@ private class HttpConnectorConfigCustomization(
|
|||
if (runtimeMode.defaultToOrchestrator) {
|
||||
rustTemplate(
|
||||
"""
|
||||
// TODO(enableNewSmithyRuntimeCleanup): Remove this function
|
||||
/// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any.
|
||||
pub fn http_connector(&self) -> Option<&#{HttpConnector}> {
|
||||
self.config.load::<#{HttpConnector}>()
|
||||
}
|
||||
|
||||
/// Return the [`SharedConnector`](#{SharedConnector}) to use when making requests, if any.
|
||||
pub fn connector(&self) -> Option<#{SharedConnector}> {
|
||||
self.runtime_components.connector()
|
||||
/// Return the [`SharedHttpConnector`](#{SharedHttpConnector}) to use when making requests, if any.
|
||||
pub fn http_connector(&self) -> Option<#{SharedHttpConnector}> {
|
||||
self.runtime_components.http_connector()
|
||||
}
|
||||
""",
|
||||
*codegenScope,
|
||||
|
|
|
@ -8,7 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations
|
|||
import software.amazon.smithy.model.shapes.OperationShape
|
||||
import software.amazon.smithy.model.shapes.ShapeId
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
|
||||
|
@ -28,9 +28,9 @@ class NoAuthDecorator : ClientCodegenDecorator {
|
|||
override fun authOptions(
|
||||
codegenContext: ClientCodegenContext,
|
||||
operationShape: OperationShape,
|
||||
baseAuthOptions: List<AuthOption>,
|
||||
): List<AuthOption> = baseAuthOptions +
|
||||
AuthOption.StaticAuthOption(noAuthSchemeShapeId) {
|
||||
baseAuthSchemeOptions: List<AuthSchemeOption>,
|
||||
): List<AuthSchemeOption> = baseAuthSchemeOptions +
|
||||
AuthSchemeOption.StaticAuthSchemeOption(noAuthSchemeShapeId) {
|
||||
rustTemplate(
|
||||
"#{NO_AUTH_SCHEME_ID},",
|
||||
"NO_AUTH_SCHEME_ID" to noAuthModule(codegenContext).resolve("NO_AUTH_SCHEME_ID"),
|
||||
|
|
|
@ -26,14 +26,14 @@ import java.util.logging.Logger
|
|||
|
||||
typealias ClientProtocolMap = ProtocolMap<OperationGenerator, ClientCodegenContext>
|
||||
|
||||
sealed interface AuthOption {
|
||||
/** Auth scheme for the `StaticAuthOptionResolver` */
|
||||
data class StaticAuthOption(
|
||||
sealed interface AuthSchemeOption {
|
||||
/** Auth scheme for the `StaticAuthSchemeOptionResolver` */
|
||||
data class StaticAuthSchemeOption(
|
||||
val schemeShapeId: ShapeId,
|
||||
val constructor: Writable,
|
||||
) : AuthOption
|
||||
) : AuthSchemeOption
|
||||
|
||||
class CustomResolver(/* unimplemented */) : AuthOption
|
||||
class CustomResolver(/* unimplemented */) : AuthSchemeOption
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,8 +47,8 @@ interface ClientCodegenDecorator : CoreCodegenDecorator<ClientCodegenContext, Cl
|
|||
fun authOptions(
|
||||
codegenContext: ClientCodegenContext,
|
||||
operationShape: OperationShape,
|
||||
baseAuthOptions: List<AuthOption>,
|
||||
): List<AuthOption> = baseAuthOptions
|
||||
baseAuthSchemeOptions: List<AuthSchemeOption>,
|
||||
): List<AuthSchemeOption> = baseAuthSchemeOptions
|
||||
|
||||
fun configCustomizations(
|
||||
codegenContext: ClientCodegenContext,
|
||||
|
@ -110,8 +110,8 @@ open class CombinedClientCodegenDecorator(decorators: List<ClientCodegenDecorato
|
|||
override fun authOptions(
|
||||
codegenContext: ClientCodegenContext,
|
||||
operationShape: OperationShape,
|
||||
baseAuthOptions: List<AuthOption>,
|
||||
): List<AuthOption> = combineCustomizations(baseAuthOptions) { decorator, authOptions ->
|
||||
baseAuthSchemeOptions: List<AuthSchemeOption>,
|
||||
): List<AuthSchemeOption> = combineCustomizations(baseAuthSchemeOptions) { decorator, authOptions ->
|
||||
decorator.authOptions(codegenContext, operationShape, authOptions)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ internal class EndpointConfigCustomization(
|
|||
"OldSharedEndpointResolver" to types.sharedEndpointResolver,
|
||||
"Params" to typesGenerator.paramsStruct(),
|
||||
"Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"),
|
||||
"SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::SharedEndpointResolver"),
|
||||
"SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint::SharedEndpointResolver"),
|
||||
"SmithyResolver" to types.resolveEndpoint,
|
||||
)
|
||||
|
||||
|
|
|
@ -45,15 +45,12 @@ class EndpointParamsInterceptorGenerator(
|
|||
val runtimeApi = CargoDependency.smithyRuntimeApi(rc).toType()
|
||||
val interceptors = runtimeApi.resolve("client::interceptors")
|
||||
val orchestrator = runtimeApi.resolve("client::orchestrator")
|
||||
val smithyTypes = CargoDependency.smithyTypes(rc).toType()
|
||||
arrayOf(
|
||||
*preludeScope,
|
||||
"BoxError" to RuntimeType.boxError(rc),
|
||||
"ConfigBag" to RuntimeType.configBag(rc),
|
||||
"ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc)
|
||||
.resolve("client::config_bag_accessors::ConfigBagAccessors"),
|
||||
"ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"),
|
||||
"EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"),
|
||||
"EndpointResolverParams" to runtimeApi.resolve("client::endpoint::EndpointResolverParams"),
|
||||
"HttpRequest" to orchestrator.resolve("HttpRequest"),
|
||||
"HttpResponse" to orchestrator.resolve("HttpResponse"),
|
||||
"Interceptor" to RuntimeType.interceptor(rc),
|
||||
|
@ -82,7 +79,6 @@ class EndpointParamsInterceptorGenerator(
|
|||
context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>,
|
||||
cfg: &mut #{ConfigBag},
|
||||
) -> #{Result}<(), #{BoxError}> {
|
||||
use #{ConfigBagAccessors};
|
||||
let _input = context.input()
|
||||
.downcast_ref::<${operationInput.name}>()
|
||||
.ok_or("failed to downcast to ${operationInput.name}")?;
|
||||
|
@ -93,7 +89,7 @@ class EndpointParamsInterceptorGenerator(
|
|||
#{param_setters}
|
||||
.build()
|
||||
.map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?;
|
||||
cfg.interceptor_state().set_endpoint_resolver_params(#{EndpointResolverParams}::new(params));
|
||||
cfg.interceptor_state().store_put(#{EndpointResolverParams}::new(params));
|
||||
#{Ok}(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ class ConfigOverrideRuntimePluginGenerator(
|
|||
*RuntimeType.preludeScope,
|
||||
"Cow" to RuntimeType.Cow,
|
||||
"CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"),
|
||||
"ConfigBagAccessors" to runtimeApi.resolve("client::config_bag_accessors::ConfigBagAccessors"),
|
||||
"FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"),
|
||||
"InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"),
|
||||
"Layer" to smithyTypes.resolve("config_bag::Layer"),
|
||||
|
|
|
@ -7,7 +7,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators
|
|||
|
||||
import software.amazon.smithy.model.shapes.OperationShape
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator
|
||||
|
@ -85,7 +85,7 @@ open class OperationGenerator(
|
|||
private fun renderOperationStruct(
|
||||
operationWriter: RustWriter,
|
||||
operationShape: OperationShape,
|
||||
authOptions: List<AuthOption>,
|
||||
authSchemeOptions: List<AuthSchemeOption>,
|
||||
operationCustomizations: List<OperationCustomization>,
|
||||
) {
|
||||
val operationName = symbolProvider.toSymbol(operationShape).name
|
||||
|
@ -213,7 +213,7 @@ open class OperationGenerator(
|
|||
operationWriter,
|
||||
operationShape,
|
||||
operationName,
|
||||
authOptions,
|
||||
authSchemeOptions,
|
||||
operationCustomizations,
|
||||
)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import software.amazon.smithy.model.shapes.OperationShape
|
|||
import software.amazon.smithy.model.traits.OptionalAuthTrait
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customizations.noAuthSchemeShapeId
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
|
||||
|
@ -35,21 +35,20 @@ class OperationRuntimePluginGenerator(
|
|||
val smithyTypes = RuntimeType.smithyTypes(rc)
|
||||
arrayOf(
|
||||
*preludeScope,
|
||||
"AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"),
|
||||
"AuthSchemeOptionResolverParams" to runtimeApi.resolve("client::auth::AuthSchemeOptionResolverParams"),
|
||||
"BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig),
|
||||
"ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig),
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig),
|
||||
"Cow" to RuntimeType.Cow,
|
||||
"SharedAuthOptionResolver" to runtimeApi.resolve("client::auth::SharedAuthOptionResolver"),
|
||||
"DynResponseDeserializer" to runtimeApi.resolve("client::orchestrator::DynResponseDeserializer"),
|
||||
"FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"),
|
||||
"Layer" to smithyTypes.resolve("config_bag::Layer"),
|
||||
"RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"),
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig),
|
||||
"RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(codegenContext.runtimeConfig),
|
||||
"SharedRequestSerializer" to runtimeApi.resolve("client::orchestrator::SharedRequestSerializer"),
|
||||
"StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"),
|
||||
"StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"),
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig),
|
||||
"SharedAuthSchemeOptionResolver" to runtimeApi.resolve("client::auth::SharedAuthSchemeOptionResolver"),
|
||||
"SharedRequestSerializer" to runtimeApi.resolve("client::ser_de::SharedRequestSerializer"),
|
||||
"SharedResponseDeserializer" to runtimeApi.resolve("client::ser_de::SharedResponseDeserializer"),
|
||||
"StaticAuthSchemeOptionResolver" to runtimeApi.resolve("client::auth::static_resolver::StaticAuthSchemeOptionResolver"),
|
||||
"StaticAuthSchemeOptionResolverParams" to runtimeApi.resolve("client::auth::static_resolver::StaticAuthSchemeOptionResolverParams"),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -57,7 +56,7 @@ class OperationRuntimePluginGenerator(
|
|||
writer: RustWriter,
|
||||
operationShape: OperationShape,
|
||||
operationStructName: String,
|
||||
authOptions: List<AuthOption>,
|
||||
authSchemeOptions: List<AuthSchemeOption>,
|
||||
customizations: List<OperationCustomization>,
|
||||
) {
|
||||
writer.rustTemplate(
|
||||
|
@ -65,13 +64,12 @@ class OperationRuntimePluginGenerator(
|
|||
impl #{RuntimePlugin} for $operationStructName {
|
||||
fn config(&self) -> #{Option}<#{FrozenLayer}> {
|
||||
let mut cfg = #{Layer}::new(${operationShape.id.name.dq()});
|
||||
use #{ConfigBagAccessors} as _;
|
||||
|
||||
cfg.store_put(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer));
|
||||
cfg.store_put(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer));
|
||||
cfg.store_put(#{SharedResponseDeserializer}::new(${operationStructName}ResponseDeserializer));
|
||||
|
||||
${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */}
|
||||
cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new()));
|
||||
cfg.store_put(#{AuthSchemeOptionResolverParams}::new(#{StaticAuthSchemeOptionResolverParams}::new()));
|
||||
|
||||
#{additional_config}
|
||||
|
||||
|
@ -96,7 +94,7 @@ class OperationRuntimePluginGenerator(
|
|||
""",
|
||||
*codegenScope,
|
||||
*preludeScope,
|
||||
"auth_options" to generateAuthOptions(operationShape, authOptions),
|
||||
"auth_options" to generateAuthOptions(operationShape, authSchemeOptions),
|
||||
"additional_config" to writable {
|
||||
writeCustomizations(
|
||||
customizations,
|
||||
|
@ -130,20 +128,20 @@ class OperationRuntimePluginGenerator(
|
|||
|
||||
private fun generateAuthOptions(
|
||||
operationShape: OperationShape,
|
||||
authOptions: List<AuthOption>,
|
||||
authSchemeOptions: List<AuthSchemeOption>,
|
||||
): Writable = writable {
|
||||
if (authOptions.any { it is AuthOption.CustomResolver }) {
|
||||
throw IllegalStateException("AuthOption.CustomResolver is unimplemented")
|
||||
if (authSchemeOptions.any { it is AuthSchemeOption.CustomResolver }) {
|
||||
throw IllegalStateException("AuthSchemeOption.CustomResolver is unimplemented")
|
||||
} else {
|
||||
val authOptionsMap = authOptions.associate {
|
||||
val option = it as AuthOption.StaticAuthOption
|
||||
val authOptionsMap = authSchemeOptions.associate {
|
||||
val option = it as AuthSchemeOption.StaticAuthSchemeOption
|
||||
option.schemeShapeId to option
|
||||
}
|
||||
withBlockTemplate(
|
||||
"""
|
||||
.with_auth_option_resolver(#{Some}(
|
||||
#{SharedAuthOptionResolver}::new(
|
||||
#{StaticAuthOptionResolver}::new(vec![
|
||||
.with_auth_scheme_option_resolver(#{Some}(
|
||||
#{SharedAuthSchemeOptionResolver}::new(
|
||||
#{StaticAuthSchemeOptionResolver}::new(vec![
|
||||
""",
|
||||
"]))))",
|
||||
*codegenScope,
|
||||
|
|
|
@ -50,10 +50,10 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) {
|
|||
)
|
||||
}
|
||||
|
||||
fun registerHttpAuthScheme(writer: RustWriter, authScheme: Writable) {
|
||||
fun registerAuthScheme(writer: RustWriter, authScheme: Writable) {
|
||||
writer.rustTemplate(
|
||||
"""
|
||||
runtime_components.push_http_auth_scheme(#{auth_scheme});
|
||||
runtime_components.push_auth_scheme(#{auth_scheme});
|
||||
""",
|
||||
"auth_scheme" to authScheme,
|
||||
)
|
||||
|
|
|
@ -313,8 +313,7 @@ class ServiceConfigGenerator(
|
|||
*preludeScope,
|
||||
"BoxError" to RuntimeType.boxError(runtimeConfig),
|
||||
"CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"),
|
||||
"ConfigBag" to RuntimeType.configBag(runtimeConfig),
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig),
|
||||
"ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig),
|
||||
"Cow" to RuntimeType.Cow,
|
||||
"FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"),
|
||||
"Layer" to smithyTypes.resolve("config_bag::Layer"),
|
||||
|
|
|
@ -375,7 +375,7 @@ class DefaultProtocolTestGenerator(
|
|||
|
||||
let op = #{Operation}::new();
|
||||
let config = op.config().expect("the operation has config");
|
||||
let de = config.load::<#{DynResponseDeserializer}>().expect("the config must have a deserializer");
|
||||
let de = config.load::<#{SharedResponseDeserializer}>().expect("the config must have a deserializer");
|
||||
|
||||
let parsed = de.deserialize_streaming(&mut http_response);
|
||||
let parsed = parsed.unwrap_or_else(|| {
|
||||
|
@ -386,9 +386,9 @@ class DefaultProtocolTestGenerator(
|
|||
});
|
||||
""",
|
||||
"copy_from_slice" to RT.Bytes.resolve("copy_from_slice"),
|
||||
"DynResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::DynResponseDeserializer"),
|
||||
"SharedResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::SharedResponseDeserializer"),
|
||||
"Operation" to codegenContext.symbolProvider.toSymbol(operationShape),
|
||||
"ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::ResponseDeserializer"),
|
||||
"ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"),
|
||||
"RuntimePlugin" to RT.runtimePlugin(rc),
|
||||
"SdkBody" to RT.sdkBody(rc),
|
||||
)
|
||||
|
|
|
@ -37,7 +37,6 @@ class RequestSerializerGenerator(
|
|||
private val codegenScope by lazy {
|
||||
val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig)
|
||||
val interceptorContext = runtimeApi.resolve("client::interceptors::context")
|
||||
val orchestrator = runtimeApi.resolve("client::orchestrator")
|
||||
val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig)
|
||||
arrayOf(
|
||||
*preludeScope,
|
||||
|
@ -46,11 +45,11 @@ class RequestSerializerGenerator(
|
|||
"ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig),
|
||||
"header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"),
|
||||
"http" to RuntimeType.Http,
|
||||
"HttpRequest" to orchestrator.resolve("HttpRequest"),
|
||||
"HttpRequest" to runtimeApi.resolve("client::orchestrator::HttpRequest"),
|
||||
"HttpRequestBuilder" to RuntimeType.HttpRequestBuilder,
|
||||
"Input" to interceptorContext.resolve("Input"),
|
||||
"operation" to RuntimeType.operationModule(codegenContext.runtimeConfig),
|
||||
"RequestSerializer" to orchestrator.resolve("RequestSerializer"),
|
||||
"RequestSerializer" to runtimeApi.resolve("client::ser_de::RequestSerializer"),
|
||||
"SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig),
|
||||
"HeaderSerializationSettings" to RuntimeType.forInlineDependency(
|
||||
InlineDependency.serializationSettings(
|
||||
|
|
|
@ -44,7 +44,7 @@ class ResponseDeserializerGenerator(
|
|||
"Output" to interceptorContext.resolve("Output"),
|
||||
"OutputOrError" to interceptorContext.resolve("OutputOrError"),
|
||||
"OrchestratorError" to orchestrator.resolve("OrchestratorError"),
|
||||
"ResponseDeserializer" to orchestrator.resolve("ResponseDeserializer"),
|
||||
"ResponseDeserializer" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::ser_de::ResponseDeserializer"),
|
||||
"SdkBody" to RuntimeType.sdkBody(runtimeConfig),
|
||||
"SdkError" to RuntimeType.sdkError(runtimeConfig),
|
||||
"TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"),
|
||||
|
|
|
@ -187,7 +187,7 @@ class EndpointsDecoratorTest {
|
|||
use aws_smithy_async::rt::sleep::TokioSleep;
|
||||
use aws_smithy_client::never::NeverConnector;
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams;
|
||||
use aws_smithy_runtime_api::client::endpoint::EndpointResolverParams;
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
use aws_smithy_types::config_bag::ConfigBag;
|
||||
use aws_smithy_types::endpoint::Endpoint;
|
||||
|
|
|
@ -46,9 +46,8 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
|
|||
val runtimeConfig = clientCodegenContext.runtimeConfig
|
||||
val codegenScope = arrayOf(
|
||||
*preludeScope,
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig),
|
||||
"EndpointResolverParams" to RuntimeType.smithyRuntimeApi(runtimeConfig)
|
||||
.resolve("client::orchestrator::EndpointResolverParams"),
|
||||
.resolve("client::endpoint::EndpointResolverParams"),
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig),
|
||||
)
|
||||
rustCrate.testModule {
|
||||
|
@ -57,7 +56,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
|
|||
rustTemplate(
|
||||
"""
|
||||
use #{RuntimePlugin};
|
||||
use ::aws_smithy_runtime_api::client::orchestrator::EndpointResolver;
|
||||
use ::aws_smithy_runtime_api::client::endpoint::EndpointResolver;
|
||||
|
||||
let expected_url = "http://localhost:1234/";
|
||||
let client_config = crate::config::Config::builder().build();
|
||||
|
@ -93,7 +92,6 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
|
|||
val runtimeConfig = clientCodegenContext.runtimeConfig
|
||||
val codegenScope = arrayOf(
|
||||
*preludeScope,
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig),
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig),
|
||||
)
|
||||
rustCrate.testModule {
|
||||
|
@ -175,7 +173,6 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
|
|||
"AlwaysRetry" to RuntimeType.smithyRuntimeApi(runtimeConfig)
|
||||
.resolve("client::retries::AlwaysRetry"),
|
||||
"ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"),
|
||||
"ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig),
|
||||
"ErrorKind" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::ErrorKind"),
|
||||
"InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig),
|
||||
"Layer" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Layer"),
|
||||
|
@ -184,7 +181,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
|
|||
"RetryConfig" to RuntimeType.smithyTypes(clientCodegenContext.runtimeConfig)
|
||||
.resolve("retry::RetryConfig"),
|
||||
"RequestAttempts" to smithyRuntimeApiTestUtil(runtimeConfig).toType()
|
||||
.resolve("client::request_attempts::RequestAttempts"),
|
||||
.resolve("client::retries::RequestAttempts"),
|
||||
"RetryClassifiers" to RuntimeType.smithyRuntimeApi(runtimeConfig)
|
||||
.resolve("client::retries::RetryClassifiers"),
|
||||
"RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig),
|
||||
|
|
|
@ -102,15 +102,15 @@ private class TestOperationCustomization(
|
|||
.map_err(|e| #{OrchestratorError}::operation(#{TypedBox}::new(e).erase_error()))
|
||||
}
|
||||
}
|
||||
cfg.store_put(#{DynResponseDeserializer}::new(TestDeser));
|
||||
cfg.store_put(#{SharedResponseDeserializer}::new(TestDeser));
|
||||
""",
|
||||
*preludeScope,
|
||||
"DynResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::DynResponseDeserializer"),
|
||||
"SharedResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::SharedResponseDeserializer"),
|
||||
"Error" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Error"),
|
||||
"HttpResponse" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::HttpResponse"),
|
||||
"OrchestratorError" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::OrchestratorError"),
|
||||
"Output" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Output"),
|
||||
"ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::ResponseDeserializer"),
|
||||
"ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"),
|
||||
"TypedBox" to RT.smithyTypes(rc).resolve("type_erasure::TypedBox"),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -297,7 +297,9 @@ data class CargoDependency(
|
|||
|
||||
fun smithyQuery(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-query")
|
||||
fun smithyRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime")
|
||||
.withFeature("client")
|
||||
fun smithyRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime-api")
|
||||
.withFeature("client")
|
||||
fun smithyRuntimeApiTestUtil(runtimeConfig: RuntimeConfig) =
|
||||
smithyRuntimeApi(runtimeConfig).toDevDependency().withFeature("test-util")
|
||||
fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types")
|
||||
|
|
|
@ -336,8 +336,6 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null)
|
|||
|
||||
fun configBag(runtimeConfig: RuntimeConfig): RuntimeType =
|
||||
smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag")
|
||||
fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType =
|
||||
smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors")
|
||||
fun runtimeComponents(runtimeConfig: RuntimeConfig) =
|
||||
smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponents")
|
||||
fun runtimeComponentsBuilder(runtimeConfig: RuntimeConfig) =
|
||||
|
|
|
@ -34,17 +34,17 @@ There are two stages to identity and auth:
|
|||
First, let's establish the aspects of auth that can be configured from the model at codegen time.
|
||||
|
||||
- **Data**
|
||||
- **AuthOptionResolverParams:** parameters required to resolve auth options. These parameters are allowed
|
||||
- **AuthSchemeOptionResolverParams:** parameters required to resolve auth scheme options. These parameters are allowed
|
||||
to come from both the client config and the operation input structs.
|
||||
- **HttpAuthSchemes:** a list of auth schemes that can be used to sign HTTP requests. This information
|
||||
- **AuthSchemes:** a list of auth schemes that can be used to sign HTTP requests. This information
|
||||
comes directly from the service model.
|
||||
- **AuthSchemeProperties:** configuration from the auth scheme for the signer.
|
||||
- **IdentityResolvers:** list of available identity resolvers.
|
||||
- **Implementations**
|
||||
- **IdentityResolver:** resolves an identity for use in authentication.
|
||||
There can be multiple identity resolvers that need to be selected from.
|
||||
- **HttpRequestSigner:** a signing implementation that signs a HTTP request.
|
||||
- **AuthOptionResolver:** resolves a list of auth options for a given operation and its inputs.
|
||||
- **Signer:** a signing implementation that signs a HTTP request.
|
||||
- **AuthSchemeOptionResolver:** resolves a list of auth scheme options for a given operation and its inputs.
|
||||
|
||||
As it is undocumented (at time of writing), this document assumes that the code generator
|
||||
creates one service-level runtime plugin, and an operation-level runtime plugin per operation, hence
|
||||
|
@ -52,34 +52,34 @@ referred to as the service runtime plugin and operation runtime plugin.
|
|||
|
||||
The code generator emits code to add identity resolvers and HTTP auth schemes to the config bag
|
||||
in the service runtime plugin. It then emits code to register an interceptor in the operation runtime
|
||||
plugin that reads the operation input to generate the auth option resolver params (which also get added
|
||||
plugin that reads the operation input to generate the auth scheme option resolver params (which also get added
|
||||
to the config bag).
|
||||
|
||||
### The execution stage
|
||||
|
||||
At a high-level, the process of resolving an identity and signing a request looks as follows:
|
||||
|
||||
1. Retrieve the `AuthOptionResolverParams` from the config bag. The `AuthOptionResolverParams` allow client
|
||||
config and operation inputs to play a role in which auth option is selected.
|
||||
2. Retrieve the `AuthOptionResolver` from the config bag, and use it to resolve the auth options available
|
||||
with the `AuthOptionResolverParams`. The returned auth options are in priority order.
|
||||
1. Retrieve the `AuthSchemeOptionResolverParams` from the config bag. The `AuthSchemeOptionResolverParams` allow client
|
||||
config and operation inputs to play a role in which auth scheme option is selected.
|
||||
2. Retrieve the `AuthSchemeOptionResolver` from the config bag, and use it to resolve the auth scheme options available
|
||||
with the `AuthSchemeOptionResolverParams`. The returned auth scheme options are in priority order.
|
||||
3. Retrieve the `IdentityResolvers` list from the config bag.
|
||||
4. For each auth option:
|
||||
1. Attempt to find an HTTP auth scheme for that auth option in the config bag (from the `HttpAuthSchemes` list).
|
||||
4. For each auth scheme option:
|
||||
1. Attempt to find an HTTP auth scheme for that auth scheme option in the config bag (from the `AuthSchemes` list).
|
||||
2. If an auth scheme is found:
|
||||
1. Use the auth scheme to extract the correct identity resolver from the `IdentityResolvers` list.
|
||||
2. Retrieve the `HttpRequestSigner` implementation from the auth scheme.
|
||||
2. Retrieve the `Signer` implementation from the auth scheme.
|
||||
3. Use the `IdentityResolver` to resolve the identity needed for signing.
|
||||
4. Sign the request with the identity, and break out of the loop from step #4.
|
||||
|
||||
In general, it is assumed that if an HTTP auth scheme exists for an auth option, then an identity resolver
|
||||
also exists for that auth option. Otherwise, the auth option was configured incorrectly during codegen.
|
||||
In general, it is assumed that if an HTTP auth scheme exists for an auth scheme option, then an identity resolver
|
||||
also exists for that auth scheme option. Otherwise, the auth option was configured incorrectly during codegen.
|
||||
|
||||
How this looks in Rust
|
||||
----------------------
|
||||
|
||||
The client will use trait objects and dynamic dispatch for the `IdentityResolver`,
|
||||
`HttpRequestSigner`, and `AuthOptionResolver` implementations. Generics could potentially be used,
|
||||
`Signer`, and `AuthSchemeOptionResolver` implementations. Generics could potentially be used,
|
||||
but the number of generic arguments and trait bounds in the orchestrator would balloon to
|
||||
unmaintainable levels if each configurable implementation in it was made generic.
|
||||
|
||||
|
@ -87,38 +87,37 @@ These traits look like this:
|
|||
|
||||
```rust,ignore
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HttpAuthOption {
|
||||
pub struct AuthSchemeId {
|
||||
scheme_id: &'static str,
|
||||
properties: Arc<PropertyBag>,
|
||||
}
|
||||
|
||||
pub trait AuthOptionResolver: Send + Sync + Debug {
|
||||
fn resolve_auth_options<'a>(
|
||||
pub trait AuthSchemeOptionResolver: Send + Sync + Debug {
|
||||
fn resolve_auth_scheme_options<'a>(
|
||||
&'a self,
|
||||
params: &AuthOptionResolverParams,
|
||||
) -> Result<Cow<'a, [HttpAuthOption]>, BoxError>;
|
||||
params: &AuthSchemeOptionResolverParams,
|
||||
) -> Result<Cow<'a, [AuthSchemeId]>, BoxError>;
|
||||
}
|
||||
|
||||
pub trait IdentityResolver: Send + Sync + Debug {
|
||||
// `identity_properties` come from `HttpAuthOption::properties`
|
||||
fn resolve_identity(&self, identity_properties: &PropertyBag) -> BoxFallibleFut<Identity>;
|
||||
fn resolve_identity(&self, config: &ConfigBag) -> BoxFallibleFut<Identity>;
|
||||
}
|
||||
|
||||
pub trait HttpRequestSigner: Send + Sync + Debug {
|
||||
pub trait Signer: Send + Sync + Debug {
|
||||
/// Return a signed version of the given request using the given identity.
|
||||
///
|
||||
/// If the provided identity is incompatible with this signer, an error must be returned.
|
||||
fn sign_request(
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
// `signing_properties` come from `HttpAuthOption::properties`
|
||||
signing_properties: &PropertyBag,
|
||||
auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>,
|
||||
runtime_components: &RuntimeComponents,
|
||||
config_bag: &ConfigBag,
|
||||
) -> Result<(), BoxError>;
|
||||
}
|
||||
```
|
||||
|
||||
`IdentityResolver` and `HttpRequestSigner` implementations are both given an `Identity`, but
|
||||
`IdentityResolver` and `Signer` implementations are both given an `Identity`, but
|
||||
will need to understand what the concrete data type underlying that identity is. The `Identity` struct
|
||||
uses a `Arc<dyn Any>` to represent the actual identity data so that generics are not needed in
|
||||
the traits:
|
||||
|
@ -137,11 +136,13 @@ will use downcasting to access the identity data types they understand. For exam
|
|||
it might look like the following:
|
||||
|
||||
```rust,ignore
|
||||
fn sign_request(
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
signing_properties: &PropertyBag
|
||||
auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>,
|
||||
runtime_components: &RuntimeComponents,
|
||||
config_bag: &ConfigBag,
|
||||
) -> Result<(), BoxError> {
|
||||
let aws_credentials = identity.data::<Credentials>()
|
||||
.ok_or_else(|| "The SigV4 signer requires AWS credentials")?;
|
||||
|
@ -181,8 +182,8 @@ The `expiration` field is a special case that is allowed onto the `Identity` str
|
|||
cache implementations will always need to be aware of this piece of information, and having it as an `Option`
|
||||
still allows for non-expiring identities.
|
||||
|
||||
Ultimately, this design constrains `HttpRequestSigner` implementations to concrete types. There is no world
|
||||
where an `HttpRequestSigner` can operate across multiple unknown identity data types via trait, and that
|
||||
Ultimately, this design constrains `Signer` implementations to concrete types. There is no world
|
||||
where an `Signer` can operate across multiple unknown identity data types via trait, and that
|
||||
should be OK since the signer implementation can always be wrapped with an implementation that is aware
|
||||
of the concrete type provided by the identity resolver, and can do any necessary conversions.
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ repository = "https://github.com/awslabs/smithy-rs"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
client = []
|
||||
http-auth = ["dep:zeroize"]
|
||||
test-util = []
|
||||
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
# aws-smithy-runtime-api
|
||||
|
||||
**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.**
|
||||
APIs needed to configure and customize the Smithy generated code.
|
||||
|
||||
Lightweight crate with traits and types necessary to configure runtime logic in the `aws-smithy-runtime` crate.
|
||||
Most users will not need to use this crate directly as the most frequently used
|
||||
APIs are re-exported in the generated clients. However, this crate will be useful
|
||||
for anyone writing a library for others to use with their generated clients.
|
||||
|
||||
If you're needing to depend on this and you're not writing a library for Smithy
|
||||
generated clients, then please file an issue on [smithy-rs](https://github.com/awslabs/smithy-rs)
|
||||
as we likely missed re-exporting one of the APIs.
|
||||
|
||||
<!-- anchor_start:footer -->
|
||||
This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly.
|
||||
|
|
|
@ -3,35 +3,24 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
pub mod runtime_components;
|
||||
|
||||
/// Client orchestrator configuration accessors for the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag).
|
||||
pub mod config_bag_accessors;
|
||||
pub mod endpoint;
|
||||
|
||||
/// Smithy identity used by auth and signing.
|
||||
pub mod identity;
|
||||
|
||||
/// Smithy interceptors for smithy clients.
|
||||
///
|
||||
/// Interceptors are lifecycle hooks that can read/modify requests and responses.
|
||||
pub mod interceptors;
|
||||
|
||||
pub mod orchestrator;
|
||||
|
||||
/// Smithy code related to retry handling and token bucket.
|
||||
///
|
||||
/// This code defines when and how failed requests should be retried. It also defines the behavior
|
||||
/// used to limit the rate that requests are sent.
|
||||
pub mod retries;
|
||||
|
||||
/// Runtime plugin type definitions.
|
||||
pub mod runtime_components;
|
||||
|
||||
pub mod runtime_plugin;
|
||||
|
||||
/// Smithy auth runtime plugins
|
||||
pub mod auth;
|
||||
|
||||
/// A type to track the number of requests sent by the orchestrator for a given operation.
|
||||
pub mod request_attempts;
|
||||
|
||||
/// Smithy connectors and related code.
|
||||
pub mod connectors;
|
||||
|
||||
pub mod ser_de;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! APIs for request authentication.
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::identity::{Identity, SharedIdentityResolver};
|
||||
use crate::client::orchestrator::HttpRequest;
|
||||
|
@ -14,12 +16,18 @@ use std::borrow::Cow;
|
|||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Auth schemes for the HTTP `Authorization` header.
|
||||
#[cfg(feature = "http-auth")]
|
||||
pub mod http;
|
||||
|
||||
pub mod option_resolver;
|
||||
/// Static auth scheme option resolver.
|
||||
pub mod static_resolver;
|
||||
|
||||
/// New type around an auth scheme ID.
|
||||
///
|
||||
/// Each auth scheme must have a unique string identifier associated with it,
|
||||
/// which is used to refer to auth schemes by the auth scheme option resolver, and
|
||||
/// also used to select an identity resolver to use.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct AuthSchemeId {
|
||||
scheme_id: &'static str,
|
||||
|
@ -43,71 +51,116 @@ impl From<&'static str> for AuthSchemeId {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parameters needed to resolve auth scheme options.
|
||||
///
|
||||
/// Most generated clients will use the [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver),
|
||||
/// which doesn't require any parameters for resolution (and has its own empty params struct).
|
||||
///
|
||||
/// However, more complex auth scheme resolvers may need modeled parameters in order to resolve
|
||||
/// the auth scheme options. For those, this params struct holds a type erased box so that any
|
||||
/// kind of parameters can be contained within, and type casted by the auth scheme option resolver
|
||||
/// implementation.
|
||||
#[derive(Debug)]
|
||||
pub struct AuthOptionResolverParams(TypeErasedBox);
|
||||
pub struct AuthSchemeOptionResolverParams(TypeErasedBox);
|
||||
|
||||
impl AuthOptionResolverParams {
|
||||
impl AuthSchemeOptionResolverParams {
|
||||
/// Creates a new [`AuthSchemeOptionResolverParams`].
|
||||
pub fn new<T: fmt::Debug + Send + Sync + 'static>(params: T) -> Self {
|
||||
Self(TypedBox::new(params).erase())
|
||||
}
|
||||
|
||||
/// Returns the underlying parameters as the type `T` if they are that type.
|
||||
pub fn get<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
|
||||
self.0.downcast_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for AuthOptionResolverParams {
|
||||
impl Storable for AuthSchemeOptionResolverParams {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
pub trait AuthOptionResolver: Send + Sync + fmt::Debug {
|
||||
fn resolve_auth_options(
|
||||
/// Resolver for auth scheme options.
|
||||
///
|
||||
/// The orchestrator needs to select an auth scheme to sign requests with, and potentially
|
||||
/// from several different available auth schemes. Smithy models have a number of ways
|
||||
/// to specify which operations can use which auth schemes under which conditions, as
|
||||
/// documented in the [Smithy spec](https://smithy.io/2.0/spec/authentication-traits.html).
|
||||
///
|
||||
/// The orchestrator uses the auth scheme option resolver runtime component to resolve
|
||||
/// an ordered list of options that are available to choose from for a given request.
|
||||
/// This resolver can be a simple static list, such as with the
|
||||
/// [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver),
|
||||
/// or it can be a complex code generated resolver that incorporates parameters from both
|
||||
/// the model and the resolved endpoint.
|
||||
pub trait AuthSchemeOptionResolver: Send + Sync + fmt::Debug {
|
||||
/// Returns a list of available auth scheme options to choose from.
|
||||
fn resolve_auth_scheme_options(
|
||||
&self,
|
||||
params: &AuthOptionResolverParams,
|
||||
params: &AuthSchemeOptionResolverParams,
|
||||
) -> Result<Cow<'_, [AuthSchemeId]>, BoxError>;
|
||||
}
|
||||
|
||||
/// A shared auth scheme option resolver.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedAuthOptionResolver(Arc<dyn AuthOptionResolver>);
|
||||
pub struct SharedAuthSchemeOptionResolver(Arc<dyn AuthSchemeOptionResolver>);
|
||||
|
||||
impl SharedAuthOptionResolver {
|
||||
pub fn new(auth_option_resolver: impl AuthOptionResolver + 'static) -> Self {
|
||||
Self(Arc::new(auth_option_resolver))
|
||||
impl SharedAuthSchemeOptionResolver {
|
||||
/// Creates a new [`SharedAuthSchemeOptionResolver`].
|
||||
pub fn new(auth_scheme_option_resolver: impl AuthSchemeOptionResolver + 'static) -> Self {
|
||||
Self(Arc::new(auth_scheme_option_resolver))
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthOptionResolver for SharedAuthOptionResolver {
|
||||
fn resolve_auth_options(
|
||||
impl AuthSchemeOptionResolver for SharedAuthSchemeOptionResolver {
|
||||
fn resolve_auth_scheme_options(
|
||||
&self,
|
||||
params: &AuthOptionResolverParams,
|
||||
params: &AuthSchemeOptionResolverParams,
|
||||
) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
|
||||
(*self.0).resolve_auth_options(params)
|
||||
(*self.0).resolve_auth_scheme_options(params)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HttpAuthScheme: Send + Sync + fmt::Debug {
|
||||
/// An auth scheme.
|
||||
///
|
||||
/// Auth schemes have unique identifiers (the `scheme_id`),
|
||||
/// and provide an identity resolver and a signer.
|
||||
pub trait AuthScheme: Send + Sync + fmt::Debug {
|
||||
/// Returns the unique identifier associated with this auth scheme.
|
||||
///
|
||||
/// This identifier is used to refer to this auth scheme from the
|
||||
/// [`AuthSchemeOptionResolver`], and is also associated with
|
||||
/// identity resolvers in the config.
|
||||
fn scheme_id(&self) -> AuthSchemeId;
|
||||
|
||||
/// Returns the identity resolver that can resolve an identity for this scheme, if one is available.
|
||||
///
|
||||
/// The [`AuthScheme`] doesn't actually own an identity resolver. Rather, identity resolvers
|
||||
/// are configured as runtime components. The auth scheme merely chooses a compatible identity
|
||||
/// resolver from the runtime components via the [`GetIdentityResolver`] trait. The trait is
|
||||
/// given rather than the full set of runtime components to prevent complex resolution logic
|
||||
/// involving multiple components from taking place in this function, since that's not the
|
||||
/// intended use of this design.
|
||||
fn identity_resolver(
|
||||
&self,
|
||||
identity_resolvers: &dyn GetIdentityResolver,
|
||||
) -> Option<SharedIdentityResolver>;
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner;
|
||||
/// Returns the signing implementation for this auth scheme.
|
||||
fn signer(&self) -> &dyn Signer;
|
||||
}
|
||||
|
||||
/// Container for a shared HTTP auth scheme implementation.
|
||||
/// Container for a shared auth scheme implementation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedHttpAuthScheme(Arc<dyn HttpAuthScheme>);
|
||||
pub struct SharedAuthScheme(Arc<dyn AuthScheme>);
|
||||
|
||||
impl SharedHttpAuthScheme {
|
||||
/// Creates a new [`SharedHttpAuthScheme`] from the given auth scheme.
|
||||
pub fn new(auth_scheme: impl HttpAuthScheme + 'static) -> Self {
|
||||
impl SharedAuthScheme {
|
||||
/// Creates a new [`SharedAuthScheme`] from the given auth scheme.
|
||||
pub fn new(auth_scheme: impl AuthScheme + 'static) -> Self {
|
||||
Self(Arc::new(auth_scheme))
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for SharedHttpAuthScheme {
|
||||
impl AuthScheme for SharedAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
self.0.scheme_id()
|
||||
}
|
||||
|
@ -119,16 +172,17 @@ impl HttpAuthScheme for SharedHttpAuthScheme {
|
|||
self.0.identity_resolver(identity_resolvers)
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
self.0.request_signer()
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
self.0.signer()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HttpRequestSigner: Send + Sync + fmt::Debug {
|
||||
/// Return a signed version of the given request using the given identity.
|
||||
/// Signing implementation for an auth scheme.
|
||||
pub trait Signer: Send + Sync + fmt::Debug {
|
||||
/// Sign the given request with the given identity, components, and config.
|
||||
///
|
||||
/// If the provided identity is incompatible with this signer, an error must be returned.
|
||||
fn sign_request(
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
|
@ -140,23 +194,33 @@ pub trait HttpRequestSigner: Send + Sync + fmt::Debug {
|
|||
|
||||
/// Endpoint configuration for the selected auth scheme.
|
||||
///
|
||||
/// The configuration held by this struct originates from the endpoint rule set in the service model.
|
||||
///
|
||||
/// This struct gets added to the request state by the auth orchestrator.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AuthSchemeEndpointConfig<'a>(Option<&'a Document>);
|
||||
|
||||
impl<'a> AuthSchemeEndpointConfig<'a> {
|
||||
/// Creates a new [`AuthSchemeEndpointConfig`].
|
||||
pub fn new(config: Option<&'a Document>) -> Self {
|
||||
Self(config)
|
||||
}
|
||||
|
||||
/// Creates an empty AuthSchemeEndpointConfig.
|
||||
/// Creates an empty [`AuthSchemeEndpointConfig`].
|
||||
pub fn empty() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> Option<&'a Document> {
|
||||
/// Returns the endpoint configuration as a [`Document`].
|
||||
pub fn as_document(&self) -> Option<&'a Document> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Option<&'a Document>> for AuthSchemeEndpointConfig<'a> {
|
||||
fn from(value: Option<&'a Document>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> {
|
||||
fn from(value: &'a Document) -> Self {
|
||||
Self(Some(value))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,14 @@
|
|||
|
||||
use crate::client::auth::AuthSchemeId;
|
||||
|
||||
/// Auth scheme ID for HTTP API key based authentication.
|
||||
pub const HTTP_API_KEY_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-api-key-auth");
|
||||
|
||||
/// Auth scheme ID for HTTP Basic Auth.
|
||||
pub const HTTP_BASIC_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-basic-auth");
|
||||
|
||||
/// Auth scheme ID for HTTP Bearer Auth.
|
||||
pub const HTTP_BEARER_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-bearer-auth");
|
||||
|
||||
/// Auth scheme ID for HTTP Digest Auth.
|
||||
pub const HTTP_DIGEST_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-digest-auth");
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// New-type around a `Vec<HttpAuthOption>` that implements `AuthOptionResolver`.
|
||||
///
|
||||
/// This is useful for clients that don't require `AuthOptionResolverParams` to resolve auth options.
|
||||
#[derive(Debug)]
|
||||
pub struct StaticAuthOptionResolver {
|
||||
auth_options: Vec<AuthSchemeId>,
|
||||
}
|
||||
|
||||
impl StaticAuthOptionResolver {
|
||||
/// Creates a new instance of `StaticAuthOptionResolver`.
|
||||
pub fn new(auth_options: Vec<AuthSchemeId>) -> Self {
|
||||
Self { auth_options }
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthOptionResolver for StaticAuthOptionResolver {
|
||||
fn resolve_auth_options(
|
||||
&self,
|
||||
_params: &AuthOptionResolverParams,
|
||||
) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
|
||||
Ok(Cow::Borrowed(&self.auth_options))
|
||||
}
|
||||
}
|
||||
|
||||
/// Empty params to be used with [`StaticAuthOptionResolver`].
|
||||
#[derive(Debug)]
|
||||
pub struct StaticAuthOptionResolverParams;
|
||||
|
||||
impl StaticAuthOptionResolverParams {
|
||||
/// Creates a new `StaticAuthOptionResolverParams`.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StaticAuthOptionResolverParams> for AuthOptionResolverParams {
|
||||
fn from(params: StaticAuthOptionResolverParams) -> Self {
|
||||
AuthOptionResolverParams::new(params)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::auth::{AuthSchemeId, AuthSchemeOptionResolver, AuthSchemeOptionResolverParams};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// New-type around a `Vec<AuthSchemeId>` that implements `AuthSchemeOptionResolver`.
|
||||
#[derive(Debug)]
|
||||
pub struct StaticAuthSchemeOptionResolver {
|
||||
auth_scheme_options: Vec<AuthSchemeId>,
|
||||
}
|
||||
|
||||
impl StaticAuthSchemeOptionResolver {
|
||||
/// Creates a new instance of `StaticAuthSchemeOptionResolver`.
|
||||
pub fn new(auth_scheme_options: Vec<AuthSchemeId>) -> Self {
|
||||
Self {
|
||||
auth_scheme_options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthSchemeOptionResolver for StaticAuthSchemeOptionResolver {
|
||||
fn resolve_auth_scheme_options(
|
||||
&self,
|
||||
_params: &AuthSchemeOptionResolverParams,
|
||||
) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
|
||||
Ok(Cow::Borrowed(&self.auth_scheme_options))
|
||||
}
|
||||
}
|
||||
|
||||
/// Empty params to be used with [`StaticAuthSchemeOptionResolver`].
|
||||
#[derive(Debug)]
|
||||
pub struct StaticAuthSchemeOptionResolverParams;
|
||||
|
||||
impl StaticAuthSchemeOptionResolverParams {
|
||||
/// Creates a new `StaticAuthSchemeOptionResolverParams`.
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StaticAuthSchemeOptionResolverParams> for AuthSchemeOptionResolverParams {
|
||||
fn from(params: StaticAuthSchemeOptionResolverParams) -> Self {
|
||||
AuthSchemeOptionResolverParams::new(params)
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use crate::client::auth::AuthOptionResolverParams;
|
||||
use crate::client::orchestrator::{
|
||||
DynResponseDeserializer, EndpointResolverParams, LoadedRequestBody, ResponseDeserializer,
|
||||
SharedRequestSerializer, NOT_NEEDED,
|
||||
};
|
||||
use aws_smithy_types::config_bag::{CloneableLayer, ConfigBag, FrozenLayer, Layer};
|
||||
|
||||
// Place traits in a private module so that they can be used in the public API without being a part of the public API.
|
||||
mod internal {
|
||||
use aws_smithy_types::config_bag::{
|
||||
CloneableLayer, ConfigBag, FrozenLayer, Layer, Storable, Store, StoreAppend, StoreReplace,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Settable {
|
||||
fn unset<T: Send + Sync + Clone + Debug + 'static>(&mut self);
|
||||
|
||||
fn store_put<T>(&mut self, value: T)
|
||||
where
|
||||
T: Storable<Storer = StoreReplace<T>>;
|
||||
|
||||
fn store_append<T>(&mut self, item: T)
|
||||
where
|
||||
T: Storable<Storer = StoreAppend<T>>;
|
||||
}
|
||||
|
||||
impl Settable for Layer {
|
||||
fn unset<T: Send + Sync + Clone + Debug + 'static>(&mut self) {
|
||||
Layer::unset::<T>(self);
|
||||
}
|
||||
|
||||
fn store_put<T>(&mut self, value: T)
|
||||
where
|
||||
T: Storable<Storer = StoreReplace<T>>,
|
||||
{
|
||||
Layer::store_put(self, value);
|
||||
}
|
||||
|
||||
fn store_append<T>(&mut self, item: T)
|
||||
where
|
||||
T: Storable<Storer = StoreAppend<T>>,
|
||||
{
|
||||
Layer::store_append(self, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Gettable {
|
||||
fn load<T: Storable>(&self) -> <T::Storer as Store>::ReturnedType<'_>;
|
||||
}
|
||||
|
||||
impl Gettable for ConfigBag {
|
||||
fn load<T: Storable>(&self) -> <T::Storer as Store>::ReturnedType<'_> {
|
||||
ConfigBag::load::<T>(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Gettable for CloneableLayer {
|
||||
fn load<T: Storable>(&self) -> <T::Storer as Store>::ReturnedType<'_> {
|
||||
Layer::load::<T>(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Gettable for Layer {
|
||||
fn load<T: Storable>(&self) -> <T::Storer as Store>::ReturnedType<'_> {
|
||||
Layer::load::<T>(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Gettable for FrozenLayer {
|
||||
fn load<T: Storable>(&self) -> <T::Storer as Store>::ReturnedType<'_> {
|
||||
Layer::load::<T>(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use internal::{Gettable, Settable};
|
||||
|
||||
pub trait ConfigBagAccessors {
|
||||
fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams
|
||||
where
|
||||
Self: Gettable,
|
||||
{
|
||||
self.load::<AuthOptionResolverParams>()
|
||||
.expect("auth option resolver params must be set")
|
||||
}
|
||||
fn set_auth_option_resolver_params(
|
||||
&mut self,
|
||||
auth_option_resolver_params: AuthOptionResolverParams,
|
||||
) where
|
||||
Self: Settable,
|
||||
{
|
||||
self.store_put::<AuthOptionResolverParams>(auth_option_resolver_params);
|
||||
}
|
||||
|
||||
fn endpoint_resolver_params(&self) -> &EndpointResolverParams
|
||||
where
|
||||
Self: Gettable,
|
||||
{
|
||||
self.load::<EndpointResolverParams>()
|
||||
.expect("endpoint resolver params must be set")
|
||||
}
|
||||
|
||||
fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams)
|
||||
where
|
||||
Self: Settable,
|
||||
{
|
||||
self.store_put::<EndpointResolverParams>(endpoint_resolver_params);
|
||||
}
|
||||
|
||||
fn request_serializer(&self) -> SharedRequestSerializer
|
||||
where
|
||||
Self: Gettable,
|
||||
{
|
||||
self.load::<SharedRequestSerializer>()
|
||||
.expect("missing request serializer")
|
||||
.clone()
|
||||
}
|
||||
fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer)
|
||||
where
|
||||
Self: Settable,
|
||||
{
|
||||
self.store_put::<SharedRequestSerializer>(request_serializer);
|
||||
}
|
||||
|
||||
fn response_deserializer(&self) -> &dyn ResponseDeserializer
|
||||
where
|
||||
Self: Gettable,
|
||||
{
|
||||
self.load::<DynResponseDeserializer>()
|
||||
.expect("missing response deserializer")
|
||||
}
|
||||
fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer)
|
||||
where
|
||||
Self: Settable,
|
||||
{
|
||||
self.store_put::<DynResponseDeserializer>(response_deserializer);
|
||||
}
|
||||
|
||||
fn loaded_request_body(&self) -> &LoadedRequestBody
|
||||
where
|
||||
Self: Gettable,
|
||||
{
|
||||
self.load::<LoadedRequestBody>().unwrap_or(&NOT_NEEDED)
|
||||
}
|
||||
fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody)
|
||||
where
|
||||
Self: Settable,
|
||||
{
|
||||
self.store_put::<LoadedRequestBody>(loaded_request_body);
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigBagAccessors for ConfigBag {}
|
||||
impl ConfigBagAccessors for FrozenLayer {}
|
||||
impl ConfigBagAccessors for CloneableLayer {}
|
||||
impl ConfigBagAccessors for Layer {}
|
|
@ -7,20 +7,30 @@ use crate::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse};
|
|||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait Connector: Send + Sync + fmt::Debug {
|
||||
/// Trait with a `call` function that asynchronously converts a request into a response.
|
||||
///
|
||||
/// Ordinarily, a connector would use an underlying HTTP library such as [hyper](https://crates.io/crates/hyper),
|
||||
/// and any associated HTTPS implementation alongside it to service requests.
|
||||
///
|
||||
/// However, it can also be useful to create fake connectors implementing this trait
|
||||
/// for testing.
|
||||
pub trait HttpConnector: Send + Sync + fmt::Debug {
|
||||
/// Asynchronously converts a request into a response.
|
||||
fn call(&self, request: HttpRequest) -> BoxFuture<HttpResponse>;
|
||||
}
|
||||
|
||||
/// A shared [`HttpConnector`] implementation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedConnector(Arc<dyn Connector>);
|
||||
pub struct SharedHttpConnector(Arc<dyn HttpConnector>);
|
||||
|
||||
impl SharedConnector {
|
||||
pub fn new(connection: impl Connector + 'static) -> Self {
|
||||
impl SharedHttpConnector {
|
||||
/// Returns a new [`SharedHttpConnector`].
|
||||
pub fn new(connection: impl HttpConnector + 'static) -> Self {
|
||||
Self(Arc::new(connection))
|
||||
}
|
||||
}
|
||||
|
||||
impl Connector for SharedConnector {
|
||||
impl HttpConnector for SharedHttpConnector {
|
||||
fn call(&self, request: HttpRequest) -> BoxFuture<HttpResponse> {
|
||||
(*self.0).call(request)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! APIs needed to configure endpoint resolution for clients.
|
||||
|
||||
use crate::client::orchestrator::Future;
|
||||
use aws_smithy_types::config_bag::{Storable, StoreReplace};
|
||||
use aws_smithy_types::endpoint::Endpoint;
|
||||
use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Parameters originating from the Smithy endpoint ruleset required for endpoint resolution.
|
||||
///
|
||||
/// The actual endpoint parameters are code generated from the Smithy model, and thus,
|
||||
/// are not known to the runtime crates. Hence, this struct is really a new-type around
|
||||
/// a [`TypeErasedBox`] that holds the actual concrete parameters in it.
|
||||
#[derive(Debug)]
|
||||
pub struct EndpointResolverParams(TypeErasedBox);
|
||||
|
||||
impl EndpointResolverParams {
|
||||
/// Creates a new [`EndpointResolverParams`] from a concrete parameters instance.
|
||||
pub fn new<T: fmt::Debug + Send + Sync + 'static>(params: T) -> Self {
|
||||
Self(TypedBox::new(params).erase())
|
||||
}
|
||||
|
||||
/// Attempts to downcast the underlying concrete parameters to `T` and return it as a reference.
|
||||
pub fn get<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
|
||||
self.0.downcast_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for EndpointResolverParams {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
/// Configurable endpoint resolver implementation.
|
||||
pub trait EndpointResolver: Send + Sync + fmt::Debug {
|
||||
/// Asynchronously resolves an endpoint to use from the given endpoint parameters.
|
||||
fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future<Endpoint>;
|
||||
}
|
||||
|
||||
/// Shared endpoint resolver.
|
||||
///
|
||||
/// This is a simple shared ownership wrapper type for the [`EndpointResolver`] trait.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedEndpointResolver(Arc<dyn EndpointResolver>);
|
||||
|
||||
impl SharedEndpointResolver {
|
||||
/// Creates a new [`SharedEndpointResolver`].
|
||||
pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self {
|
||||
Self(Arc::new(endpoint_resolver))
|
||||
}
|
||||
}
|
||||
|
||||
impl EndpointResolver for SharedEndpointResolver {
|
||||
fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future<Endpoint> {
|
||||
self.0.resolve_endpoint(params)
|
||||
}
|
||||
}
|
|
@ -15,8 +15,19 @@ use std::time::SystemTime;
|
|||
#[cfg(feature = "http-auth")]
|
||||
pub mod http;
|
||||
|
||||
/// Resolves an identity for a request.
|
||||
/// Resolver for identities.
|
||||
///
|
||||
/// Every [`AuthScheme`](crate::client::auth::AuthScheme) has one or more compatible
|
||||
/// identity resolvers, which are selected from runtime components by the auth scheme
|
||||
/// implementation itself.
|
||||
///
|
||||
/// The identity resolver must return a [`Future`] with the resolved identity, or an error
|
||||
/// if resolution failed. There is no optionality for identity resolvers. The identity either
|
||||
/// resolves successfully, or it fails. The orchestrator will choose exactly one auth scheme
|
||||
/// to use, and thus, its chosen identity resolver is the only identity resolver that runs.
|
||||
/// There is no fallback to other auth schemes in the absense of an identity.
|
||||
pub trait IdentityResolver: Send + Sync + Debug {
|
||||
/// Asynchronously resolves an identity for a request using the given config.
|
||||
fn resolve_identity(&self, config_bag: &ConfigBag) -> Future<Identity>;
|
||||
}
|
||||
|
||||
|
@ -67,6 +78,17 @@ impl ConfiguredIdentityResolver {
|
|||
}
|
||||
}
|
||||
|
||||
/// An identity that can be used for authentication.
|
||||
///
|
||||
/// The [`Identity`] is a container for any arbitrary identity data that may be used
|
||||
/// by a [`Signer`](crate::client::auth::Signer) implementation. Under the hood, it
|
||||
/// has an `Arc<dyn Any>`, and it is the responsibility of the signer to downcast
|
||||
/// to the appropriate data type using the `data()` function.
|
||||
///
|
||||
/// The `Identity` also holds an optional expiration time, which may duplicate
|
||||
/// an expiration time on the identity data. This is because an `Arc<dyn Any>`
|
||||
/// can't be downcast to any arbitrary trait, and expiring identities are
|
||||
/// common enough to be built-in.
|
||||
#[derive(Clone)]
|
||||
pub struct Identity {
|
||||
data: Arc<dyn Any + Send + Sync>,
|
||||
|
@ -76,6 +98,7 @@ pub struct Identity {
|
|||
}
|
||||
|
||||
impl Identity {
|
||||
/// Creates a new identity with the given data and expiration time.
|
||||
pub fn new<T>(data: T, expiration: Option<SystemTime>) -> Self
|
||||
where
|
||||
T: Any + Debug + Send + Sync,
|
||||
|
@ -87,10 +110,12 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the raw identity data.
|
||||
pub fn data<T: Any + Debug + Send + Sync + 'static>(&self) -> Option<&T> {
|
||||
self.data.downcast_ref()
|
||||
}
|
||||
|
||||
/// Returns the expiration time for this identity, if any.
|
||||
pub fn expiration(&self) -> Option<&SystemTime> {
|
||||
self.expiration.as_ref()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Interceptors for clients.
|
||||
//!
|
||||
//! Interceptors are operation lifecycle hooks that can read/modify requests and responses.
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::interceptors::context::{
|
||||
AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut,
|
||||
|
|
|
@ -36,6 +36,7 @@ use std::fmt::Debug;
|
|||
use std::{fmt, mem};
|
||||
use tracing::{debug, error, trace};
|
||||
|
||||
// TODO(enableNewSmithyRuntimeLaunch): New-type `Input`/`Output`/`Error`
|
||||
pub type Input = TypeErasedBox;
|
||||
pub type Output = TypeErasedBox;
|
||||
pub type Error = TypeErasedError;
|
||||
|
|
|
@ -193,18 +193,22 @@ impl<'a, I, O, E: Debug> From<&'a InterceptorContext<I, O, E>>
|
|||
}
|
||||
|
||||
impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> {
|
||||
/// Returns the operation input.
|
||||
pub fn input(&self) -> Option<&I> {
|
||||
self.inner.input.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the serialized request.
|
||||
pub fn request(&self) -> Option<&Request> {
|
||||
self.inner.request.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the raw response.
|
||||
pub fn response(&self) -> Option<&Response> {
|
||||
self.inner.response.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the deserialized operation output or error.
|
||||
pub fn output_or_error(&self) -> Option<Result<&O, &OrchestratorError<E>>> {
|
||||
self.inner.output_or_error.as_ref().map(|o| o.as_ref())
|
||||
}
|
||||
|
@ -223,34 +227,42 @@ impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext<I, O, E>>
|
|||
}
|
||||
|
||||
impl<'a, I, O, E: Debug> FinalizerInterceptorContextMut<'a, I, O, E> {
|
||||
/// Returns the operation input.
|
||||
pub fn input(&self) -> Option<&I> {
|
||||
self.inner.input.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the serialized request.
|
||||
pub fn request(&self) -> Option<&Request> {
|
||||
self.inner.request.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the raw response.
|
||||
pub fn response(&self) -> Option<&Response> {
|
||||
self.inner.response.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the deserialized operation output or error.
|
||||
pub fn output_or_error(&self) -> Option<Result<&O, &OrchestratorError<E>>> {
|
||||
self.inner.output_or_error.as_ref().map(|o| o.as_ref())
|
||||
}
|
||||
|
||||
/// Mutably returns the operation input.
|
||||
pub fn input_mut(&mut self) -> Option<&mut I> {
|
||||
self.inner.input.as_mut()
|
||||
}
|
||||
|
||||
/// Mutably returns the serialized request.
|
||||
pub fn request_mut(&mut self) -> Option<&mut Request> {
|
||||
self.inner.request.as_mut()
|
||||
}
|
||||
|
||||
/// Mutably returns the raw response.
|
||||
pub fn response_mut(&mut self) -> Option<&mut Response> {
|
||||
self.inner.response.as_mut()
|
||||
}
|
||||
|
||||
/// Mutably returns the deserialized operation output or error.
|
||||
pub fn output_or_error_mut(&mut self) -> Option<&mut Result<O, OrchestratorError<E>>> {
|
||||
self.inner.output_or_error.as_mut()
|
||||
}
|
||||
|
|
|
@ -181,6 +181,7 @@ pub struct ContextAttachedError {
|
|||
}
|
||||
|
||||
impl ContextAttachedError {
|
||||
/// Creates a new `ContextAttachedError` with the given `context` and `source`.
|
||||
pub fn new(context: impl Into<String>, source: impl Into<BoxError>) -> Self {
|
||||
Self {
|
||||
context: context.into(),
|
||||
|
|
|
@ -3,132 +3,50 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Client request orchestration.
|
||||
//!
|
||||
//! The orchestrator handles the full request/response lifecycle including:
|
||||
//! - Request serialization
|
||||
//! - Endpoint resolution
|
||||
//! - Identity resolution
|
||||
//! - Signing
|
||||
//! - Request transmission with retry and timeouts
|
||||
//! - Response deserialization
|
||||
//!
|
||||
//! There are several hook points in the orchestration where [interceptors](crate::client::interceptors)
|
||||
//! can read and modify the input, request, response, or output/error.
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::interceptors::context::{Error, Input, Output};
|
||||
use crate::client::interceptors::context::phase::Phase;
|
||||
use crate::client::interceptors::InterceptorError;
|
||||
use aws_smithy_async::future::now_or_later::NowOrLater;
|
||||
use aws_smithy_http::body::SdkBody;
|
||||
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
||||
use aws_smithy_types::endpoint::Endpoint;
|
||||
use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox};
|
||||
use aws_smithy_http::result::{ConnectorError, SdkError};
|
||||
use aws_smithy_types::config_bag::{Storable, StoreReplace};
|
||||
use aws_smithy_types::type_erasure::TypeErasedError;
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Errors that can occur while running the orchestrator.
|
||||
mod error;
|
||||
|
||||
pub use error::OrchestratorError;
|
||||
|
||||
/// Type alias for the HTTP request type that the orchestrator uses.
|
||||
pub type HttpRequest = http::Request<SdkBody>;
|
||||
|
||||
/// Type alias for the HTTP response type that the orchestrator uses.
|
||||
pub type HttpResponse = http::Response<SdkBody>;
|
||||
|
||||
/// Type alias for boxed futures that are returned from several traits since async trait functions are not stable yet (as of 2023-07-21).
|
||||
///
|
||||
/// See [the Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) for
|
||||
/// more information on async functions in traits.
|
||||
pub type BoxFuture<T> = Pin<Box<dyn StdFuture<Output = Result<T, BoxError>> + Send>>;
|
||||
|
||||
/// Type alias for futures that are returned from several traits since async trait functions are not stable yet (as of 2023-07-21).
|
||||
///
|
||||
/// See [the Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) for
|
||||
/// more information on async functions in traits.
|
||||
pub type Future<T> = NowOrLater<Result<T, BoxError>, BoxFuture<T>>;
|
||||
|
||||
pub trait RequestSerializer: Send + Sync + fmt::Debug {
|
||||
fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result<HttpRequest, BoxError>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedRequestSerializer(Arc<dyn RequestSerializer>);
|
||||
|
||||
impl SharedRequestSerializer {
|
||||
pub fn new(serializer: impl RequestSerializer + 'static) -> Self {
|
||||
Self(Arc::new(serializer))
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestSerializer for SharedRequestSerializer {
|
||||
fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result<HttpRequest, BoxError> {
|
||||
self.0.serialize_input(input, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for SharedRequestSerializer {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
pub trait ResponseDeserializer: Send + Sync + fmt::Debug {
|
||||
fn deserialize_streaming(
|
||||
&self,
|
||||
response: &mut HttpResponse,
|
||||
) -> Option<Result<Output, OrchestratorError<Error>>> {
|
||||
let _ = response;
|
||||
None
|
||||
}
|
||||
|
||||
fn deserialize_nonstreaming(
|
||||
&self,
|
||||
response: &HttpResponse,
|
||||
) -> Result<Output, OrchestratorError<Error>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DynResponseDeserializer(Box<dyn ResponseDeserializer>);
|
||||
|
||||
impl DynResponseDeserializer {
|
||||
pub fn new(serializer: impl ResponseDeserializer + 'static) -> Self {
|
||||
Self(Box::new(serializer))
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseDeserializer for DynResponseDeserializer {
|
||||
fn deserialize_nonstreaming(
|
||||
&self,
|
||||
response: &HttpResponse,
|
||||
) -> Result<Output, OrchestratorError<Error>> {
|
||||
self.0.deserialize_nonstreaming(response)
|
||||
}
|
||||
|
||||
fn deserialize_streaming(
|
||||
&self,
|
||||
response: &mut HttpResponse,
|
||||
) -> Option<Result<Output, OrchestratorError<Error>>> {
|
||||
self.0.deserialize_streaming(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for DynResponseDeserializer {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EndpointResolverParams(TypeErasedBox);
|
||||
|
||||
impl EndpointResolverParams {
|
||||
pub fn new<T: fmt::Debug + Send + Sync + 'static>(params: T) -> Self {
|
||||
Self(TypedBox::new(params).erase())
|
||||
}
|
||||
|
||||
pub fn get<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
|
||||
self.0.downcast_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for EndpointResolverParams {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
pub trait EndpointResolver: Send + Sync + fmt::Debug {
|
||||
fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future<Endpoint>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedEndpointResolver(Arc<dyn EndpointResolver>);
|
||||
|
||||
impl SharedEndpointResolver {
|
||||
pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self {
|
||||
Self(Arc::new(endpoint_resolver))
|
||||
}
|
||||
}
|
||||
|
||||
impl EndpointResolver for SharedEndpointResolver {
|
||||
fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future<Endpoint> {
|
||||
self.0.resolve_endpoint(params)
|
||||
}
|
||||
}
|
||||
|
||||
/// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit.
|
||||
///
|
||||
/// This enum gets placed into the `ConfigBag` to change the orchestrator behavior.
|
||||
|
@ -153,4 +71,143 @@ impl Storable for LoadedRequestBody {
|
|||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
pub(crate) const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded;
|
||||
// TODO(enableNewSmithyRuntimeLaunch): Make OrchestratorError adhere to the errors RFC
|
||||
/// Errors that can occur while running the orchestrator.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OrchestratorError<E> {
|
||||
/// An error occurred within an interceptor.
|
||||
Interceptor { err: InterceptorError },
|
||||
/// An error returned by a service.
|
||||
Operation { err: E },
|
||||
/// An error that occurs when a request times out.
|
||||
Timeout { err: BoxError },
|
||||
/// An error that occurs when request dispatch fails.
|
||||
Connector { err: ConnectorError },
|
||||
/// An error that occurs when a response can't be deserialized.
|
||||
Response { err: BoxError },
|
||||
/// A general orchestrator error.
|
||||
Other { err: BoxError },
|
||||
}
|
||||
|
||||
impl<E: Debug> OrchestratorError<E> {
|
||||
/// Create a new `OrchestratorError` from a [`BoxError`].
|
||||
pub fn other(err: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
|
||||
let err = err.into();
|
||||
Self::Other { err }
|
||||
}
|
||||
|
||||
/// Create a new `OrchestratorError` from an error received from a service.
|
||||
pub fn operation(err: E) -> Self {
|
||||
Self::Operation { err }
|
||||
}
|
||||
|
||||
/// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`].
|
||||
pub fn interceptor(err: InterceptorError) -> Self {
|
||||
Self::Interceptor { err }
|
||||
}
|
||||
|
||||
/// Create a new `OrchestratorError::Timeout` from a [`BoxError`].
|
||||
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> {
|
||||
match self {
|
||||
Self::Operation { err } => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the `OrchestratorError` into an [`SdkError`].
|
||||
pub(crate) fn into_sdk_error(
|
||||
self,
|
||||
phase: &Phase,
|
||||
response: Option<HttpResponse>,
|
||||
) -> SdkError<E, HttpResponse> {
|
||||
match self {
|
||||
Self::Interceptor { err } => {
|
||||
use Phase::*;
|
||||
match phase {
|
||||
BeforeSerialization | Serialization => SdkError::construction_failure(err),
|
||||
BeforeTransmit | Transmit => match response {
|
||||
Some(response) => SdkError::response_error(err, response),
|
||||
None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)),
|
||||
},
|
||||
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
||||
SdkError::response_error(err, response.expect("phase has a response"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::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.");
|
||||
SdkError::service_error(err, response.expect("phase has a response"))
|
||||
}
|
||||
Self::Connector { err } => SdkError::dispatch_failure(err),
|
||||
Self::Timeout { err } => SdkError::timeout_error(err),
|
||||
Self::Response { err } => SdkError::response_error(err, response.unwrap()),
|
||||
Self::Other { err } => {
|
||||
use Phase::*;
|
||||
match phase {
|
||||
BeforeSerialization | Serialization => SdkError::construction_failure(err),
|
||||
BeforeTransmit | Transmit => convert_dispatch_error(err, response),
|
||||
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
||||
SdkError::response_error(err, response.expect("phase has a response"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_dispatch_error<O>(
|
||||
err: BoxError,
|
||||
response: Option<HttpResponse>,
|
||||
) -> SdkError<O, HttpResponse> {
|
||||
let err = match err.downcast::<ConnectorError>() {
|
||||
Ok(connector_error) => {
|
||||
return SdkError::dispatch_failure(*connector_error);
|
||||
}
|
||||
Err(e) => e,
|
||||
};
|
||||
match response {
|
||||
Some(response) => SdkError::response_error(err, response),
|
||||
None => SdkError::dispatch_failure(ConnectorError::other(err, None)),
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<InterceptorError> for OrchestratorError<E>
|
||||
where
|
||||
E: Debug + std::error::Error + 'static,
|
||||
{
|
||||
fn from(err: InterceptorError) -> Self {
|
||||
Self::interceptor(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypeErasedError> for OrchestratorError<TypeErasedError> {
|
||||
fn from(err: TypeErasedError) -> Self {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use super::BoxError;
|
||||
use crate::client::interceptors::context::phase::Phase;
|
||||
use crate::client::interceptors::InterceptorError;
|
||||
use crate::client::orchestrator::HttpResponse;
|
||||
use aws_smithy_http::result::{ConnectorError, SdkError};
|
||||
use aws_smithy_types::type_erasure::TypeErasedError;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OrchestratorError<E> {
|
||||
/// An error occurred within an interceptor.
|
||||
Interceptor { err: InterceptorError },
|
||||
/// An error returned by a service.
|
||||
Operation { err: E },
|
||||
/// An error that occurs when a request times out.
|
||||
Timeout { err: BoxError },
|
||||
/// An error that occurs when request dispatch fails.
|
||||
Connector { err: ConnectorError },
|
||||
/// An error that occurs when a response can't be deserialized.
|
||||
Response { err: BoxError },
|
||||
/// A general orchestrator error.
|
||||
Other { err: BoxError },
|
||||
}
|
||||
|
||||
impl<E: Debug> OrchestratorError<E> {
|
||||
/// Create a new `OrchestratorError` from a [`BoxError`].
|
||||
pub fn other(err: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
|
||||
let err = err.into();
|
||||
Self::Other { err }
|
||||
}
|
||||
|
||||
/// Create a new `OrchestratorError` from an error received from a service.
|
||||
pub fn operation(err: E) -> Self {
|
||||
Self::Operation { err }
|
||||
}
|
||||
|
||||
/// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`].
|
||||
pub fn interceptor(err: InterceptorError) -> Self {
|
||||
Self::Interceptor { err }
|
||||
}
|
||||
|
||||
/// Create a new `OrchestratorError::Timeout` from a [`BoxError`].
|
||||
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> {
|
||||
match self {
|
||||
Self::Operation { err } => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the `OrchestratorError` into an [`SdkError`].
|
||||
pub(crate) fn into_sdk_error(
|
||||
self,
|
||||
phase: &Phase,
|
||||
response: Option<HttpResponse>,
|
||||
) -> SdkError<E, HttpResponse> {
|
||||
match self {
|
||||
Self::Interceptor { err } => {
|
||||
use Phase::*;
|
||||
match phase {
|
||||
BeforeSerialization | Serialization => SdkError::construction_failure(err),
|
||||
BeforeTransmit | Transmit => match response {
|
||||
Some(response) => SdkError::response_error(err, response),
|
||||
None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)),
|
||||
},
|
||||
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
||||
SdkError::response_error(err, response.expect("phase has a response"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::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.");
|
||||
SdkError::service_error(err, response.expect("phase has a response"))
|
||||
}
|
||||
Self::Connector { err } => SdkError::dispatch_failure(err),
|
||||
Self::Timeout { err } => SdkError::timeout_error(err),
|
||||
Self::Response { err } => SdkError::response_error(err, response.unwrap()),
|
||||
Self::Other { err } => {
|
||||
use Phase::*;
|
||||
match phase {
|
||||
BeforeSerialization | Serialization => SdkError::construction_failure(err),
|
||||
BeforeTransmit | Transmit => convert_dispatch_error(err, response),
|
||||
BeforeDeserialization | Deserialization | AfterDeserialization => {
|
||||
SdkError::response_error(err, response.expect("phase has a response"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_dispatch_error<O>(
|
||||
err: BoxError,
|
||||
response: Option<HttpResponse>,
|
||||
) -> SdkError<O, HttpResponse> {
|
||||
let err = match err.downcast::<ConnectorError>() {
|
||||
Ok(connector_error) => {
|
||||
return SdkError::dispatch_failure(*connector_error);
|
||||
}
|
||||
Err(e) => e,
|
||||
};
|
||||
match response {
|
||||
Some(response) => SdkError::response_error(err, response),
|
||||
None => SdkError::dispatch_failure(ConnectorError::other(err, None)),
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<InterceptorError> for OrchestratorError<E>
|
||||
where
|
||||
E: Debug + std::error::Error + 'static,
|
||||
{
|
||||
fn from(err: InterceptorError) -> Self {
|
||||
Self::interceptor(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypeErasedError> for OrchestratorError<TypeErasedError> {
|
||||
fn from(err: TypeErasedError) -> Self {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use aws_smithy_types::config_bag::{Storable, StoreReplace};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RequestAttempts {
|
||||
attempts: u32,
|
||||
}
|
||||
|
||||
impl RequestAttempts {
|
||||
#[cfg(any(feature = "test-util", test))]
|
||||
pub fn new(attempts: u32) -> Self {
|
||||
Self { attempts }
|
||||
}
|
||||
|
||||
pub fn attempts(&self) -> u32 {
|
||||
self.attempts
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for RequestAttempts {
|
||||
fn from(attempts: u32) -> Self {
|
||||
Self { attempts }
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for RequestAttempts {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
|
@ -3,8 +3,13 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Retry handling and token bucket.
|
||||
//!
|
||||
//! This code defines when and how failed requests should be retried. It also defines the behavior
|
||||
//! used to limit the rate that requests are sent.
|
||||
|
||||
use crate::client::interceptors::context::InterceptorContext;
|
||||
use aws_smithy_types::config_bag::ConfigBag;
|
||||
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
||||
use std::fmt::Debug;
|
||||
use std::time::Duration;
|
||||
use tracing::trace;
|
||||
|
@ -14,13 +19,17 @@ pub use aws_smithy_types::retry::ErrorKind;
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// An answer to the question "should I make a request attempt?"
|
||||
pub enum ShouldAttempt {
|
||||
/// Yes, an attempt should be made
|
||||
Yes,
|
||||
/// No, no attempt should be made
|
||||
No,
|
||||
/// Yes, an attempt should be made, but only after the given amount of time has passed
|
||||
YesAfterDelay(Duration),
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-util")]
|
||||
impl ShouldAttempt {
|
||||
/// Returns the delay duration if this is a `YesAfterDelay` variant.
|
||||
pub fn expect_delay(self) -> Duration {
|
||||
match self {
|
||||
ShouldAttempt::YesAfterDelay(delay) => delay,
|
||||
|
@ -29,13 +38,26 @@ impl ShouldAttempt {
|
|||
}
|
||||
}
|
||||
|
||||
/// Decider for whether or not to attempt a request, and when.
|
||||
///
|
||||
/// The orchestrator consults the retry strategy every time before making a request.
|
||||
/// This includes the initial request, and any retry attempts thereafter. The
|
||||
/// orchestrator will retry indefinitely (until success) if the retry strategy
|
||||
/// always returns `ShouldAttempt::Yes` from `should_attempt_retry`.
|
||||
pub trait RetryStrategy: Send + Sync + Debug {
|
||||
/// Decides if the initial attempt should be made.
|
||||
fn should_attempt_initial_request(
|
||||
&self,
|
||||
runtime_components: &RuntimeComponents,
|
||||
cfg: &ConfigBag,
|
||||
) -> Result<ShouldAttempt, BoxError>;
|
||||
|
||||
/// Decides if a retry should be done.
|
||||
///
|
||||
/// The previous attempt's output or error are provided in the
|
||||
/// [`InterceptorContext`] when this is called.
|
||||
///
|
||||
/// `ShouldAttempt::YesAfterDelay` can be used to add a backoff time.
|
||||
fn should_attempt_retry(
|
||||
&self,
|
||||
context: &InterceptorContext,
|
||||
|
@ -44,10 +66,12 @@ pub trait RetryStrategy: Send + Sync + Debug {
|
|||
) -> Result<ShouldAttempt, BoxError>;
|
||||
}
|
||||
|
||||
/// A shared retry strategy.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedRetryStrategy(Arc<dyn RetryStrategy>);
|
||||
|
||||
impl SharedRetryStrategy {
|
||||
/// Creates a new [`SharedRetryStrategy`] from a retry strategy.
|
||||
pub fn new(retry_strategy: impl RetryStrategy + 'static) -> Self {
|
||||
Self(Arc::new(retry_strategy))
|
||||
}
|
||||
|
@ -74,10 +98,13 @@ impl RetryStrategy for SharedRetryStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
/// Classification result from [`ClassifyRetry`].
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub enum RetryReason {
|
||||
/// There was an unexpected error, and this is the kind of error so that it can be properly retried.
|
||||
Error(ErrorKind),
|
||||
/// The server explicitly told us to back off by this amount of time.
|
||||
Explicit(Duration),
|
||||
}
|
||||
|
||||
|
@ -91,12 +118,14 @@ pub trait ClassifyRetry: Send + Sync + Debug {
|
|||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
/// Classifies an error into a [`RetryReason`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RetryClassifiers {
|
||||
inner: Vec<Arc<dyn ClassifyRetry>>,
|
||||
}
|
||||
|
||||
impl RetryClassifiers {
|
||||
/// Creates a new [`RetryClassifiers`].
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
// It's always expected that at least one classifier will be defined,
|
||||
|
@ -105,6 +134,7 @@ impl RetryClassifiers {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds a classifier to this collection.
|
||||
pub fn with_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self {
|
||||
self.inner.push(Arc::new(retry_classifier));
|
||||
self
|
||||
|
@ -138,6 +168,43 @@ impl ClassifyRetry for RetryClassifiers {
|
|||
}
|
||||
}
|
||||
|
||||
/// A type to track the number of requests sent by the orchestrator for a given operation.
|
||||
///
|
||||
/// `RequestAttempts` is added to the `ConfigBag` by the orchestrator,
|
||||
/// and holds the current attempt number.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RequestAttempts {
|
||||
attempts: u32,
|
||||
}
|
||||
|
||||
impl RequestAttempts {
|
||||
/// Creates a new [`RequestAttempts`] with the given number of attempts.
|
||||
pub fn new(attempts: u32) -> Self {
|
||||
Self { attempts }
|
||||
}
|
||||
|
||||
/// Returns the number of attempts.
|
||||
pub fn attempts(&self) -> u32 {
|
||||
self.attempts
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for RequestAttempts {
|
||||
fn from(attempts: u32) -> Self {
|
||||
Self::new(attempts)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequestAttempts> for u32 {
|
||||
fn from(value: RequestAttempts) -> Self {
|
||||
value.attempts()
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for RequestAttempts {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-util")]
|
||||
mod test_util {
|
||||
use super::{ClassifyRetry, ErrorKind, RetryReason};
|
||||
|
|
|
@ -3,13 +3,21 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Runtime components used to make a request and handle a response.
|
||||
//!
|
||||
//! Runtime components are trait implementations that are _always_ used by the orchestrator.
|
||||
//! There are other trait implementations that can be configured for a client, but if they
|
||||
//! aren't directly and always used by the orchestrator, then they are placed in the
|
||||
//! [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag) instead of in
|
||||
//! [`RuntimeComponents`](RuntimeComponents).
|
||||
|
||||
use crate::client::auth::{
|
||||
AuthSchemeId, HttpAuthScheme, SharedAuthOptionResolver, SharedHttpAuthScheme,
|
||||
AuthScheme, AuthSchemeId, SharedAuthScheme, SharedAuthSchemeOptionResolver,
|
||||
};
|
||||
use crate::client::connectors::SharedConnector;
|
||||
use crate::client::connectors::SharedHttpConnector;
|
||||
use crate::client::endpoint::SharedEndpointResolver;
|
||||
use crate::client::identity::{ConfiguredIdentityResolver, SharedIdentityResolver};
|
||||
use crate::client::interceptors::SharedInterceptor;
|
||||
use crate::client::orchestrator::SharedEndpointResolver;
|
||||
use crate::client::retries::{RetryClassifiers, SharedRetryStrategy};
|
||||
use aws_smithy_async::rt::sleep::SharedAsyncSleep;
|
||||
use aws_smithy_async::time::SharedTimeSource;
|
||||
|
@ -134,6 +142,7 @@ macro_rules! declare_runtime_components {
|
|||
$($field_name: runtime_component_field_type!($outer_type $inner_type $($option)?),)+
|
||||
}
|
||||
|
||||
/// Builder for [`RuntimeComponents`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct $builder_name {
|
||||
builder_name: &'static str,
|
||||
|
@ -171,16 +180,16 @@ macro_rules! declare_runtime_components {
|
|||
declare_runtime_components! {
|
||||
fields for RuntimeComponents and RuntimeComponentsBuilder {
|
||||
#[required]
|
||||
auth_option_resolver: Option<SharedAuthOptionResolver>,
|
||||
auth_scheme_option_resolver: Option<SharedAuthSchemeOptionResolver>,
|
||||
|
||||
// A connector is not required since a client could technically only be used for presigning
|
||||
connector: Option<SharedConnector>,
|
||||
http_connector: Option<SharedHttpConnector>,
|
||||
|
||||
#[required]
|
||||
endpoint_resolver: Option<SharedEndpointResolver>,
|
||||
|
||||
#[atLeastOneRequired]
|
||||
http_auth_schemes: Vec<SharedHttpAuthScheme>,
|
||||
auth_schemes: Vec<SharedAuthScheme>,
|
||||
|
||||
#[atLeastOneRequired]
|
||||
identity_resolvers: Vec<ConfiguredIdentityResolver>,
|
||||
|
@ -204,14 +213,14 @@ impl RuntimeComponents {
|
|||
RuntimeComponentsBuilder::new(name)
|
||||
}
|
||||
|
||||
/// Returns the auth option resolver.
|
||||
pub fn auth_option_resolver(&self) -> SharedAuthOptionResolver {
|
||||
self.auth_option_resolver.value.clone()
|
||||
/// Returns the auth scheme option resolver.
|
||||
pub fn auth_scheme_option_resolver(&self) -> SharedAuthSchemeOptionResolver {
|
||||
self.auth_scheme_option_resolver.value.clone()
|
||||
}
|
||||
|
||||
/// Returns the connector.
|
||||
pub fn connector(&self) -> Option<SharedConnector> {
|
||||
self.connector.as_ref().map(|s| s.value.clone())
|
||||
pub fn http_connector(&self) -> Option<SharedHttpConnector> {
|
||||
self.http_connector.as_ref().map(|s| s.value.clone())
|
||||
}
|
||||
|
||||
/// Returns the endpoint resolver.
|
||||
|
@ -220,8 +229,8 @@ impl RuntimeComponents {
|
|||
}
|
||||
|
||||
/// Returns the requested auth scheme if it is set.
|
||||
pub fn http_auth_scheme(&self, scheme_id: AuthSchemeId) -> Option<SharedHttpAuthScheme> {
|
||||
self.http_auth_schemes
|
||||
pub fn auth_scheme(&self, scheme_id: AuthSchemeId) -> Option<SharedAuthScheme> {
|
||||
self.auth_schemes
|
||||
.iter()
|
||||
.find(|s| s.value.scheme_id() == scheme_id)
|
||||
.map(|s| s.value.clone())
|
||||
|
@ -254,44 +263,46 @@ impl RuntimeComponents {
|
|||
}
|
||||
|
||||
impl RuntimeComponentsBuilder {
|
||||
/// Returns the auth option resolver.
|
||||
pub fn auth_option_resolver(&self) -> Option<SharedAuthOptionResolver> {
|
||||
self.auth_option_resolver.as_ref().map(|s| s.value.clone())
|
||||
/// Returns the auth scheme option resolver.
|
||||
pub fn auth_scheme_option_resolver(&self) -> Option<SharedAuthSchemeOptionResolver> {
|
||||
self.auth_scheme_option_resolver
|
||||
.as_ref()
|
||||
.map(|s| s.value.clone())
|
||||
}
|
||||
|
||||
/// Sets the auth option resolver.
|
||||
pub fn set_auth_option_resolver(
|
||||
/// Sets the auth scheme option resolver.
|
||||
pub fn set_auth_scheme_option_resolver(
|
||||
&mut self,
|
||||
auth_option_resolver: Option<SharedAuthOptionResolver>,
|
||||
auth_scheme_option_resolver: Option<SharedAuthSchemeOptionResolver>,
|
||||
) -> &mut Self {
|
||||
self.auth_option_resolver =
|
||||
auth_option_resolver.map(|r| Tracked::new(self.builder_name, r));
|
||||
self.auth_scheme_option_resolver =
|
||||
auth_scheme_option_resolver.map(|r| Tracked::new(self.builder_name, r));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the auth option resolver.
|
||||
pub fn with_auth_option_resolver(
|
||||
/// Sets the auth scheme option resolver.
|
||||
pub fn with_auth_scheme_option_resolver(
|
||||
mut self,
|
||||
auth_option_resolver: Option<SharedAuthOptionResolver>,
|
||||
auth_scheme_option_resolver: Option<SharedAuthSchemeOptionResolver>,
|
||||
) -> Self {
|
||||
self.set_auth_option_resolver(auth_option_resolver);
|
||||
self.set_auth_scheme_option_resolver(auth_scheme_option_resolver);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the connector.
|
||||
pub fn connector(&self) -> Option<SharedConnector> {
|
||||
self.connector.as_ref().map(|s| s.value.clone())
|
||||
/// Returns the HTTP connector.
|
||||
pub fn http_connector(&self) -> Option<SharedHttpConnector> {
|
||||
self.http_connector.as_ref().map(|s| s.value.clone())
|
||||
}
|
||||
|
||||
/// Sets the connector.
|
||||
pub fn set_connector(&mut self, connector: Option<SharedConnector>) -> &mut Self {
|
||||
self.connector = connector.map(|c| Tracked::new(self.builder_name, c));
|
||||
/// Sets the HTTP connector.
|
||||
pub fn set_http_connector(&mut self, connector: Option<SharedHttpConnector>) -> &mut Self {
|
||||
self.http_connector = connector.map(|c| Tracked::new(self.builder_name, c));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the connector.
|
||||
pub fn with_connector(mut self, connector: Option<SharedConnector>) -> Self {
|
||||
self.set_connector(connector);
|
||||
/// Sets the HTTP connector.
|
||||
pub fn with_http_connector(mut self, connector: Option<SharedHttpConnector>) -> Self {
|
||||
self.set_http_connector(connector);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -318,21 +329,21 @@ impl RuntimeComponentsBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns the HTTP auth schemes.
|
||||
pub fn http_auth_schemes(&self) -> impl Iterator<Item = SharedHttpAuthScheme> + '_ {
|
||||
self.http_auth_schemes.iter().map(|s| s.value.clone())
|
||||
/// Returns the auth schemes.
|
||||
pub fn auth_schemes(&self) -> impl Iterator<Item = SharedAuthScheme> + '_ {
|
||||
self.auth_schemes.iter().map(|s| s.value.clone())
|
||||
}
|
||||
|
||||
/// Adds a HTTP auth scheme.
|
||||
pub fn push_http_auth_scheme(&mut self, auth_scheme: SharedHttpAuthScheme) -> &mut Self {
|
||||
self.http_auth_schemes
|
||||
/// Adds an auth scheme.
|
||||
pub fn push_auth_scheme(&mut self, auth_scheme: SharedAuthScheme) -> &mut Self {
|
||||
self.auth_schemes
|
||||
.push(Tracked::new(self.builder_name, auth_scheme));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a HTTP auth scheme.
|
||||
pub fn with_http_auth_scheme(mut self, auth_scheme: SharedHttpAuthScheme) -> Self {
|
||||
self.push_http_auth_scheme(auth_scheme);
|
||||
/// Adds an auth scheme.
|
||||
pub fn with_auth_scheme(mut self, auth_scheme: SharedAuthScheme) -> Self {
|
||||
self.push_auth_scheme(auth_scheme);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -499,11 +510,12 @@ impl RuntimeComponentsBuilder {
|
|||
/// Creates a runtime components builder with all the required components filled in with fake (panicking) implementations.
|
||||
#[cfg(feature = "test-util")]
|
||||
pub fn for_tests() -> Self {
|
||||
use crate::client::auth::AuthOptionResolver;
|
||||
use crate::client::connectors::Connector;
|
||||
use crate::client::auth::AuthSchemeOptionResolver;
|
||||
use crate::client::connectors::HttpConnector;
|
||||
use crate::client::endpoint::{EndpointResolver, EndpointResolverParams};
|
||||
use crate::client::identity::Identity;
|
||||
use crate::client::identity::IdentityResolver;
|
||||
use crate::client::orchestrator::{EndpointResolver, EndpointResolverParams, Future};
|
||||
use crate::client::orchestrator::Future;
|
||||
use crate::client::retries::RetryStrategy;
|
||||
use aws_smithy_async::rt::sleep::AsyncSleep;
|
||||
use aws_smithy_async::time::TimeSource;
|
||||
|
@ -511,20 +523,20 @@ impl RuntimeComponentsBuilder {
|
|||
use aws_smithy_types::endpoint::Endpoint;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FakeAuthOptionResolver;
|
||||
impl AuthOptionResolver for FakeAuthOptionResolver {
|
||||
fn resolve_auth_options(
|
||||
struct FakeAuthSchemeOptionResolver;
|
||||
impl AuthSchemeOptionResolver for FakeAuthSchemeOptionResolver {
|
||||
fn resolve_auth_scheme_options(
|
||||
&self,
|
||||
_: &crate::client::auth::AuthOptionResolverParams,
|
||||
_: &crate::client::auth::AuthSchemeOptionResolverParams,
|
||||
) -> Result<std::borrow::Cow<'_, [AuthSchemeId]>, crate::box_error::BoxError>
|
||||
{
|
||||
unreachable!("fake auth option resolver must be overridden for this test")
|
||||
unreachable!("fake auth scheme option resolver must be overridden for this test")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FakeConnector;
|
||||
impl Connector for FakeConnector {
|
||||
impl HttpConnector for FakeConnector {
|
||||
fn call(
|
||||
&self,
|
||||
_: crate::client::orchestrator::HttpRequest,
|
||||
|
@ -543,8 +555,8 @@ impl RuntimeComponentsBuilder {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FakeHttpAuthScheme;
|
||||
impl HttpAuthScheme for FakeHttpAuthScheme {
|
||||
struct FakeAuthScheme;
|
||||
impl AuthScheme for FakeAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
AuthSchemeId::new("fake")
|
||||
}
|
||||
|
@ -556,7 +568,7 @@ impl RuntimeComponentsBuilder {
|
|||
None
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn crate::client::auth::HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn crate::client::auth::Signer {
|
||||
unreachable!("fake http auth scheme must be overridden for this test")
|
||||
}
|
||||
}
|
||||
|
@ -609,18 +621,19 @@ impl RuntimeComponentsBuilder {
|
|||
}
|
||||
|
||||
Self::new("aws_smithy_runtime_api::client::runtime_components::RuntimeComponentBuilder::for_tests")
|
||||
.with_auth_option_resolver(Some(SharedAuthOptionResolver::new(FakeAuthOptionResolver)))
|
||||
.with_connector(Some(SharedConnector::new(FakeConnector)))
|
||||
.with_auth_scheme(SharedAuthScheme::new(FakeAuthScheme))
|
||||
.with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new(FakeAuthSchemeOptionResolver)))
|
||||
.with_endpoint_resolver(Some(SharedEndpointResolver::new(FakeEndpointResolver)))
|
||||
.with_http_auth_scheme(SharedHttpAuthScheme::new(FakeHttpAuthScheme))
|
||||
.with_http_connector(Some(SharedHttpConnector::new(FakeConnector)))
|
||||
.with_identity_resolver(AuthSchemeId::new("fake"), SharedIdentityResolver::new(FakeIdentityResolver))
|
||||
.with_retry_classifiers(Some(RetryClassifiers::new()))
|
||||
.with_retry_strategy(Some(SharedRetryStrategy::new(FakeRetryStrategy)))
|
||||
.with_time_source(Some(SharedTimeSource::new(FakeTimeSource)))
|
||||
.with_sleep_impl(Some(SharedAsyncSleep::new(FakeSleep)))
|
||||
.with_time_source(Some(SharedTimeSource::new(FakeTimeSource)))
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that occurs when building runtime components.
|
||||
#[derive(Debug)]
|
||||
pub struct BuildError(&'static str);
|
||||
|
||||
|
@ -634,7 +647,7 @@ impl fmt::Display for BuildError {
|
|||
|
||||
/// A trait for retrieving a shared identity resolver.
|
||||
///
|
||||
/// This trait exists so that [`HttpAuthScheme::identity_resolver`](crate::client::auth::HttpAuthScheme::identity_resolver)
|
||||
/// This trait exists so that [`AuthScheme::identity_resolver`](crate::client::auth::AuthScheme::identity_resolver)
|
||||
/// can have access to configured identity resolvers without having access to all the runtime components.
|
||||
pub trait GetIdentityResolver: Send + Sync {
|
||||
/// Returns the requested identity resolver if it is set.
|
||||
|
|
|
@ -3,6 +3,21 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Runtime plugin type definitions.
|
||||
//!
|
||||
//! Runtime plugins are used to extend the runtime with custom behavior.
|
||||
//! This can include:
|
||||
//! - Registering interceptors
|
||||
//! - Registering auth schemes
|
||||
//! - Adding entries to the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag) for orchestration
|
||||
//! - Setting runtime components
|
||||
//!
|
||||
//! Runtime plugins are divided into service/operation "levels", with service runtime plugins
|
||||
//! executing before operation runtime plugins. Runtime plugins configured in a service
|
||||
//! config will always be at the service level, while runtime plugins added during
|
||||
//! operation customization will be at the operation level. Custom runtime plugins will
|
||||
//! always run after the default runtime plugins within their level.
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::runtime_components::{
|
||||
RuntimeComponentsBuilder, EMPTY_RUNTIME_COMPONENTS_BUILDER,
|
||||
|
@ -12,25 +27,44 @@ use std::borrow::Cow;
|
|||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// RuntimePlugin Trait
|
||||
/// Runtime plugin trait
|
||||
///
|
||||
/// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior.
|
||||
/// A `RuntimePlugin` is the unit of configuration for augmenting the SDK with new behavior.
|
||||
///
|
||||
/// Runtime plugins can register interceptors, set runtime components, and modify configuration.
|
||||
pub trait RuntimePlugin: Debug + Send + Sync {
|
||||
/// Optionally returns additional config that should be added to the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag).
|
||||
///
|
||||
/// As a best practice, a frozen layer should be stored on the runtime plugin instance as
|
||||
/// a member, and then cloned upon return since that clone is cheap. Constructing a new
|
||||
/// [`Layer`](aws_smithy_types::config_bag::Layer) and freezing it will require a lot of allocations.
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns a [`RuntimeComponentsBuilder`](RuntimeComponentsBuilder) to incorporate into the final runtime components.
|
||||
///
|
||||
/// The order of runtime plugins determines which runtime components "win". Components set by later runtime plugins will
|
||||
/// override those set by earlier runtime plugins.
|
||||
///
|
||||
/// If no runtime component changes are desired, just return an empty builder.
|
||||
///
|
||||
/// This method returns a [`Cow`] for flexibility. Some implementers may want to store the components builder
|
||||
/// as a member and return a reference to it, while others may need to create the builder every call. If possible,
|
||||
/// returning a reference is preferred for performance.
|
||||
fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> {
|
||||
Cow::Borrowed(&EMPTY_RUNTIME_COMPONENTS_BUILDER)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared runtime plugin
|
||||
///
|
||||
/// Allows for multiple places to share ownership of one runtime plugin.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SharedRuntimePlugin(Arc<dyn RuntimePlugin>);
|
||||
|
||||
impl SharedRuntimePlugin {
|
||||
/// Returns a new [`SharedRuntimePlugin`].
|
||||
pub fn new(plugin: impl RuntimePlugin + 'static) -> Self {
|
||||
Self(Arc::new(plugin))
|
||||
}
|
||||
|
@ -46,6 +80,47 @@ impl RuntimePlugin for SharedRuntimePlugin {
|
|||
}
|
||||
}
|
||||
|
||||
/// Runtime plugin that simply returns the config and components given at construction time.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct StaticRuntimePlugin {
|
||||
config: Option<FrozenLayer>,
|
||||
runtime_components: Option<RuntimeComponentsBuilder>,
|
||||
}
|
||||
|
||||
impl StaticRuntimePlugin {
|
||||
/// Returns a new [`StaticRuntimePlugin`].
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Changes the config.
|
||||
pub fn with_config(mut self, config: FrozenLayer) -> Self {
|
||||
self.config = Some(config);
|
||||
self
|
||||
}
|
||||
|
||||
/// Changes the runtime components.
|
||||
pub fn with_runtime_components(mut self, runtime_components: RuntimeComponentsBuilder) -> Self {
|
||||
self.runtime_components = Some(runtime_components);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimePlugin for StaticRuntimePlugin {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> {
|
||||
self.runtime_components
|
||||
.as_ref()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| RuntimePlugin::runtime_components(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Used internally in the orchestrator implementation and in the generated code. Not intended to be used elsewhere.
|
||||
#[doc(hidden)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct RuntimePlugins {
|
||||
client_plugins: Vec<SharedRuntimePlugin>,
|
||||
|
@ -99,41 +174,6 @@ impl RuntimePlugins {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct StaticRuntimePlugin {
|
||||
config: Option<FrozenLayer>,
|
||||
runtime_components: Option<RuntimeComponentsBuilder>,
|
||||
}
|
||||
|
||||
impl StaticRuntimePlugin {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn with_config(mut self, config: FrozenLayer) -> Self {
|
||||
self.config = Some(config);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_runtime_components(mut self, runtime_components: RuntimeComponentsBuilder) -> Self {
|
||||
self.runtime_components = Some(runtime_components);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimePlugin for StaticRuntimePlugin {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> {
|
||||
self.runtime_components
|
||||
.as_ref()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| RuntimePlugin::runtime_components(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{RuntimePlugin, RuntimePlugins};
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Serialization/deserialization for the orchestrator.
|
||||
|
||||
use crate::box_error::BoxError;
|
||||
use crate::client::interceptors::context::{Error, Input, Output};
|
||||
use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError};
|
||||
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Serialization implementation that converts an [`Input`] into an [`HttpRequest`].
|
||||
pub trait RequestSerializer: Send + Sync + fmt::Debug {
|
||||
/// Serializes the input into an HTTP request.
|
||||
///
|
||||
/// The type of the [`Input`] must be known ahead of time by the request serializer
|
||||
/// implementation, and must be downcasted to get access to the information necessary
|
||||
/// for serialization.
|
||||
///
|
||||
/// The request serializer is generally added to the [`ConfigBag`] by the operation's
|
||||
/// code generated runtime plugin, which is aware of the correct input/output/error types.
|
||||
fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result<HttpRequest, BoxError>;
|
||||
}
|
||||
|
||||
/// A shared request serializer.
|
||||
///
|
||||
/// This is a simple shared ownership wrapper type for the [`RequestSerializer`] trait.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SharedRequestSerializer(Arc<dyn RequestSerializer>);
|
||||
|
||||
impl SharedRequestSerializer {
|
||||
/// Creates a new shared request serializer.
|
||||
pub fn new(serializer: impl RequestSerializer + 'static) -> Self {
|
||||
Self(Arc::new(serializer))
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestSerializer for SharedRequestSerializer {
|
||||
fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result<HttpRequest, BoxError> {
|
||||
self.0.serialize_input(input, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for SharedRequestSerializer {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
||||
|
||||
/// Deserialization implementation that converts an [`HttpResponse`] into an [`Output`] or [`Error`].
|
||||
pub trait ResponseDeserializer: Send + Sync + fmt::Debug {
|
||||
/// For streaming requests, deserializes the response headers.
|
||||
///
|
||||
/// The orchestrator will call `deserialize_streaming` first, and if it returns `None`,
|
||||
/// then it will continue onto `deserialize_nonstreaming`. This method should only be
|
||||
/// implemented for streaming requests where the streaming response body needs to be a part
|
||||
/// of the deserialized output.
|
||||
fn deserialize_streaming(
|
||||
&self,
|
||||
response: &mut HttpResponse,
|
||||
) -> Option<Result<Output, OrchestratorError<Error>>> {
|
||||
let _ = response;
|
||||
None
|
||||
}
|
||||
|
||||
/// Deserialize the entire response including its body into an output or error.
|
||||
fn deserialize_nonstreaming(
|
||||
&self,
|
||||
response: &HttpResponse,
|
||||
) -> Result<Output, OrchestratorError<Error>>;
|
||||
}
|
||||
|
||||
/// Shared response deserializer.
|
||||
///
|
||||
/// This is a simple shared ownership wrapper type for the [`ResponseDeserializer`] trait.
|
||||
#[derive(Debug)]
|
||||
pub struct SharedResponseDeserializer(Arc<dyn ResponseDeserializer>);
|
||||
|
||||
impl SharedResponseDeserializer {
|
||||
/// Creates a new [`SharedResponseDeserializer`].
|
||||
pub fn new(serializer: impl ResponseDeserializer + 'static) -> Self {
|
||||
Self(Arc::new(serializer))
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseDeserializer for SharedResponseDeserializer {
|
||||
fn deserialize_nonstreaming(
|
||||
&self,
|
||||
response: &HttpResponse,
|
||||
) -> Result<Output, OrchestratorError<Error>> {
|
||||
self.0.deserialize_nonstreaming(response)
|
||||
}
|
||||
|
||||
fn deserialize_streaming(
|
||||
&self,
|
||||
response: &mut HttpResponse,
|
||||
) -> Option<Result<Output, OrchestratorError<Error>>> {
|
||||
self.0.deserialize_streaming(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for SharedResponseDeserializer {
|
||||
type Storer = StoreReplace<Self>;
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#![warn(
|
||||
// TODO(enableNewSmithyRuntimeLaunch): Add in remaining missing docs
|
||||
// missing_docs,
|
||||
rustdoc::missing_crate_level_docs,
|
||||
unreachable_pub,
|
||||
|
@ -11,12 +12,26 @@
|
|||
)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
|
||||
//! Basic types for the new smithy client orchestrator.
|
||||
//! APIs needed to configure and customize the Smithy generated code.
|
||||
//!
|
||||
//! Most users will not need to use this crate directly as the most frequently used
|
||||
//! APIs are re-exported in the generated clients. However, this crate will be useful
|
||||
//! for anyone writing a library for others to use with their generated clients.
|
||||
//!
|
||||
//! If you're needing to depend on this and you're not writing a library for Smithy
|
||||
//! generated clients, then please file an issue on [smithy-rs](https://github.com/awslabs/smithy-rs)
|
||||
//! as we likely missed re-exporting one of the APIs.
|
||||
//!
|
||||
//! All client-specific code is in the [`client`](crate::client) root level module
|
||||
//! to leave room for smithy-rs server APIs in the future.
|
||||
|
||||
/// A boxed error that is `Send` and `Sync`.
|
||||
pub mod box_error;
|
||||
|
||||
/// Smithy runtime for client orchestration.
|
||||
/// APIs for client orchestration.
|
||||
#[cfg(feature = "client")]
|
||||
pub mod client;
|
||||
|
||||
/// Internal builder macros. Not intended to be used outside of the aws-smithy-runtime crates.
|
||||
#[doc(hidden)]
|
||||
pub mod macros;
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! builder {
|
||||
($($tt:tt)+) => {
|
||||
|
@ -128,6 +129,7 @@ macro_rules! builder {
|
|||
|
||||
/// Define a new builder struct, its fields, and their docs. This macro is intended to be called
|
||||
/// by the `builder!` macro and should not be called directly.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! builder_struct {
|
||||
($($_setter_name:ident, $field_name:ident, $ty:ty, $doc:literal $(,)?)+) => {
|
||||
|
@ -143,6 +145,7 @@ macro_rules! builder_struct {
|
|||
|
||||
/// Define setter methods for a builder struct. Must be called from within an `impl` block. This
|
||||
/// macro is intended to be called by the `builder!` macro and should not be called directly.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! builder_methods {
|
||||
($fn_name:ident, $arg_name:ident, $ty:ty, $doc:literal, $($tail:tt)+) => {
|
||||
|
|
|
@ -10,6 +10,7 @@ repository = "https://github.com/awslabs/smithy-rs"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
client = ["aws-smithy-runtime-api/client"]
|
||||
http-auth = ["aws-smithy-runtime-api/http-auth"]
|
||||
test-util = ["dep:aws-smithy-protocol-test", "dep:tracing-subscriber"]
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# aws-smithy-runtime
|
||||
|
||||
**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.**
|
||||
|
||||
Runtime support logic and types for smithy-rs generated code.
|
||||
|
||||
<!-- anchor_start:footer -->
|
||||
|
|
|
@ -10,7 +10,7 @@ use aws_smithy_runtime_api::client::auth::http::{
|
|||
HTTP_DIGEST_AUTH_SCHEME_ID,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::auth::{
|
||||
AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner,
|
||||
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Signer,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::identity::http::{Login, Token};
|
||||
use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver};
|
||||
|
@ -51,7 +51,7 @@ impl ApiKeyAuthScheme {
|
|||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for ApiKeyAuthScheme {
|
||||
impl AuthScheme for ApiKeyAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
HTTP_API_KEY_AUTH_SCHEME_ID
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl HttpAuthScheme for ApiKeyAuthScheme {
|
|||
identity_resolvers.identity_resolver(self.scheme_id())
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +75,8 @@ struct ApiKeySigner {
|
|||
name: String,
|
||||
}
|
||||
|
||||
impl HttpRequestSigner for ApiKeySigner {
|
||||
fn sign_request(
|
||||
impl Signer for ApiKeySigner {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
|
@ -122,7 +122,7 @@ impl BasicAuthScheme {
|
|||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for BasicAuthScheme {
|
||||
impl AuthScheme for BasicAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
HTTP_BASIC_AUTH_SCHEME_ID
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ impl HttpAuthScheme for BasicAuthScheme {
|
|||
identity_resolvers.identity_resolver(self.scheme_id())
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +142,8 @@ impl HttpAuthScheme for BasicAuthScheme {
|
|||
#[derive(Debug, Default)]
|
||||
struct BasicAuthSigner;
|
||||
|
||||
impl HttpRequestSigner for BasicAuthSigner {
|
||||
fn sign_request(
|
||||
impl Signer for BasicAuthSigner {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
|
@ -181,7 +181,7 @@ impl BearerAuthScheme {
|
|||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for BearerAuthScheme {
|
||||
impl AuthScheme for BearerAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
HTTP_BEARER_AUTH_SCHEME_ID
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ impl HttpAuthScheme for BearerAuthScheme {
|
|||
identity_resolvers.identity_resolver(self.scheme_id())
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
@ -201,8 +201,8 @@ impl HttpAuthScheme for BearerAuthScheme {
|
|||
#[derive(Debug, Default)]
|
||||
struct BearerAuthSigner;
|
||||
|
||||
impl HttpRequestSigner for BearerAuthSigner {
|
||||
fn sign_request(
|
||||
impl Signer for BearerAuthSigner {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
identity: &Identity,
|
||||
|
@ -238,7 +238,7 @@ impl DigestAuthScheme {
|
|||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for DigestAuthScheme {
|
||||
impl AuthScheme for DigestAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
HTTP_DIGEST_AUTH_SCHEME_ID
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ impl HttpAuthScheme for DigestAuthScheme {
|
|||
identity_resolvers.identity_resolver(self.scheme_id())
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
@ -258,8 +258,8 @@ impl HttpAuthScheme for DigestAuthScheme {
|
|||
#[derive(Debug, Default)]
|
||||
struct DigestAuthSigner;
|
||||
|
||||
impl HttpRequestSigner for DigestAuthSigner {
|
||||
fn sign_request(
|
||||
impl Signer for DigestAuthSigner {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
_request: &mut HttpRequest,
|
||||
_identity: &Identity,
|
||||
|
@ -295,7 +295,7 @@ mod tests {
|
|||
.body(SdkBody::empty())
|
||||
.unwrap();
|
||||
signer
|
||||
.sign_request(
|
||||
.sign_http_request(
|
||||
&mut request,
|
||||
&identity,
|
||||
AuthSchemeEndpointConfig::empty(),
|
||||
|
@ -325,7 +325,7 @@ mod tests {
|
|||
.body(SdkBody::empty())
|
||||
.unwrap();
|
||||
signer
|
||||
.sign_request(
|
||||
.sign_http_request(
|
||||
&mut request,
|
||||
&identity,
|
||||
AuthSchemeEndpointConfig::empty(),
|
||||
|
@ -349,7 +349,7 @@ mod tests {
|
|||
let mut request = http::Request::builder().body(SdkBody::empty()).unwrap();
|
||||
|
||||
signer
|
||||
.sign_request(
|
||||
.sign_http_request(
|
||||
&mut request,
|
||||
&identity,
|
||||
AuthSchemeEndpointConfig::empty(),
|
||||
|
@ -372,7 +372,7 @@ mod tests {
|
|||
let identity = Identity::new(Token::new("some-token", None), None);
|
||||
let mut request = http::Request::builder().body(SdkBody::empty()).unwrap();
|
||||
signer
|
||||
.sign_request(
|
||||
.sign_http_request(
|
||||
&mut request,
|
||||
&identity,
|
||||
AuthSchemeEndpointConfig::empty(),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
use crate::client::identity::no_auth::NoAuthIdentityResolver;
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::auth::{
|
||||
AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, SharedHttpAuthScheme,
|
||||
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, SharedAuthScheme, Signer,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver};
|
||||
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
|
||||
|
@ -43,7 +43,7 @@ impl NoAuthRuntimePlugin {
|
|||
NO_AUTH_SCHEME_ID,
|
||||
SharedIdentityResolver::new(NoAuthIdentityResolver::new()),
|
||||
)
|
||||
.with_http_auth_scheme(SharedHttpAuthScheme::new(NoAuthScheme::new())),
|
||||
.with_auth_scheme(SharedAuthScheme::new(NoAuthScheme::new())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +68,8 @@ impl NoAuthScheme {
|
|||
#[derive(Debug, Default)]
|
||||
struct NoAuthSigner;
|
||||
|
||||
impl HttpRequestSigner for NoAuthSigner {
|
||||
fn sign_request(
|
||||
impl Signer for NoAuthSigner {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
_request: &mut HttpRequest,
|
||||
_identity: &Identity,
|
||||
|
@ -81,7 +81,7 @@ impl HttpRequestSigner for NoAuthSigner {
|
|||
}
|
||||
}
|
||||
|
||||
impl HttpAuthScheme for NoAuthScheme {
|
||||
impl AuthScheme for NoAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
NO_AUTH_SCHEME_ID
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ impl HttpAuthScheme for NoAuthScheme {
|
|||
identity_resolvers.identity_resolver(NO_AUTH_SCHEME_ID)
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,13 @@ pub mod connection_poisoning;
|
|||
#[cfg(feature = "test-util")]
|
||||
pub mod test_util;
|
||||
|
||||
// TODO(enableNewSmithyRuntimeCleanup): Delete this module
|
||||
/// Unstable API for interfacing the old middleware connectors with the newer orchestrator connectors.
|
||||
///
|
||||
/// Important: This module and its contents will be removed in the next release.
|
||||
pub mod adapter {
|
||||
use aws_smithy_client::erase::DynConnector;
|
||||
use aws_smithy_runtime_api::client::connectors::Connector;
|
||||
use aws_smithy_runtime_api::client::connectors::HttpConnector;
|
||||
use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
@ -27,7 +31,7 @@ pub mod adapter {
|
|||
}
|
||||
}
|
||||
|
||||
impl Connector for DynConnectorAdapter {
|
||||
impl HttpConnector for DynConnectorAdapter {
|
||||
fn call(&self, request: HttpRequest) -> BoxFuture<HttpResponse> {
|
||||
let future = self.dyn_connector.lock().unwrap().call_lite(request);
|
||||
future
|
||||
|
|
|
@ -9,7 +9,7 @@ use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep};
|
|||
use aws_smithy_http::body::SdkBody;
|
||||
use aws_smithy_http::result::ConnectorError;
|
||||
use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType};
|
||||
use aws_smithy_runtime_api::client::connectors::Connector;
|
||||
use aws_smithy_runtime_api::client::connectors::HttpConnector;
|
||||
use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse};
|
||||
use http::header::{HeaderName, CONTENT_TYPE};
|
||||
use std::fmt::Debug;
|
||||
|
@ -181,7 +181,7 @@ impl ValidateRequest {
|
|||
}
|
||||
}
|
||||
|
||||
/// TestConnection for use as a [`Connector`].
|
||||
/// TestConnection for use as a [`HttpConnector`].
|
||||
///
|
||||
/// A basic test connection. It will:
|
||||
/// - Respond to requests with a preloaded series of responses
|
||||
|
@ -222,7 +222,7 @@ impl TestConnection {
|
|||
}
|
||||
}
|
||||
|
||||
impl Connector for TestConnection {
|
||||
impl HttpConnector for TestConnection {
|
||||
fn call(&self, request: HttpRequest) -> BoxFuture<HttpResponse> {
|
||||
let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() {
|
||||
self.requests.lock().unwrap().push(ValidateRequest {
|
||||
|
|
|
@ -15,19 +15,20 @@ use aws_smithy_http::body::SdkBody;
|
|||
use aws_smithy_http::byte_stream::ByteStream;
|
||||
use aws_smithy_http::result::SdkError;
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::connectors::Connector;
|
||||
use aws_smithy_runtime_api::client::connectors::HttpConnector;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||
Error, Input, InterceptorContext, Output, RewindResult,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::interceptors::Interceptors;
|
||||
use aws_smithy_runtime_api::client::orchestrator::{
|
||||
DynResponseDeserializer, HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer,
|
||||
ResponseDeserializer, SharedRequestSerializer,
|
||||
HttpResponse, LoadedRequestBody, OrchestratorError,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::request_attempts::RequestAttempts;
|
||||
use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt};
|
||||
use aws_smithy_runtime_api::client::retries::{RequestAttempts, RetryStrategy, ShouldAttempt};
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins;
|
||||
use aws_smithy_runtime_api::client::ser_de::{
|
||||
RequestSerializer, ResponseDeserializer, SharedRequestSerializer, SharedResponseDeserializer,
|
||||
};
|
||||
use aws_smithy_types::config_bag::ConfigBag;
|
||||
use std::mem;
|
||||
use tracing::{debug, debug_span, instrument, trace, Instrument};
|
||||
|
@ -334,7 +335,7 @@ async fn try_attempt(
|
|||
let response = halt_on_err!([ctx] => {
|
||||
let request = ctx.take_request().expect("set during serialization");
|
||||
trace!(request = ?request, "transmitting request");
|
||||
let connector = halt_on_err!([ctx] => runtime_components.connector().ok_or_else(||
|
||||
let connector = halt_on_err!([ctx] => runtime_components.http_connector().ok_or_else(||
|
||||
OrchestratorError::other("No HTTP connector was available to send this request. \
|
||||
Enable the `rustls` crate feature or set a connector to fix this.")
|
||||
));
|
||||
|
@ -359,7 +360,7 @@ async fn try_attempt(
|
|||
let output_or_error = async {
|
||||
let response = ctx.response_mut().expect("set during transmit");
|
||||
let response_deserializer = cfg
|
||||
.load::<DynResponseDeserializer>()
|
||||
.load::<SharedResponseDeserializer>()
|
||||
.expect("a request deserializer must be in the config bag");
|
||||
let maybe_deserialized = {
|
||||
let _span = debug_span!("deserialize_streaming").entered();
|
||||
|
@ -420,11 +421,14 @@ mod tests {
|
|||
deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer,
|
||||
};
|
||||
use ::http::{Request, Response, StatusCode};
|
||||
use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver;
|
||||
use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver;
|
||||
use aws_smithy_runtime_api::client::auth::{
|
||||
AuthOptionResolverParams, SharedAuthOptionResolver,
|
||||
AuthSchemeOptionResolverParams, SharedAuthSchemeOptionResolver,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::connectors::{HttpConnector, SharedHttpConnector};
|
||||
use aws_smithy_runtime_api::client::endpoint::{
|
||||
EndpointResolverParams, SharedEndpointResolver,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::connectors::{Connector, SharedConnector};
|
||||
use aws_smithy_runtime_api::client::interceptors::context::{
|
||||
AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut,
|
||||
BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut,
|
||||
|
@ -433,10 +437,7 @@ mod tests {
|
|||
FinalizerInterceptorContextRef,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor};
|
||||
use aws_smithy_runtime_api::client::orchestrator::{
|
||||
BoxFuture, DynResponseDeserializer, EndpointResolverParams, Future, HttpRequest,
|
||||
SharedEndpointResolver, SharedRequestSerializer,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, Future, HttpRequest};
|
||||
use aws_smithy_runtime_api::client::retries::SharedRetryStrategy;
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder;
|
||||
use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins};
|
||||
|
@ -474,7 +475,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
impl Connector for OkConnector {
|
||||
impl HttpConnector for OkConnector {
|
||||
fn call(&self, _request: HttpRequest) -> BoxFuture<HttpResponse> {
|
||||
Box::pin(Future::ready(Ok(::http::Response::builder()
|
||||
.status(200)
|
||||
|
@ -496,9 +497,9 @@ mod tests {
|
|||
.with_endpoint_resolver(Some(SharedEndpointResolver::new(
|
||||
StaticUriEndpointResolver::http_localhost(8080),
|
||||
)))
|
||||
.with_connector(Some(SharedConnector::new(OkConnector::new())))
|
||||
.with_auth_option_resolver(Some(SharedAuthOptionResolver::new(
|
||||
StaticAuthOptionResolver::new(vec![NO_AUTH_SCHEME_ID]),
|
||||
.with_http_connector(Some(SharedHttpConnector::new(OkConnector::new())))
|
||||
.with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new(
|
||||
StaticAuthSchemeOptionResolver::new(vec![NO_AUTH_SCHEME_ID]),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
@ -507,10 +508,10 @@ mod tests {
|
|||
impl RuntimePlugin for TestOperationRuntimePlugin {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
let mut layer = Layer::new("TestOperationRuntimePlugin");
|
||||
layer.store_put(AuthOptionResolverParams::new("idontcare"));
|
||||
layer.store_put(AuthSchemeOptionResolverParams::new("idontcare"));
|
||||
layer.store_put(EndpointResolverParams::new("dontcare"));
|
||||
layer.store_put(SharedRequestSerializer::new(new_request_serializer()));
|
||||
layer.store_put(DynResponseDeserializer::new(new_response_deserializer()));
|
||||
layer.store_put(SharedResponseDeserializer::new(new_response_deserializer()));
|
||||
Some(layer.freeze())
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::auth::{
|
||||
AuthOptionResolver, AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme,
|
||||
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, AuthSchemeOptionResolver,
|
||||
AuthSchemeOptionResolverParams,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors;
|
||||
use aws_smithy_runtime_api::client::identity::IdentityResolver;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
|
@ -49,7 +49,7 @@ impl fmt::Display for AuthOrchestrationError {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NoMatchingAuthScheme => f.write_str(
|
||||
"no auth scheme matched auth options. This is a bug. Please file an issue.",
|
||||
"no auth scheme matched auth scheme options. This is a bug. Please file an issue.",
|
||||
),
|
||||
Self::BadAuthSchemeEndpointConfig(message) => f.write_str(message),
|
||||
Self::AuthSchemeEndpointConfigMismatch(supported_schemes) => {
|
||||
|
@ -70,24 +70,26 @@ pub(super) async fn orchestrate_auth(
|
|||
runtime_components: &RuntimeComponents,
|
||||
cfg: &ConfigBag,
|
||||
) -> Result<(), BoxError> {
|
||||
let params = cfg.auth_option_resolver_params();
|
||||
let auth_option_resolver = runtime_components.auth_option_resolver();
|
||||
let auth_options = auth_option_resolver.resolve_auth_options(params)?;
|
||||
let params = cfg
|
||||
.load::<AuthSchemeOptionResolverParams>()
|
||||
.expect("auth scheme option resolver params must be set");
|
||||
let option_resolver = runtime_components.auth_scheme_option_resolver();
|
||||
let options = option_resolver.resolve_auth_scheme_options(params)?;
|
||||
|
||||
trace!(
|
||||
auth_option_resolver_params = ?params,
|
||||
auth_options = ?auth_options,
|
||||
auth_scheme_option_resolver_params = ?params,
|
||||
auth_scheme_options = ?options,
|
||||
"orchestrating auth",
|
||||
);
|
||||
|
||||
for &scheme_id in auth_options.as_ref() {
|
||||
if let Some(auth_scheme) = runtime_components.http_auth_scheme(scheme_id) {
|
||||
for &scheme_id in options.as_ref() {
|
||||
if let Some(auth_scheme) = runtime_components.auth_scheme(scheme_id) {
|
||||
if let Some(identity_resolver) = auth_scheme.identity_resolver(runtime_components) {
|
||||
let request_signer = auth_scheme.request_signer();
|
||||
let signer = auth_scheme.signer();
|
||||
trace!(
|
||||
auth_scheme = ?auth_scheme,
|
||||
identity_resolver = ?identity_resolver,
|
||||
request_signer = ?request_signer,
|
||||
signer = ?signer,
|
||||
"resolved auth scheme, identity resolver, and signing implementation"
|
||||
);
|
||||
|
||||
|
@ -103,7 +105,7 @@ pub(super) async fn orchestrate_auth(
|
|||
|
||||
trace!("signing request");
|
||||
let request = ctx.request_mut().expect("set during serialization");
|
||||
request_signer.sign_request(
|
||||
signer.sign_http_request(
|
||||
request,
|
||||
&identity,
|
||||
auth_scheme_endpoint_config,
|
||||
|
@ -125,7 +127,7 @@ fn extract_endpoint_auth_scheme_config(
|
|||
let auth_schemes = match endpoint.properties().get("authSchemes") {
|
||||
Some(Document::Array(schemes)) => schemes,
|
||||
// no auth schemes:
|
||||
None => return Ok(AuthSchemeEndpointConfig::new(None)),
|
||||
None => return Ok(AuthSchemeEndpointConfig::from(None)),
|
||||
_other => {
|
||||
return Err(AuthOrchestrationError::BadAuthSchemeEndpointConfig(
|
||||
"expected an array for `authSchemes` in endpoint config".into(),
|
||||
|
@ -144,17 +146,17 @@ fn extract_endpoint_auth_scheme_config(
|
|||
.ok_or_else(|| {
|
||||
AuthOrchestrationError::auth_scheme_endpoint_config_mismatch(auth_schemes.iter())
|
||||
})?;
|
||||
Ok(AuthSchemeEndpointConfig::new(Some(auth_scheme_config)))
|
||||
Ok(AuthSchemeEndpointConfig::from(Some(auth_scheme_config)))
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "test-util"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aws_smithy_http::body::SdkBody;
|
||||
use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver;
|
||||
use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver;
|
||||
use aws_smithy_runtime_api::client::auth::{
|
||||
AuthOptionResolverParams, AuthSchemeId, HttpAuthScheme, HttpRequestSigner,
|
||||
SharedAuthOptionResolver, SharedHttpAuthScheme,
|
||||
AuthScheme, AuthSchemeId, AuthSchemeOptionResolverParams, SharedAuthScheme,
|
||||
SharedAuthSchemeOptionResolver, Signer,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::identity::{
|
||||
Identity, IdentityResolver, SharedIdentityResolver,
|
||||
|
@ -181,8 +183,8 @@ mod tests {
|
|||
#[derive(Debug)]
|
||||
struct TestSigner;
|
||||
|
||||
impl HttpRequestSigner for TestSigner {
|
||||
fn sign_request(
|
||||
impl Signer for TestSigner {
|
||||
fn sign_http_request(
|
||||
&self,
|
||||
request: &mut HttpRequest,
|
||||
_identity: &Identity,
|
||||
|
@ -203,7 +205,7 @@ mod tests {
|
|||
struct TestAuthScheme {
|
||||
signer: TestSigner,
|
||||
}
|
||||
impl HttpAuthScheme for TestAuthScheme {
|
||||
impl AuthScheme for TestAuthScheme {
|
||||
fn scheme_id(&self) -> AuthSchemeId {
|
||||
TEST_SCHEME_ID
|
||||
}
|
||||
|
@ -215,7 +217,7 @@ mod tests {
|
|||
identity_resolvers.identity_resolver(self.scheme_id())
|
||||
}
|
||||
|
||||
fn request_signer(&self) -> &dyn HttpRequestSigner {
|
||||
fn signer(&self) -> &dyn Signer {
|
||||
&self.signer
|
||||
}
|
||||
}
|
||||
|
@ -227,21 +229,19 @@ mod tests {
|
|||
ctx.enter_before_transmit_phase();
|
||||
|
||||
let runtime_components = RuntimeComponentsBuilder::for_tests()
|
||||
.with_auth_option_resolver(Some(SharedAuthOptionResolver::new(
|
||||
StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID]),
|
||||
.with_auth_scheme(SharedAuthScheme::new(TestAuthScheme { signer: TestSigner }))
|
||||
.with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new(
|
||||
StaticAuthSchemeOptionResolver::new(vec![TEST_SCHEME_ID]),
|
||||
)))
|
||||
.with_identity_resolver(
|
||||
TEST_SCHEME_ID,
|
||||
SharedIdentityResolver::new(TestIdentityResolver),
|
||||
)
|
||||
.with_http_auth_scheme(SharedHttpAuthScheme::new(TestAuthScheme {
|
||||
signer: TestSigner,
|
||||
}))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut layer: Layer = Layer::new("test");
|
||||
layer.store_put(AuthOptionResolverParams::new("doesntmatter"));
|
||||
layer.store_put(AuthSchemeOptionResolverParams::new("doesntmatter"));
|
||||
layer.store_put(Endpoint::builder().url("dontcare").build());
|
||||
let cfg = ConfigBag::of_layers(vec![layer]);
|
||||
|
||||
|
@ -279,10 +279,10 @@ mod tests {
|
|||
identity: impl IdentityResolver + 'static,
|
||||
) -> (RuntimeComponents, ConfigBag) {
|
||||
let runtime_components = RuntimeComponentsBuilder::for_tests()
|
||||
.with_http_auth_scheme(SharedHttpAuthScheme::new(BasicAuthScheme::new()))
|
||||
.with_http_auth_scheme(SharedHttpAuthScheme::new(BearerAuthScheme::new()))
|
||||
.with_auth_option_resolver(Some(SharedAuthOptionResolver::new(
|
||||
StaticAuthOptionResolver::new(vec![
|
||||
.with_auth_scheme(SharedAuthScheme::new(BasicAuthScheme::new()))
|
||||
.with_auth_scheme(SharedAuthScheme::new(BearerAuthScheme::new()))
|
||||
.with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new(
|
||||
StaticAuthSchemeOptionResolver::new(vec![
|
||||
HTTP_BASIC_AUTH_SCHEME_ID,
|
||||
HTTP_BEARER_AUTH_SCHEME_ID,
|
||||
]),
|
||||
|
@ -293,7 +293,7 @@ mod tests {
|
|||
|
||||
let mut layer = Layer::new("test");
|
||||
layer.store_put(Endpoint::builder().url("dontcare").build());
|
||||
layer.store_put(AuthOptionResolverParams::new("doesntmatter"));
|
||||
layer.store_put(AuthSchemeOptionResolverParams::new("doesntmatter"));
|
||||
|
||||
(runtime_components, ConfigBag::of_layers(vec![layer]))
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ mod tests {
|
|||
.build();
|
||||
let config = extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into())
|
||||
.expect("success");
|
||||
assert!(config.config().is_none());
|
||||
assert!(config.as_document().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -412,7 +412,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
"magic string value",
|
||||
config
|
||||
.config()
|
||||
.as_document()
|
||||
.expect("config is set")
|
||||
.as_object()
|
||||
.expect("it's an object")
|
||||
|
|
|
@ -9,11 +9,9 @@ use aws_smithy_http::endpoint::{
|
|||
SharedEndpointResolver,
|
||||
};
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors;
|
||||
use aws_smithy_runtime_api::client::endpoint::{EndpointResolver, EndpointResolverParams};
|
||||
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
|
||||
use aws_smithy_runtime_api::client::orchestrator::{
|
||||
EndpointResolver, EndpointResolverParams, Future, HttpRequest,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest};
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
||||
use aws_smithy_types::endpoint::Endpoint;
|
||||
|
@ -109,7 +107,9 @@ pub(super) async fn orchestrate_endpoint(
|
|||
) -> Result<(), BoxError> {
|
||||
trace!("orchestrating endpoint resolution");
|
||||
|
||||
let params = cfg.endpoint_resolver_params();
|
||||
let params = cfg
|
||||
.load::<EndpointResolverParams>()
|
||||
.expect("endpoint resolver params must be set");
|
||||
let endpoint_prefix = cfg.load::<EndpointPrefix>();
|
||||
let request = ctx.request_mut().expect("set during serialization");
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
|
||||
use aws_smithy_runtime_api::client::request_attempts::RequestAttempts;
|
||||
use aws_smithy_runtime_api::client::retries::{
|
||||
ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt,
|
||||
ClassifyRetry, RequestAttempts, RetryReason, RetryStrategy, ShouldAttempt,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
use aws_smithy_types::config_bag::ConfigBag;
|
||||
|
|
|
@ -10,9 +10,8 @@ use crate::client::retries::strategy::standard::ReleaseResult::{
|
|||
use crate::client::retries::token_bucket::TokenBucket;
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
|
||||
use aws_smithy_runtime_api::client::request_attempts::RequestAttempts;
|
||||
use aws_smithy_runtime_api::client::retries::{
|
||||
ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt,
|
||||
ClassifyRetry, RequestAttempts, RetryReason, RetryStrategy, ShouldAttempt,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
||||
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::{Error, Output};
|
||||
use aws_smithy_runtime_api::client::orchestrator::{
|
||||
DynResponseDeserializer, HttpResponse, OrchestratorError, ResponseDeserializer,
|
||||
};
|
||||
use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError};
|
||||
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
|
||||
use aws_smithy_runtime_api::client::ser_de::{ResponseDeserializer, SharedResponseDeserializer};
|
||||
use aws_smithy_types::config_bag::{FrozenLayer, Layer};
|
||||
use std::sync::Mutex;
|
||||
|
||||
|
@ -46,7 +44,7 @@ impl ResponseDeserializer for CannedResponseDeserializer {
|
|||
impl RuntimePlugin for CannedResponseDeserializer {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
let mut cfg = Layer::new("CannedResponse");
|
||||
cfg.set_response_deserializer(DynResponseDeserializer::new(Self {
|
||||
cfg.store_put(SharedResponseDeserializer::new(Self {
|
||||
inner: Mutex::new(self.take()),
|
||||
}));
|
||||
Some(cfg.freeze())
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
*/
|
||||
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors;
|
||||
use aws_smithy_runtime_api::client::interceptors::context::Input;
|
||||
use aws_smithy_runtime_api::client::orchestrator::SharedRequestSerializer;
|
||||
use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, RequestSerializer};
|
||||
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
|
||||
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
|
||||
use aws_smithy_runtime_api::client::ser_de::{RequestSerializer, SharedRequestSerializer};
|
||||
use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer};
|
||||
use std::sync::Mutex;
|
||||
|
||||
|
@ -52,7 +51,7 @@ impl RequestSerializer for CannedRequestSerializer {
|
|||
impl RuntimePlugin for CannedRequestSerializer {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
let mut cfg = Layer::new("CannedRequest");
|
||||
cfg.set_request_serializer(SharedRequestSerializer::new(Self {
|
||||
cfg.store_put(SharedRequestSerializer::new(Self {
|
||||
inner: Mutex::new(self.take()),
|
||||
}));
|
||||
Some(cfg.freeze())
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
)]
|
||||
|
||||
/// Runtime support logic for generated clients.
|
||||
#[cfg(feature = "client")]
|
||||
pub mod client;
|
||||
|
||||
pub mod static_partition_map;
|
||||
|
|
|
@ -22,7 +22,7 @@ async-trait = "0.1"
|
|||
aws-smithy-http = { path = "../aws-smithy-http" }
|
||||
aws-smithy-http-server = { path = "../aws-smithy-http-server" }
|
||||
aws-smithy-json = { path = "../aws-smithy-json" }
|
||||
aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" }
|
||||
aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["client"] }
|
||||
aws-smithy-types = { path = "../aws-smithy-types" }
|
||||
aws-smithy-xml = { path = "../aws-smithy-xml" }
|
||||
bytes = "1"
|
||||
|
|
Loading…
Reference in New Issue