Delete `aws_smithy_http::ResolveEndpoint` and point usages to service-specific trait (#3078)

## Motivation and Context
- Fixes https://github.com/awslabs/smithy-rs/issues/3043

As a follow up to #3072 this removes the old endpoint resolver
interfaces in favor of creating a per-service resolver trait.

This trait defines a `into_shared_resolver()` method which converts the
local trait into a global resolver that can be used with the
orchestrator.

## Description
<!--- Describe your changes in detail -->

## Testing
<!--- Please describe in detail how you tested your changes -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS
SDK, generated SDK code, or SDK runtime crates

----

_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:
Russell Cohen 2023-10-18 16:57:56 -04:00 committed by GitHub
parent bcfc211277
commit 12fa4d3963
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 208 additions and 460 deletions

View File

@ -414,3 +414,15 @@ message = "The `idempotency_provider` field has been removed from config as a pu
references = ["smithy-rs#3072"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "rcoh"
[[smithy-rs]]
message = "The `config::Builder::endpoint_resolver` method no longer accepts `&'static str`. Use `config::Builder::endpoint_url` instead."
references = ["smithy-rs#3078"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "rcoh"
[[smithy-rs]]
message = "**This change has [detailed upgrade guidance](https://github.com/awslabs/smithy-rs/discussions/3079).** <br><br>The endpoint interfaces from `aws-smithy-http` have been removed. Service-specific endpoint resolver traits have been added."
references = ["smithy-rs#3043", "smithy-rs#3078"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "rcoh"

View File

@ -8,7 +8,10 @@
use aws_smithy_async::future::BoxFuture;
use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep};
use aws_smithy_async::time::SharedTimeSource;
use aws_smithy_http::endpoint::{ResolveEndpoint, ResolveEndpointError};
use aws_smithy_runtime_api::box_error::BoxError;
use aws_smithy_runtime_api::client::endpoint::{
EndpointFuture, EndpointResolverParams, ResolveEndpoint,
};
use aws_smithy_types::endpoint::Endpoint;
use std::fmt::{Debug, Formatter};
use std::future::Future;
@ -20,11 +23,9 @@ use tokio::sync::oneshot::{Receiver, Sender};
/// Endpoint reloader
#[must_use]
pub struct ReloadEndpoint {
loader: Box<
dyn Fn() -> BoxFuture<'static, (Endpoint, SystemTime), ResolveEndpointError> + Send + Sync,
>,
loader: Box<dyn Fn() -> BoxFuture<'static, (Endpoint, SystemTime), BoxError> + Send + Sync>,
endpoint: Arc<Mutex<Option<ExpiringEndpoint>>>,
error: Arc<Mutex<Option<ResolveEndpointError>>>,
error: Arc<Mutex<Option<BoxError>>>,
rx: Receiver<()>,
sleep: SharedAsyncSleep,
time: SharedTimeSource,
@ -79,14 +80,14 @@ impl ReloadEndpoint {
#[derive(Debug, Clone)]
pub(crate) struct EndpointCache {
error: Arc<Mutex<Option<ResolveEndpointError>>>,
error: Arc<Mutex<Option<BoxError>>>,
endpoint: Arc<Mutex<Option<ExpiringEndpoint>>>,
// When the sender is dropped, this allows the reload loop to stop
_drop_guard: Arc<Sender<()>>,
}
impl<T> ResolveEndpoint<T> for EndpointCache {
fn resolve_endpoint(&self, _params: &T) -> aws_smithy_http::endpoint::Result {
impl ResolveEndpoint for EndpointCache {
fn resolve_endpoint<'a>(&'a self, _params: &'a EndpointResolverParams) -> EndpointFuture<'a> {
self.resolve_endpoint()
}
}
@ -111,9 +112,9 @@ pub(crate) async fn create_cache<F>(
loader_fn: impl Fn() -> F + Send + Sync + 'static,
sleep: SharedAsyncSleep,
time: SharedTimeSource,
) -> Result<(EndpointCache, ReloadEndpoint), ResolveEndpointError>
) -> Result<(EndpointCache, ReloadEndpoint), BoxError>
where
F: Future<Output = Result<(Endpoint, SystemTime), ResolveEndpointError>> + Send + 'static,
F: Future<Output = Result<(Endpoint, SystemTime), BoxError>> + Send + 'static,
{
let error_holder = Arc::new(Mutex::new(None));
let endpoint_holder = Arc::new(Mutex::new(None));
@ -135,25 +136,24 @@ where
reloader.reload_once().await;
// if we didn't successfully get an endpoint, bail out so the client knows
// configuration failed to work
cache.resolve_endpoint()?;
cache.resolve_endpoint().await?;
Ok((cache, reloader))
}
impl EndpointCache {
fn resolve_endpoint(&self) -> aws_smithy_http::endpoint::Result {
fn resolve_endpoint(&self) -> EndpointFuture<'_> {
tracing::trace!("resolving endpoint from endpoint discovery cache");
self.endpoint
let ep = self
.endpoint
.lock()
.unwrap()
.as_ref()
.map(|e| e.endpoint.clone())
.ok_or_else(|| {
self.error
.lock()
.unwrap()
.take()
.unwrap_or_else(|| ResolveEndpointError::message("no endpoint loaded"))
})
let error: Option<BoxError> = self.error.lock().unwrap().take();
error.unwrap_or_else(|| "Failed to resolve endpoint".into())
});
EndpointFuture::ready(ep)
}
}
@ -215,7 +215,7 @@ mod test {
.await
.expect("returns an endpoint");
assert_eq!(
cache.resolve_endpoint().expect("ok").url(),
cache.resolve_endpoint().await.expect("ok").url(),
"http://foo.com/1"
);
// 120 second buffer
@ -223,13 +223,13 @@ mod test {
.reload_increment(expiry - Duration::from_secs(240))
.await;
assert_eq!(
cache.resolve_endpoint().expect("ok").url(),
cache.resolve_endpoint().await.expect("ok").url(),
"http://foo.com/1"
);
reloader.reload_increment(expiry).await;
assert_eq!(
cache.resolve_endpoint().expect("ok").url(),
cache.resolve_endpoint().await.expect("ok").url(),
"http://foo.com/2"
);
}
@ -266,18 +266,27 @@ mod test {
gate.expect_sleep().await.duration(),
Duration::from_secs(60)
);
assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/1");
assert_eq!(
cache.resolve_endpoint().await.unwrap().url(),
"http://foo.com/1"
);
// t = 60
let sleep = gate.expect_sleep().await;
// we're still holding the drop guard, so we haven't expired yet.
assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/1");
assert_eq!(
cache.resolve_endpoint().await.unwrap().url(),
"http://foo.com/1"
);
assert_eq!(sleep.duration(), Duration::from_secs(60));
sleep.allow_progress();
// t = 120
let sleep = gate.expect_sleep().await;
assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/2");
assert_eq!(
cache.resolve_endpoint().await.unwrap().url(),
"http://foo.com/2"
);
sleep.allow_progress();
let sleep = gate.expect_sleep().await;

View File

@ -55,11 +55,9 @@ class TimestreamDecorator : ClientCodegenDecorator {
// helper function to resolve an endpoint given a base client
rustTemplate(
"""
async fn resolve_endpoint(client: &crate::Client) -> Result<(#{Endpoint}, #{SystemTime}), #{ResolveEndpointError}> {
async fn resolve_endpoint(client: &crate::Client) -> Result<(#{Endpoint}, #{SystemTime}), #{BoxError}> {
let describe_endpoints =
client.describe_endpoints().send().await.map_err(|e| {
#{ResolveEndpointError}::from_source("failed to call describe_endpoints", e)
})?;
client.describe_endpoints().send().await?;
let endpoint = describe_endpoints.endpoints().get(0).unwrap();
let expiry = client.config().time_source().expect("checked when ep discovery was enabled").now()
+ #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60);
@ -75,7 +73,7 @@ class TimestreamDecorator : ClientCodegenDecorator {
/// Enable endpoint discovery for this client
///
/// This method MUST be called to construct a working client.
pub async fn with_endpoint_discovery_enabled(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> {
pub async fn with_endpoint_discovery_enabled(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{BoxError}> {
let handle = self.handle.clone();
// The original client without endpoint discover gets moved into the endpoint discovery
@ -92,11 +90,11 @@ class TimestreamDecorator : ClientCodegenDecorator {
.expect("endpoint discovery requires the client config to have a time source"),
).await?;
let client_with_discovery = crate::Client::from_conf(
handle.conf.to_builder()
.endpoint_resolver(#{SharedEndpointResolver}::new(resolver))
.build()
);
use #{IntoShared};
let mut conf = handle.conf.to_builder();
conf.set_endpoint_resolver(Some(resolver.into_shared()));
let client_with_discovery = crate::Client::from_conf(conf.build());
Ok((client_with_discovery, reloader))
}
}
@ -104,10 +102,10 @@ class TimestreamDecorator : ClientCodegenDecorator {
*RuntimeType.preludeScope,
"Arc" to RuntimeType.Arc,
"Duration" to RuntimeType.std.resolve("time::Duration"),
"SharedEndpointResolver" to RuntimeType.smithyHttp(codegenContext.runtimeConfig)
.resolve("endpoint::SharedEndpointResolver"),
"SystemTime" to RuntimeType.std.resolve("time::SystemTime"),
"endpoint_discovery" to endpointDiscovery.toType(),
"BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig),
"IntoShared" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig).resolve("shared::IntoShared"),
*Types(codegenContext.runtimeConfig).toArray(),
)
}

View File

@ -104,7 +104,7 @@ class ClientModuleDocProvider(
ClientRustModule.Config.endpoint -> strDoc("Types needed to configure endpoint resolution.")
ClientRustModule.Config.retry -> strDoc("Retry configuration.")
ClientRustModule.Config.timeout -> strDoc("Timeout configuration.")
ClientRustModule.Config.interceptors -> strDoc("Types needed to implement [`Interceptor`](crate::config::Interceptor).")
ClientRustModule.Config.interceptors -> strDoc("Types needed to implement [`Intercept`](crate::config::Intercept).")
ClientRustModule.Error -> strDoc("Common errors and error handling utilities.")
ClientRustModule.Operation -> strDoc("All operations that this crate can perform.")
ClientRustModule.Meta -> strDoc("Information about this crate.")

View File

@ -7,10 +7,10 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint
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.endpoint.generators.serviceSpecificEndpointResolver
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
@ -26,23 +26,21 @@ internal class EndpointConfigCustomization(
ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val moduleUseName = codegenContext.moduleUseName()
private val types = Types(runtimeConfig)
private val epModule = RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint")
private val epRuntimeModule = RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints")
private val codegenScope = arrayOf(
*preludeScope,
"DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"),
"Endpoint" to RuntimeType.smithyHttp(runtimeConfig).resolve("endpoint::Endpoint"),
"OldSharedEndpointResolver" to types.sharedEndpointResolver,
"Params" to typesGenerator.paramsStruct(),
"IntoShared" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("shared::IntoShared"),
"Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"),
"SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint::SharedEndpointResolver"),
"SmithyResolver" to types.resolveEndpoint,
"SharedEndpointResolver" to epModule.resolve("SharedEndpointResolver"),
"StaticUriEndpointResolver" to epRuntimeModule.resolve("StaticUriEndpointResolver"),
"ServiceSpecificResolver" to codegenContext.serviceSpecificEndpointResolver(),
)
override fun section(section: ServiceConfig): Writable {
return writable {
val sharedEndpointResolver = "#{OldSharedEndpointResolver}<#{Params}>"
val resolverTrait = "#{SmithyResolver}<#{Params}>"
when (section) {
is ServiceConfig.ConfigImpl -> {
rustTemplate(
@ -57,44 +55,16 @@ internal class EndpointConfigCustomization(
}
ServiceConfig.BuilderImpl -> {
val endpointModule = ClientRustModule.Config.endpoint.fullyQualifiedPath()
.replace("crate::", "$moduleUseName::")
// if there are no rules, we don't generate a default resolver—we need to also suppress those docs.
val defaultResolverDocs = if (typesGenerator.defaultResolver() != null) {
val endpointModule = ClientRustModule.Config.endpoint.fullyQualifiedPath()
.replace("crate::", "$moduleUseName::")
"""
///
/// When unset, the client will used a generated endpoint resolver based on the endpoint resolution
/// rules for `$moduleUseName`.
///
/// ## Examples
/// ```no_run
/// use aws_smithy_http::endpoint;
/// use $endpointModule::{Params as EndpointParams, DefaultResolver};
/// /// Endpoint resolver which adds a prefix to the generated endpoint
/// ##[derive(Debug)]
/// struct PrefixResolver {
/// base_resolver: DefaultResolver,
/// prefix: String
/// }
/// impl endpoint::ResolveEndpoint<EndpointParams> for PrefixResolver {
/// fn resolve_endpoint(&self, params: &EndpointParams) -> endpoint::Result {
/// self.base_resolver
/// .resolve_endpoint(params)
/// .map(|ep|{
/// let url = ep.url().to_string();
/// ep.into_builder().url(format!("{}.{}", &self.prefix, url)).build()
/// })
/// }
/// }
/// let prefix_resolver = PrefixResolver {
/// base_resolver: DefaultResolver::new(),
/// prefix: "subdomain".to_string()
/// };
/// let config = $moduleUseName::Config::builder().endpoint_resolver(prefix_resolver);
/// ```
"""
} else {
""
"/// This service does not define a default endpoint resolver."
}
if (codegenContext.settings.codegenConfig.includeEndpointUrlConfig) {
rustTemplate(
@ -120,9 +90,8 @@ internal class EndpointConfigCustomization(
##[allow(deprecated)]
self.set_endpoint_resolver(
endpoint_url.map(|url| {
#{OldSharedEndpointResolver}::new(
#{Endpoint}::immutable(url).expect("invalid endpoint URL")
)
use #{IntoShared};
#{StaticUriEndpointResolver}::uri(url).into_shared()
})
);
self
@ -135,31 +104,48 @@ internal class EndpointConfigCustomization(
"""
/// Sets the endpoint resolver to use when making requests.
///
/// Note: setting an endpoint resolver will replace any endpoint URL that has been set.
///
$defaultResolverDocs
pub fn endpoint_resolver(mut self, endpoint_resolver: impl $resolverTrait + 'static) -> Self {
self.set_endpoint_resolver(#{Some}(#{OldSharedEndpointResolver}::new(endpoint_resolver)));
///
/// Note: setting an endpoint resolver will replace any endpoint URL that has been set.
/// This method accepts an endpoint resolver [specific to this service](#{ServiceSpecificResolver}). If you want to
/// provide a shared endpoint resolver, use [`Self::set_endpoint_resolver`].
///
/// ## Examples
/// Create a custom endpoint resolver that resolves a different endpoing per-stage, e.g. staging vs. production.
/// ```no_run
/// use $endpointModule::{ResolveEndpoint, EndpointFuture, Params, Endpoint};
/// ##[derive(Debug)]
/// struct StageResolver { stage: String }
/// impl ResolveEndpoint for StageResolver {
/// fn resolve_endpoint(&self, params: &Params) -> EndpointFuture<'_> {
/// let stage = &self.stage;
/// EndpointFuture::ready(Ok(Endpoint::builder().url(format!("{stage}.myservice.com")).build()))
/// }
/// }
/// let resolver = StageResolver { stage: std::env::var("STAGE").unwrap() };
/// let config = $moduleUseName::Config::builder().endpoint_resolver(resolver).build();
/// let client = $moduleUseName::Client::from_conf(config);
/// ```
pub fn endpoint_resolver(mut self, endpoint_resolver: impl #{ServiceSpecificResolver} + 'static) -> Self {
self.set_endpoint_resolver(#{Some}(endpoint_resolver.into_shared_resolver()));
self
}
/// Sets the endpoint resolver to use when making requests.
///
/// When unset, the client will used a generated endpoint resolver based on the endpoint resolution
/// rules for `$moduleUseName`.
$defaultResolverDocs
""",
*codegenScope,
)
rustTemplate(
"""
pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self {
self.runtime_components.set_endpoint_resolver(endpoint_resolver.map(|r|#{wrap_resolver}));
pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<#{SharedEndpointResolver}>) -> &mut Self {
self.runtime_components.set_endpoint_resolver(endpoint_resolver);
self
}
""",
*codegenScope,
"wrap_resolver" to codegenContext.wrapResolver { rust("r") },
)
}

View File

@ -53,7 +53,6 @@ class EndpointTypesGenerator(
it,
params,
codegenContext = codegenContext,
endpointCustomizations = codegenContext.rootDecorator.endpointCustomizations(codegenContext),
).generate()
}
?: {}

View File

@ -12,15 +12,14 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.CustomRuntimeFunction
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.endpointTestsModule
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.serviceSpecificEndpointResolver
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.SmithyEndpointsStdLib
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.map
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
/**
@ -118,8 +117,9 @@ class EndpointsDecorator : ClientCodegenDecorator {
override fun section(section: ServiceRuntimePluginSection): Writable {
return when (section) {
is ServiceRuntimePluginSection.RegisterRuntimeComponents -> writable {
codegenContext.defaultEndpointResolver()
?.let { resolver -> section.registerEndpointResolver(this, resolver) }
codegenContext.defaultEndpointResolver()?.also { resolver ->
section.registerEndpointResolver(this, resolver)
}
}
else -> emptySection
@ -138,28 +138,22 @@ class EndpointsDecorator : ClientCodegenDecorator {
}
}
/**
* Returns the rules-generated endpoint resolver for this service
*
* If no endpoint rules are provided, `null` will be returned.
*/
private fun ClientCodegenContext.defaultEndpointResolver(): Writable? {
val generator = EndpointTypesGenerator.fromContext(this)
val defaultResolver = generator.defaultResolver() ?: return null
val ctx = arrayOf("DefaultResolver" to defaultResolver)
return wrapResolver { rustTemplate("#{DefaultResolver}::new()", *ctx) }
}
fun ClientCodegenContext.wrapResolver(resolver: Writable): Writable {
val generator = EndpointTypesGenerator.fromContext(this)
return resolver.map { base ->
val types = Types(runtimeConfig)
val ctx = arrayOf(
"DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig)
.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"),
"Params" to generator.paramsStruct(),
"OldSharedEndpointResolver" to types.sharedEndpointResolver,
)
val ctx = arrayOf("DefaultResolver" to defaultResolver, "ServiceSpecificResolver" to serviceSpecificEndpointResolver())
return writable {
rustTemplate(
"#{DefaultEndpointResolver}::<#{Params}>::new(#{OldSharedEndpointResolver}::new(#{base}))",
"""{
use #{ServiceSpecificResolver};
#{DefaultResolver}::new().into_shared_resolver()
}""",
*ctx,
"base" to base,
)
}
}

View File

@ -57,12 +57,18 @@ internal fun endpointsLib(name: String, vararg additionalDependency: RustDepende
class Types(runtimeConfig: RuntimeConfig) {
private val smithyTypesEndpointModule = RuntimeType.smithyTypes(runtimeConfig).resolve("endpoint")
val smithyHttpEndpointModule = RuntimeType.smithyHttp(runtimeConfig).resolve("endpoint")
val resolveEndpoint = smithyHttpEndpointModule.resolve("ResolveEndpoint")
val sharedEndpointResolver = smithyHttpEndpointModule.resolve("SharedEndpointResolver")
val smithyEndpoint = smithyTypesEndpointModule.resolve("Endpoint")
val endpointFuture = RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint::EndpointFuture")
private val endpointRtApi = RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint")
val resolveEndpointError = smithyHttpEndpointModule.resolve("ResolveEndpointError")
fun toArray() = arrayOf("ResolveEndpointError" to resolveEndpointError, "Endpoint" to smithyEndpoint)
fun toArray() = arrayOf(
"Endpoint" to smithyEndpoint,
"EndpointFuture" to endpointFuture,
"SharedEndpointResolver" to endpointRtApi.resolve("SharedEndpointResolver"),
"EndpointResolverParams" to endpointRtApi.resolve("EndpointResolverParams"),
"ResolveEndpoint" to endpointRtApi.resolve("ResolveEndpoint"),
)
}
/**
@ -98,7 +104,8 @@ class AuthSchemeLister : RuleValueVisitor<Set<String>> {
}
override fun visitEndpointRule(endpoint: Endpoint): Set<String> {
return endpoint.properties.getOrDefault(Identifier.of("authSchemes"), Literal.tupleLiteral(listOf())).asTupleLiteral()
return endpoint.properties.getOrDefault(Identifier.of("authSchemes"), Literal.tupleLiteral(listOf()))
.asTupleLiteral()
.orNull()?.let {
it.map { authScheme ->
authScheme.asRecordLiteral().get()[Identifier.of("name")]!!.asStringLiteral().get().expectLiteral()

View File

@ -18,6 +18,7 @@ import software.amazon.smithy.rulesengine.language.syntax.rule.RuleValueVisitor
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.endpoint.Context
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.endpointsLib
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.memberName
@ -36,8 +37,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.toType
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rust.codegen.core.util.dq
import software.amazon.smithy.rust.codegen.core.util.orNull
import software.amazon.smithy.rust.codegen.core.util.serviceNameOrDefault
abstract class CustomRuntimeFunction {
abstract val id: String
@ -128,9 +131,13 @@ internal class EndpointResolverGenerator(
private val registry: FunctionRegistry = FunctionRegistry(stdlib)
private val types = Types(runtimeConfig)
private val codegenScope = arrayOf(
"BoxError" to RuntimeType.boxError(runtimeConfig),
"endpoint" to types.smithyHttpEndpointModule,
"SmithyEndpoint" to types.smithyEndpoint,
"EndpointFuture" to types.endpointFuture,
"ResolveEndpointError" to types.resolveEndpointError,
"EndpointError" to types.resolveEndpointError,
"ServiceSpecificEndpointResolver" to codegenContext.serviceSpecificEndpointResolver(),
"DiagnosticCollector" to endpointsLib("diagnostic").toType().resolve("DiagnosticCollector"),
)
@ -183,13 +190,17 @@ internal class EndpointResolverGenerator(
pub fn new() -> Self {
Self { #{custom_fields_init:W} }
}
fn resolve_endpoint(&self, params: &#{Params}) -> Result<#{SmithyEndpoint}, #{BoxError}> {
let mut diagnostic_collector = #{DiagnosticCollector}::new();
Ok(#{resolver_fn}(params, &mut diagnostic_collector, #{additional_args})
.map_err(|err|err.with_source(diagnostic_collector.take_last_error()))?)
}
}
impl #{endpoint}::ResolveEndpoint<#{Params}> for DefaultResolver {
fn resolve_endpoint(&self, params: &Params) -> #{endpoint}::Result {
let mut diagnostic_collector = #{DiagnosticCollector}::new();
#{resolver_fn}(params, &mut diagnostic_collector, #{additional_args})
.map_err(|err|err.with_source(diagnostic_collector.take_last_error()))
impl #{ServiceSpecificEndpointResolver} for DefaultResolver {
fn resolve_endpoint(&self, params: &#{Params}) -> #{EndpointFuture} {
#{EndpointFuture}::ready(self.resolve_endpoint(params))
}
}
""",
@ -368,3 +379,46 @@ internal class EndpointResolverGenerator(
}
}
}
fun ClientCodegenContext.serviceSpecificEndpointResolver(): RuntimeType {
val generator = EndpointTypesGenerator.fromContext(this)
return RuntimeType.forInlineFun("ResolveEndpoint", ClientRustModule.Config.endpoint) {
val ctx = arrayOf(*preludeScope, "Params" to generator.paramsStruct(), *Types(runtimeConfig).toArray(), "Debug" to RuntimeType.Debug)
rustTemplate(
"""
/// Endpoint resolver trait specific to ${serviceShape.serviceNameOrDefault("this service")}
pub trait ResolveEndpoint: #{Send} + #{Sync} + #{Debug} {
/// Resolve an endpoint with the given parameters
fn resolve_endpoint<'a>(&'a self, params: &'a #{Params}) -> #{EndpointFuture}<'a>;
/// Convert this service-specific resolver into a `SharedEndpointResolver`
///
/// The resulting resolver will downcast `EndpointResolverParams` into `#{Params}`.
fn into_shared_resolver(self) -> #{SharedEndpointResolver}
where
Self: Sized + 'static,
{
#{SharedEndpointResolver}::new(DowncastParams(self))
}
}
##[derive(Debug)]
struct DowncastParams<T>(T);
impl<T> #{ResolveEndpoint} for DowncastParams<T>
where
T: ResolveEndpoint,
{
fn resolve_endpoint<'a>(&'a self, params: &'a #{EndpointResolverParams}) -> #{EndpointFuture}<'a> {
let ep = match params.get::<#{Params}>() {
Some(params) => self.0.resolve_endpoint(params),
None => #{EndpointFuture}::ready(Err("params of expected type was not present".into())),
};
ep
}
}
""",
*ctx,
)
}
}

View File

@ -16,7 +16,6 @@ import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters
import software.amazon.smithy.rulesengine.traits.EndpointTestCase
import software.amazon.smithy.rulesengine.traits.ExpectedEndpoint
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName
import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator
@ -38,16 +37,12 @@ internal class EndpointTestGenerator(
private val paramsType: RuntimeType,
private val resolverType: RuntimeType,
private val params: Parameters,
private val endpointCustomizations: List<EndpointCustomization>,
codegenContext: ClientCodegenContext,
) {
private val runtimeConfig = codegenContext.runtimeConfig
private val serviceShape = codegenContext.serviceShape
private val model = codegenContext.model
private val types = Types(runtimeConfig)
private val codegenScope = arrayOf(
"Endpoint" to types.smithyEndpoint,
"ResolveEndpoint" to types.resolveEndpoint,
"Error" to types.resolveEndpointError,
"Document" to RuntimeType.document(runtimeConfig),
"HashMap" to RuntimeType.HashMap,
@ -67,7 +62,6 @@ internal class EndpointTestGenerator(
#{docs:W}
##[test]
fn test_$id() {
use #{ResolveEndpoint};
let params = #{params:W};
let resolver = #{resolver}::new();
let endpoint = resolver.resolve_endpoint(&params);

View File

@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators
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.endpoint.Types
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
@ -49,11 +50,11 @@ class ClientRuntimeTypesReExportGenerator(
rustCrate.withModule(ClientRustModule.Config.endpoint) {
rustTemplate(
"""
pub use #{ResolveEndpoint};
pub use #{SharedEndpointResolver};
pub use #{EndpointFuture};
pub use #{Endpoint};
""",
"ResolveEndpoint" to RuntimeType.smithyHttp(rc).resolve("endpoint::ResolveEndpoint"),
"SharedEndpointResolver" to RuntimeType.smithyHttp(rc).resolve("endpoint::SharedEndpointResolver"),
*Types(rc).toArray(),
)
}
rustCrate.withModule(ClientRustModule.Config.retry) {

View File

@ -208,7 +208,7 @@ class DefaultProtocolTestGenerator(
rustTemplate(
"""
let (http_client, request_receiver) = #{capture_request}(None);
let config_builder = #{config}::Config::builder().with_test_defaults().endpoint_resolver($host);
let config_builder = #{config}::Config::builder().with_test_defaults().endpoint_url($host);
#{customParams}
""",

View File

@ -49,7 +49,7 @@ class HttpAuthDecoratorTest {
let config = $moduleName::Config::builder()
.api_key(Token::new("some-api-key", None))
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);
@ -81,7 +81,7 @@ class HttpAuthDecoratorTest {
let config = $moduleName::Config::builder()
.basic_auth_login(Login::new("some-user", "some-pass", None))
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);
@ -121,7 +121,7 @@ class HttpAuthDecoratorTest {
let config = $moduleName::Config::builder()
.api_key(Token::new("some-api-key", None))
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);
@ -162,7 +162,7 @@ class HttpAuthDecoratorTest {
let config = $moduleName::Config::builder()
.api_key(Token::new("some-api-key", None))
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);
@ -203,7 +203,7 @@ class HttpAuthDecoratorTest {
let config = $moduleName::Config::builder()
.basic_auth_login(Login::new("some-user", "some-pass", None))
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);
@ -244,7 +244,7 @@ class HttpAuthDecoratorTest {
let config = $moduleName::Config::builder()
.bearer_token(Token::new("some-token", None))
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);
@ -281,7 +281,7 @@ class HttpAuthDecoratorTest {
);
let config = $moduleName::Config::builder()
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);

View File

@ -83,7 +83,7 @@ class MetadataCustomizationTest {
let (http_client, _captured_request) = #{capture_request}(#{None});
let client_config = crate::config::Config::builder()
.endpoint_resolver("http://localhost:1234/")
.endpoint_url("http://localhost:1234/")
.http_client(http_client)
.build();
let client = crate::client::Client::from_conf(client_config);

View File

@ -63,7 +63,7 @@ class SensitiveOutputDecoratorTest {
));
let config = $moduleName::Config::builder()
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(http_client.clone())
.build();
let client = $moduleName::Client::from_conf(config);

View File

@ -56,7 +56,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
let (http_client, req) = #{capture_request}(None);
let client_config = crate::config::Config::builder().http_client(http_client).build();
let config_override =
crate::config::Config::builder().endpoint_resolver(expected_url);
crate::config::Config::builder().endpoint_url(expected_url);
let client = crate::Client::from_conf(client_config);
let _ = dbg!(client.say_hello().customize().config_override(config_override).send().await);
assert_eq!("http://localhost:1234/", req.expect_request().uri());
@ -86,7 +86,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest {
let (http_client, captured_request) = #{capture_request}(#{None});
let expected_url = "http://localhost:1234/";
let client_config = crate::config::Config::builder()
.endpoint_resolver(expected_url)
.endpoint_url(expected_url)
.http_client(#{NeverClient}::new())
.build();
let client = crate::client::Client::from_conf(client_config.clone());

View File

@ -45,7 +45,7 @@ class CustomizableOperationGeneratorTest {
fn test() {
let config = $moduleName::Config::builder()
.http_client(#{NeverClient}::new())
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.build();
let client = $moduleName::Client::from_conf(config);
check_send_and_sync(client.say_hello().customize());

View File

@ -77,7 +77,7 @@ class FluentClientGeneratorTest {
##[test]
fn test() {
let config = $moduleName::Config::builder()
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(#{NeverClient}::new())
.build();
let client = $moduleName::Client::from_conf(config);
@ -101,7 +101,7 @@ class FluentClientGeneratorTest {
##[test]
fn test() {
let config = $moduleName::Config::builder()
.endpoint_resolver("http://localhost:1234")
.endpoint_url("http://localhost:1234")
.http_client(#{NeverClient}::new())
.build();
let client = $moduleName::Client::from_conf(config);

View File

@ -5,95 +5,23 @@
//! Code for resolving an endpoint (URI) that a request should be sent to
use crate::endpoint::error::InvalidEndpointError;
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use http::uri::{Authority, Uri};
use std::borrow::Cow;
use std::fmt::{Debug, Formatter};
use std::fmt::Debug;
use std::result::Result as StdResult;
use std::str::FromStr;
use std::sync::Arc;
use http::uri::{Authority, Uri};
use aws_smithy_types::config_bag::{Storable, StoreReplace};
pub use error::ResolveEndpointError;
use crate::endpoint::error::InvalidEndpointError;
pub mod error;
pub use error::ResolveEndpointError;
/// An endpoint-resolution-specific Result. Contains either an [`Endpoint`](aws_smithy_types::endpoint::Endpoint) or a [`ResolveEndpointError`].
pub type Result = std::result::Result<aws_smithy_types::endpoint::Endpoint, ResolveEndpointError>;
/// Implementors of this trait can resolve an endpoint that will be applied to a request.
pub trait ResolveEndpoint<Params>: Send + Sync {
/// Given some endpoint parameters, resolve an endpoint or return an error when resolution is
/// impossible.
fn resolve_endpoint(&self, params: &Params) -> Result;
}
impl<T> ResolveEndpoint<T> for &'static str {
fn resolve_endpoint(&self, _params: &T) -> Result {
Ok(aws_smithy_types::endpoint::Endpoint::builder()
.url(*self)
.build())
}
}
/// Endpoint Resolver wrapper that may be shared
#[derive(Clone)]
pub struct SharedEndpointResolver<T>(Arc<dyn ResolveEndpoint<T>>);
impl<T> Debug for SharedEndpointResolver<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SharedEndpointResolver").finish()
}
}
impl<T> SharedEndpointResolver<T> {
/// Create a new `SharedEndpointResolver` from `ResolveEndpoint`
pub fn new(resolve_endpoint: impl ResolveEndpoint<T> + 'static) -> Self {
Self(Arc::new(resolve_endpoint))
}
}
impl<T> AsRef<dyn ResolveEndpoint<T>> for SharedEndpointResolver<T> {
fn as_ref(&self) -> &(dyn ResolveEndpoint<T> + 'static) {
self.0.as_ref()
}
}
impl<T> From<Arc<dyn ResolveEndpoint<T>>> for SharedEndpointResolver<T> {
fn from(resolve_endpoint: Arc<dyn ResolveEndpoint<T>>) -> Self {
SharedEndpointResolver(resolve_endpoint)
}
}
impl<T> ResolveEndpoint<T> for SharedEndpointResolver<T> {
fn resolve_endpoint(&self, params: &T) -> Result {
self.0.resolve_endpoint(params)
}
}
/// API Endpoint
///
/// This implements an API endpoint as specified in the
/// [Smithy Endpoint Specification](https://awslabs.github.io/smithy/1.0/spec/core/endpoint-traits.html)
#[derive(Clone, Debug)]
#[deprecated(note = "Use `.endpoint_url(...)` directly instead")]
pub struct Endpoint {
uri: http::Uri,
/// If true, endpointPrefix does ignored when setting the endpoint on a request
immutable: bool,
}
#[allow(deprecated)]
/// This allows customers that use `Endpoint` to override the endpoint to continue to do so
impl<T> ResolveEndpoint<T> for Endpoint {
fn resolve_endpoint(&self, _params: &T) -> Result {
Ok(aws_smithy_types::endpoint::Endpoint::builder()
.url(self.uri.to_string())
.build())
}
}
/// A special type that adds support for services that have special URL-prefixing rules.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EndpointPrefix(String);
@ -156,95 +84,6 @@ pub fn apply_endpoint(
Ok(())
}
#[allow(deprecated)]
impl Endpoint {
/// Create a new endpoint from a URI
///
/// Certain services will augment the endpoint with additional metadata. For example,
/// S3 can prefix the host with the bucket name. If your endpoint does not support this,
/// (for example, when communicating with localhost), use [`Endpoint::immutable`].
pub fn mutable_uri(uri: Uri) -> StdResult<Self, InvalidEndpointError> {
Ok(Endpoint {
uri: Self::validate_endpoint(uri)?,
immutable: false,
})
}
/// Create a new endpoint from a URI string
///
/// Certain services will augment the endpoint with additional metadata. For example,
/// S3 can prefix the host with the bucket name. If your endpoint does not support this,
/// (for example, when communicating with localhost), use [`Endpoint::immutable`].
pub fn mutable(uri: impl AsRef<str>) -> StdResult<Self, InvalidEndpointError> {
Self::mutable_uri(
Uri::try_from(uri.as_ref()).map_err(InvalidEndpointError::failed_to_construct_uri)?,
)
}
/// Returns the URI of this endpoint
pub fn uri(&self) -> &Uri {
&self.uri
}
/// Create a new immutable endpoint from a URI
///
/// ```rust
/// # use aws_smithy_http::endpoint::Endpoint;
/// use http::Uri;
/// let uri = Uri::from_static("http://localhost:8000");
/// let endpoint = Endpoint::immutable_uri(uri);
/// ```
///
/// Certain services will augment the endpoint with additional metadata. For example,
/// S3 can prefix the host with the bucket name. This constructor creates an endpoint which will
/// ignore those mutations. If you want an endpoint which will obey mutation requests, use
/// [`Endpoint::mutable`] instead.
pub fn immutable_uri(uri: Uri) -> StdResult<Self, InvalidEndpointError> {
Ok(Endpoint {
uri: Self::validate_endpoint(uri)?,
immutable: true,
})
}
/// Create a new immutable endpoint from a URI string
///
/// ```rust
/// # use aws_smithy_http::endpoint::Endpoint;
/// let endpoint = Endpoint::immutable("http://localhost:8000");
/// ```
///
/// Certain services will augment the endpoint with additional metadata. For example,
/// S3 can prefix the host with the bucket name. This constructor creates an endpoint which will
/// ignore those mutations. If you want an endpoint which will obey mutation requests, use
/// [`Endpoint::mutable`] instead.
pub fn immutable(uri: impl AsRef<str>) -> StdResult<Self, InvalidEndpointError> {
Self::immutable_uri(
Uri::try_from(uri.as_ref()).map_err(InvalidEndpointError::failed_to_construct_uri)?,
)
}
/// Sets the endpoint on `uri`, potentially applying the specified `prefix` in the process.
pub fn set_endpoint(
&self,
uri: &mut http::Uri,
prefix: Option<&EndpointPrefix>,
) -> StdResult<(), InvalidEndpointError> {
let prefix = match self.immutable {
true => None,
false => prefix,
};
apply_endpoint(uri, &self.uri, prefix)
}
fn validate_endpoint(endpoint: Uri) -> StdResult<Uri, InvalidEndpointError> {
if endpoint.scheme().is_none() {
Err(InvalidEndpointError::endpoint_must_have_scheme())
} else {
Ok(endpoint)
}
}
}
fn merge_paths<'a>(endpoint: &'a Uri, uri: &'a Uri) -> Cow<'a, str> {
if let Some(query) = endpoint.path_and_query().and_then(|pq| pq.query()) {
tracing::warn!(query = %query, "query specified in endpoint will be ignored during endpoint resolution");
@ -261,103 +100,3 @@ fn merge_paths<'a>(endpoint: &'a Uri, uri: &'a Uri) -> Cow<'a, str> {
Cow::Owned(format!("{}/{}", ep_no_slash, uri_path_no_slash))
}
}
#[cfg(test)]
#[allow(deprecated)]
mod test {
use crate::endpoint::error::{InvalidEndpointError, InvalidEndpointErrorKind};
use crate::endpoint::{Endpoint, EndpointPrefix};
use http::Uri;
#[test]
fn prefix_endpoint() {
let ep = Endpoint::mutable("https://us-east-1.dynamo.amazonaws.com").unwrap();
let mut uri = Uri::from_static("/list_tables?k=v");
ep.set_endpoint(
&mut uri,
Some(&EndpointPrefix::new("subregion.").expect("valid prefix")),
)
.unwrap();
assert_eq!(
uri,
Uri::from_static("https://subregion.us-east-1.dynamo.amazonaws.com/list_tables?k=v")
);
}
#[test]
fn prefix_endpoint_custom_port() {
let ep = Endpoint::mutable("https://us-east-1.dynamo.amazonaws.com:6443").unwrap();
let mut uri = Uri::from_static("/list_tables?k=v");
ep.set_endpoint(
&mut uri,
Some(&EndpointPrefix::new("subregion.").expect("valid prefix")),
)
.unwrap();
assert_eq!(
uri,
Uri::from_static(
"https://subregion.us-east-1.dynamo.amazonaws.com:6443/list_tables?k=v"
)
);
}
#[test]
fn prefix_immutable_endpoint() {
let ep = Endpoint::immutable("https://us-east-1.dynamo.amazonaws.com").unwrap();
let mut uri = Uri::from_static("/list_tables?k=v");
ep.set_endpoint(
&mut uri,
Some(&EndpointPrefix::new("subregion.").expect("valid prefix")),
)
.unwrap();
assert_eq!(
uri,
Uri::from_static("https://us-east-1.dynamo.amazonaws.com/list_tables?k=v")
);
}
#[test]
fn endpoint_with_path() {
for uri in &[
// check that trailing slashes are properly normalized
"https://us-east-1.dynamo.amazonaws.com/private",
"https://us-east-1.dynamo.amazonaws.com/private/",
] {
let ep = Endpoint::immutable(uri).unwrap();
let mut uri = Uri::from_static("/list_tables?k=v");
ep.set_endpoint(
&mut uri,
Some(&EndpointPrefix::new("subregion.").expect("valid prefix")),
)
.unwrap();
assert_eq!(
uri,
Uri::from_static("https://us-east-1.dynamo.amazonaws.com/private/list_tables?k=v")
);
}
}
#[test]
fn set_endpoint_empty_path() {
let ep = Endpoint::immutable("http://localhost:8000").unwrap();
let mut uri = Uri::from_static("/");
ep.set_endpoint(&mut uri, None).unwrap();
assert_eq!(uri, Uri::from_static("http://localhost:8000/"))
}
#[test]
fn endpoint_construction_missing_scheme() {
assert!(matches!(
Endpoint::mutable("localhost:8000"),
Err(InvalidEndpointError {
kind: InvalidEndpointErrorKind::EndpointMustHaveScheme
})
));
assert!(matches!(
Endpoint::immutable("localhost:8000"),
Err(InvalidEndpointError {
kind: InvalidEndpointErrorKind::EndpointMustHaveScheme
})
));
}
}

View File

@ -5,7 +5,6 @@
use aws_smithy_http::endpoint::error::ResolveEndpointError;
use aws_smithy_http::endpoint::EndpointPrefix;
use aws_smithy_http::endpoint::SharedEndpointResolver;
use aws_smithy_runtime_api::box_error::BoxError;
use aws_smithy_runtime_api::client::endpoint::{
EndpointFuture, EndpointResolverParams, ResolveEndpoint,
@ -13,7 +12,7 @@ use aws_smithy_runtime_api::client::endpoint::{
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
use aws_smithy_types::config_bag::ConfigBag;
use aws_smithy_types::endpoint::Endpoint;
use http::header::HeaderName;
use http::uri::PathAndQuery;
@ -70,50 +69,6 @@ impl From<StaticUriEndpointResolverParams> for EndpointResolverParams {
}
}
/// Default implementation of [`ResolveEndpoint`].
///
/// This default endpoint resolver implements the `ResolveEndpoint` trait by
/// converting the type-erased [`EndpointResolverParams`] into the concrete
/// endpoint params for the service. It then delegates endpoint resolution
/// to an underlying resolver that is aware of the concrete type.
#[derive(Clone, Debug)]
pub struct DefaultEndpointResolver<Params> {
inner: SharedEndpointResolver<Params>,
}
impl<Params> Storable for DefaultEndpointResolver<Params>
where
Params: Debug + Send + Sync + 'static,
{
type Storer = StoreReplace<Self>;
}
impl<Params> DefaultEndpointResolver<Params> {
/// Creates a new `DefaultEndpointResolver`.
pub fn new(resolve_endpoint: SharedEndpointResolver<Params>) -> Self {
Self {
inner: resolve_endpoint,
}
}
}
impl<Params> ResolveEndpoint for DefaultEndpointResolver<Params>
where
Params: Debug + Send + Sync + 'static,
{
fn resolve_endpoint<'a>(&'a self, params: &'a EndpointResolverParams) -> EndpointFuture<'a> {
use aws_smithy_http::endpoint::ResolveEndpoint as _;
let ep = match params.get::<Params>() {
Some(params) => self.inner.resolve_endpoint(params).map_err(Box::new),
None => Err(Box::new(ResolveEndpointError::message(
"params of expected type was not present",
))),
}
.map_err(|e| e as _);
EndpointFuture::ready(ep)
}
}
pub(super) async fn orchestrate_endpoint(
ctx: &mut InterceptorContext,
runtime_components: &RuntimeComponents,