From 3ebc0ade044a88cc2e3896bb8c227bd2ddb9c08c Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 17 Oct 2023 13:13:10 -0400 Subject: [PATCH] Use `::endpoint::ResolveEndpoint` and delete aws_smithy_http::Endpoint --- CHANGELOG.next.toml | 12 + .../aws-inlineable/src/endpoint_discovery.rs | 59 ++-- .../timestream/TimestreamDecorator.kt | 22 +- .../codegen/client/smithy/ClientRustModule.kt | 2 +- .../endpoint/EndpointConfigCustomization.kt | 83 +++--- .../smithy/endpoint/EndpointTypesGenerator.kt | 1 - .../smithy/endpoint/EndpointsDecorator.kt | 36 +-- .../codegen/client/smithy/endpoint/Util.kt | 15 +- .../generators/EndpointResolverGenerator.kt | 64 +++- .../generators/EndpointTestGenerator.kt | 6 - .../ClientRuntimeTypesReExportGenerator.kt | 7 +- .../protocol/ProtocolTestGenerator.kt | 2 +- .../customizations/HttpAuthDecoratorTest.kt | 14 +- .../MetadataCustomizationTest.kt | 2 +- .../SensitiveOutputDecoratorTest.kt | 2 +- ...onfigOverrideRuntimePluginGeneratorTest.kt | 4 +- .../CustomizableOperationGeneratorTest.kt | 2 +- .../client/FluentClientGeneratorTest.kt | 4 +- rust-runtime/aws-smithy-http/src/endpoint.rs | 275 +----------------- .../src/client/orchestrator/endpoints.rs | 47 +-- 20 files changed, 203 insertions(+), 456 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 47b470967c..c17e89a4cf 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -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).**

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" diff --git a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs index 7e80f9ab22..4ef67888e6 100644 --- a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs +++ b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs @@ -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 BoxFuture<'static, (Endpoint, SystemTime), BoxError> + Send + Sync>, endpoint: Arc>>, - error: Arc>>, + error: Arc>>, rx: Receiver<()>, sleep: SharedAsyncSleep, time: SharedTimeSource, @@ -79,14 +80,14 @@ impl ReloadEndpoint { #[derive(Debug, Clone)] pub(crate) struct EndpointCache { - error: Arc>>, + error: Arc>>, endpoint: Arc>>, // When the sender is dropped, this allows the reload loop to stop _drop_guard: Arc>, } -impl ResolveEndpoint 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( loader_fn: impl Fn() -> F + Send + Sync + 'static, sleep: SharedAsyncSleep, time: SharedTimeSource, -) -> Result<(EndpointCache, ReloadEndpoint), ResolveEndpointError> +) -> Result<(EndpointCache, ReloadEndpoint), BoxError> where - F: Future> + Send + 'static, + F: Future> + 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 = self.error.lock().unwrap().take(); + error.unwrap_or_else(|| "err".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; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index 74c19ea048..eca1801187 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -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(), ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index 7c882a3f4b..ac39f8d3bc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -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.") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 84d30a4483..c6cf1eae0d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -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.serviceSpecificResolver 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.serviceSpecificResolver(), ) 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,17 @@ 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 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 +91,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 @@ -134,12 +104,28 @@ internal class EndpointConfigCustomization( rustTemplate( """ /// Sets the endpoint resolver to use when making requests. + $defaultResolverDocs /// /// 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))); + /// 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 + /// ```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 } @@ -153,13 +139,12 @@ internal class EndpointConfigCustomization( 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") }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt index 889b741ebb..d20e2d72b8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt @@ -53,7 +53,6 @@ class EndpointTypesGenerator( it, params, codegenContext = codegenContext, - endpointCustomizations = codegenContext.rootDecorator.endpointCustomizations(codegenContext), ).generate() } ?: {} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index a569d4f6f3..78dfd7f9c2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -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.serviceSpecificResolver 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 serviceSpecificResolver()) + return writable { rustTemplate( - "#{DefaultEndpointResolver}::<#{Params}>::new(#{OldSharedEndpointResolver}::new(#{base}))", + """{ + use #{ServiceSpecificResolver}; + #{DefaultResolver}::new().into_shared_resolver() + }""", *ctx, - "base" to base, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt index c6b5ad282c..9265a85d73 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt @@ -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> { } override fun visitEndpointRule(endpoint: Endpoint): Set { - 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() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt index 2009f82b4b..86f916f598 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt @@ -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.serviceSpecificResolver(), "DiagnosticCollector" to endpointsLib("diagnostic").toType().resolve("DiagnosticCollector"), ) @@ -183,13 +190,17 @@ internal class EndpointResolverGenerator( pub fn new() -> Self { Self { #{custom_fields_init:W} } } - } - impl #{endpoint}::ResolveEndpoint<#{Params}> for DefaultResolver { - fn resolve_endpoint(&self, params: &Params) -> #{endpoint}::Result { + fn resolve_endpoint(&self, params: &#{Params}) -> Result<#{SmithyEndpoint}, #{BoxError}> { 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())) + Ok(#{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.serviceSpecificResolver(): 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(MakeGlobal(self)) + } + } + + ##[derive(Debug)] + struct MakeGlobal(T); + impl #{ResolveEndpoint} for MakeGlobal + 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, + ) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt index 442adedf81..d14c0f3d22 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt @@ -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, 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(¶ms); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt index 09b6bd5fec..88e6bd2ffd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt @@ -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) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index c1ec854bef..9b256bbfce 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -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} """, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index 56be3c3778..76bb3f90b4 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -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); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt index 4bdb87dbdd..0795df3cfb 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt @@ -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); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/SensitiveOutputDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/SensitiveOutputDecoratorTest.kt index 66047aca47..e97ad2921b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/SensitiveOutputDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/SensitiveOutputDecoratorTest.kt @@ -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); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt index 668f408d82..b60f99a67d 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -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()); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGeneratorTest.kt index 2abb4229c8..c14d6b21fb 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGeneratorTest.kt @@ -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()); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt index 048aa961a3..1b9d76eb96 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -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); diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index 78ea5b2a34..ce7c79900f 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -5,94 +5,22 @@ //! 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; -pub mod error; +use http::uri::{Authority, Uri}; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; 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; - -/// Implementors of this trait can resolve an endpoint that will be applied to a request. -pub trait ResolveEndpoint: 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 ResolveEndpoint 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(Arc>); - -impl Debug for SharedEndpointResolver { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SharedEndpointResolver").finish() - } -} - -impl SharedEndpointResolver { - /// Create a new `SharedEndpointResolver` from `ResolveEndpoint` - pub fn new(resolve_endpoint: impl ResolveEndpoint + 'static) -> Self { - Self(Arc::new(resolve_endpoint)) - } -} - -impl AsRef> for SharedEndpointResolver { - fn as_ref(&self) -> &(dyn ResolveEndpoint + 'static) { - self.0.as_ref() - } -} - -impl From>> for SharedEndpointResolver { - fn from(resolve_endpoint: Arc>) -> Self { - SharedEndpointResolver(resolve_endpoint) - } -} - -impl ResolveEndpoint for SharedEndpointResolver { - 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, +use crate::endpoint::error::InvalidEndpointError; - /// If true, endpointPrefix does ignored when setting the endpoint on a request - immutable: bool, -} +pub mod error; -#[allow(deprecated)] -/// This allows customers that use `Endpoint` to override the endpoint to continue to do so -impl ResolveEndpoint for Endpoint { - fn resolve_endpoint(&self, _params: &T) -> Result { - Ok(aws_smithy_types::endpoint::Endpoint::builder() - .url(self.uri.to_string()) - .build()) - } -} +/// An endpoint-resolution-specific Result. Contains either an [`Endpoint`](aws_smithy_types::endpoint::Endpoint) or a [`ResolveEndpointError`]. +pub type Result = std::result::Result; /// A special type that adds support for services that have special URL-prefixing rules. #[derive(Clone, Debug, Eq, PartialEq)] @@ -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 { - 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) -> StdResult { - 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 { - 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) -> StdResult { - 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 { - 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 - }) - )); - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 30bbe3e9e3..11f7d873df 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -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 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 { - inner: SharedEndpointResolver, -} - -impl Storable for DefaultEndpointResolver -where - Params: Debug + Send + Sync + 'static, -{ - type Storer = StoreReplace; -} - -impl DefaultEndpointResolver { - /// Creates a new `DefaultEndpointResolver`. - pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { - Self { - inner: resolve_endpoint, - } - } -} - -impl ResolveEndpoint for DefaultEndpointResolver -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::() { - 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,