diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index c5c9f7adff..21f21b9688 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -486,3 +486,11 @@ message = "`ByteStream::poll_next` is now feature-gated. You can turn on a cargo references = ["smithy-rs#3033", "smithy-rs#3088"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all" } author = "ysaito1001" + +[[smithy-rs]] +message = """ +The [`connection`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/connection/index.html) and [`result`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/result/index.html) modules in `aws-smithy-http` have been moved to `aws_smithy_types`. Type aliases for all affected pub items, except for a trait, are left in `aws_smithy_http` for backwards compatibility but are deprecated. Due to lack of trait aliases, the moved trait `CreateUnhandledError` needs to be used from `aws-smithy-types`. +""" +references = ["smithy-rs#3092", "smithy-rs#3093"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index e1593172a6..02ee6b07e9 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -14,7 +14,7 @@ allowed_external_types = [ "aws_smithy_async::time::TimeSource", "aws_smithy_http::endpoint", "aws_smithy_http::endpoint::error::InvalidEndpointError", - "aws_smithy_http::result::SdkError", + "aws_smithy_types::result::SdkError", "aws_smithy_runtime::client::identity::cache::IdentityCache", "aws_smithy_runtime::client::identity::cache::lazy::LazyCacheBuilder", "aws_smithy_runtime_api::client::dns::ResolveDns", diff --git a/aws/rust-runtime/aws-config/src/http_credential_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs index 87687590cd..48bb4c4d0e 100644 --- a/aws/rust-runtime/aws-config/src/http_credential_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -12,7 +12,6 @@ use crate::json_credentials::{parse_json_credentials, JsonCredentials, Refreshab use crate::provider_config::ProviderConfig; use aws_credential_types::provider::{self, error::CredentialsError}; use aws_credential_types::Credentials; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime::client::orchestrator::operation::Operation; use aws_smithy_runtime::client::retries::classifiers::{ HttpStatusCodeClassifier, TransientErrorClassifier, @@ -27,6 +26,7 @@ use aws_smithy_runtime_api::client::retries::classifiers::RetryAction; use aws_smithy_runtime_api::client::runtime_plugin::StaticRuntimePlugin; use aws_smithy_types::body::SdkBody; use aws_smithy_types::config_bag::Layer; +use aws_smithy_types::result::SdkError; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use http::header::{ACCEPT, AUTHORIZATION}; diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index fd2315a316..86c1fa2441 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -13,8 +13,6 @@ use crate::provider_config::ProviderConfig; use crate::PKG_VERSION; use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; use aws_runtime::user_agent::UserAgentInterceptor; -use aws_smithy_http::result::ConnectorError; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime::client::orchestrator::operation::Operation; use aws_smithy_runtime::client::retries::strategy::StandardRetryStrategy; use aws_smithy_runtime_api::client::auth::AuthSchemeOptionResolverParams; @@ -33,6 +31,8 @@ use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, SharedRuntim use aws_smithy_types::body::SdkBody; use aws_smithy_types::config_bag::{FrozenLayer, Layer}; use aws_smithy_types::endpoint::Endpoint; +use aws_smithy_types::result::ConnectorError; +use aws_smithy_types::result::SdkError; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::os_shim_internal::Env; @@ -582,7 +582,6 @@ pub(crate) mod test { use crate::provider_config::ProviderConfig; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_async::test_util::{instant_time_and_sleep, InstantSleep}; - use aws_smithy_http::result::ConnectorError; use aws_smithy_runtime::client::http::test_util::{ capture_request, ReplayEvent, StaticReplayClient, }; @@ -596,6 +595,7 @@ pub(crate) mod test { use aws_smithy_runtime_api::client::retries::classifiers::{ClassifyRetry, RetryAction}; use aws_smithy_types::body::SdkBody; use aws_smithy_types::error::display::DisplayErrorContext; + use aws_smithy_types::result::ConnectorError; use aws_types::os_shim_internal::{Env, Fs}; use http::header::USER_AGENT; use http::Uri; diff --git a/aws/rust-runtime/aws-config/src/imds/client/error.rs b/aws/rust-runtime/aws-config/src/imds/client/error.rs index 4b5aacb894..7a49bf6a7f 100644 --- a/aws/rust-runtime/aws-config/src/imds/client/error.rs +++ b/aws/rust-runtime/aws-config/src/imds/client/error.rs @@ -6,9 +6,9 @@ //! Error types for [`ImdsClient`](crate::imds::client::Client) use aws_smithy_http::endpoint::error::InvalidEndpointError; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::orchestrator::HttpResponse; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::result::SdkError; use std::error::Error; use std::fmt; diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 293951b37b..ba2b1f7c76 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -12,9 +12,9 @@ use aws_sdk_sts::operation::assume_role::builders::AssumeRoleFluentBuilder; use aws_sdk_sts::operation::assume_role::AssumeRoleError; use aws_sdk_sts::types::PolicyDescriptorType; use aws_sdk_sts::Client as StsClient; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime::client::identity::IdentityCache; use aws_smithy_types::error::display::DisplayErrorContext; +use aws_smithy_types::result::SdkError; use aws_types::region::Region; use aws_types::SdkConfig; use std::time::Duration; diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index a7b429169f..0c8576ca19 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -4,11 +4,11 @@ */ use aws_smithy_http::http::HttpHeaders; -use aws_smithy_http::result::SdkError; use aws_smithy_types::error::metadata::{ Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata, }; use aws_smithy_types::error::Unhandled; +use aws_smithy_types::result::SdkError; use http::{HeaderMap, HeaderValue}; /// Constant for the [`ErrorMetadata`] extra field that contains the request ID diff --git a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs index 93d2d850fe..9eb22c332f 100644 --- a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs +++ b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs @@ -4,11 +4,11 @@ */ use aws_smithy_http::http::HttpHeaders; -use aws_smithy_http::result::SdkError; use aws_smithy_types::error::metadata::{ Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata, }; use aws_smithy_types::error::Unhandled; +use aws_smithy_types::result::SdkError; use http::{HeaderMap, HeaderValue}; const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; @@ -94,8 +94,8 @@ fn extract_extended_request_id(headers: &HeaderMap) -> Option<&str> #[cfg(test)] mod test { use super::*; - use aws_smithy_http::result::SdkError; use aws_smithy_types::body::SdkBody; + use aws_smithy_types::result::SdkError; use http::Response; #[test] diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 3c143291e0..0f30328083 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -17,7 +17,6 @@ aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", optional = true } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] } -aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } tracing = "0.1" http = "0.2.6" # cargo does not support optional test dependencies, so to completely disable rustls diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs index 1227eb24bc..8f5d473440 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -5,9 +5,9 @@ use aws_sdk_kms as kms; use aws_sdk_kms::operation::RequestId; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime::client::http::test_util::{ReplayEvent, StaticReplayClient}; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::result::SdkError; use http::header::AUTHORIZATION; use http::Uri; use kms::config::{Config, Credentials, Region}; diff --git a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs index 5e603d2827..ea7247bce4 100644 --- a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs +++ b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs @@ -6,11 +6,11 @@ use aws_credential_types::Credentials; use aws_runtime::retries::classifiers::AwsErrorCodeClassifier; use aws_sdk_kms as kms; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime::client::http::test_util::infallible_client_fn; use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, InterceptorContext}; use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError}; use aws_smithy_runtime_api::client::retries::classifiers::{ClassifyRetry, RetryAction}; +use aws_smithy_types::result::SdkError; use bytes::Bytes; use kms::operation::create_alias::CreateAliasError; diff --git a/aws/sdk/integration-tests/kms/tests/traits.rs b/aws/sdk/integration-tests/kms/tests/traits.rs index bfc1cf900b..321ac3931c 100644 --- a/aws/sdk/integration-tests/kms/tests/traits.rs +++ b/aws/sdk/integration-tests/kms/tests/traits.rs @@ -14,7 +14,9 @@ fn assert_debug() {} #[tokio::test] async fn types_are_send_sync() { assert_send_sync::(); - assert_send_sync::>(); + assert_send_sync::< + kms::error::SdkError>, + >(); assert_send_sync::(); assert_send_sync::(); assert_send_sync::(); diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index 7aee8af904..d9d2b54073 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -13,10 +13,10 @@ use aws_sdk_s3::types::{ use aws_sdk_s3::{Client, Config}; use aws_smithy_async::assert_elapsed; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime::client::http::test_util::NeverClient; use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs; use aws_smithy_types::error::display::DisplayErrorContext; +use aws_smithy_types::result::SdkError; use aws_smithy_types::timeout::TimeoutConfig; use std::fmt::Debug; use std::time::{Duration, Instant}; diff --git a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs index 528f9f1bbd..86459d4563 100644 --- a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs +++ b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs @@ -60,7 +60,7 @@ async fn test_s3_signer_query_string_with_all_valid_chars() { #[ignore] async fn test_query_strings_are_correctly_encoded() { use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Error; - use aws_smithy_http::result::SdkError; + use aws_smithy_types::result::SdkError; tracing_subscriber::fmt::init(); let config = aws_config::load_from_env().await; diff --git a/aws/sdk/integration-tests/s3/tests/service_timeout_overrides.rs b/aws/sdk/integration-tests/s3/tests/service_timeout_overrides.rs index ba014b0d7f..0ef1499cf7 100644 --- a/aws/sdk/integration-tests/s3/tests/service_timeout_overrides.rs +++ b/aws/sdk/integration-tests/s3/tests/service_timeout_overrides.rs @@ -6,7 +6,7 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; -use aws_smithy_http::result::SdkError; +use aws_smithy_types::result::SdkError; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::region::Region; use aws_types::SdkConfig; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 9d2bc151e7..07c58e0ec3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -264,7 +264,6 @@ class FluentClientGenerator( "OperationError" to errorType, "OperationOutput" to outputType, "SdkError" to RuntimeType.sdkError(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index 8e17d5bd9d..fbf8a08588 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -54,7 +54,7 @@ class OperationErrorGenerator( private val runtimeConfig = symbolProvider.config.runtimeConfig private val errorMetadata = errorMetadata(symbolProvider.config.runtimeConfig) private val createUnhandledError = - RuntimeType.smithyHttp(runtimeConfig).resolve("result::CreateUnhandledError") + RuntimeType.smithyTypes(runtimeConfig).resolve("result::CreateUnhandledError") private fun operationErrors(): List = (operationOrEventStream as OperationShape).operationErrors(model).map { it.asStructureShape().get() } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index f728db76c5..06e51e42cb 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -436,9 +436,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun queryFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("query::$func") fun sdkBody(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("body::SdkBody") - fun sdkError(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("result::SdkError") - fun sdkSuccess(runtimeConfig: RuntimeConfig): RuntimeType = - smithyHttp(runtimeConfig).resolve("result::SdkSuccess") + fun sdkError(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("result::SdkError") fun parseTimestampFormat( codegenTarget: CodegenTarget, diff --git a/rust-runtime/aws-smithy-http/src/body.rs b/rust-runtime/aws-smithy-http/src/body.rs index 05e9776ccc..efcc29b712 100644 --- a/rust-runtime/aws-smithy-http/src/body.rs +++ b/rust-runtime/aws-smithy-http/src/body.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -//TODO(runtimeCratesVersioningCleanup): Re-point those who use the deprecated type aliases to -// directly depend on `aws_smithy_types` and remove this module. +//TODO(runtimeCratesVersioningCleanup): Keep the following deprecated type aliases for at least +// one release since 0.56.1 and then remove this module. //! Types for representing the body of an HTTP request or response diff --git a/rust-runtime/aws-smithy-http/src/byte_stream.rs b/rust-runtime/aws-smithy-http/src/byte_stream.rs index e97a395b8e..6a26221f85 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -//TODO(runtimeCratesVersioningCleanup): Re-point those who use the deprecated type aliases to -// directly depend on `aws_smithy_types` and remove this module. +//TODO(runtimeCratesVersioningCleanup): Keep the following deprecated type aliases for at least +// one release since 0.56.1 and then remove this module. //! ByteStream Abstractions diff --git a/rust-runtime/aws-smithy-http/src/connection.rs b/rust-runtime/aws-smithy-http/src/connection.rs index 99ae574232..52205b240b 100644 --- a/rust-runtime/aws-smithy-http/src/connection.rs +++ b/rust-runtime/aws-smithy-http/src/connection.rs @@ -3,51 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Types related to connection monitoring and management. +//TODO(runtimeCratesVersioningCleanup): Keep the following deprecated type alias for at least +// one release since 0.56.1 and then remove this module. -use std::fmt::{Debug, Formatter}; -use std::net::SocketAddr; -use std::sync::Arc; +//! Types related to connection monitoring and management. /// Metadata that tracks the state of an active connection. -#[derive(Clone)] -pub struct ConnectionMetadata { - is_proxied: bool, - remote_addr: Option, - poison_fn: Arc, -} - -impl ConnectionMetadata { - /// Poison this connection, ensuring that it won't be reused. - pub fn poison(&self) { - tracing::info!("smithy connection was poisoned"); - (self.poison_fn)() - } - - /// Create a new [`ConnectionMetadata`]. - pub fn new( - is_proxied: bool, - remote_addr: Option, - poison: impl Fn() + Send + Sync + 'static, - ) -> Self { - Self { - is_proxied, - remote_addr, - poison_fn: Arc::new(poison), - } - } - - /// Get the remote address for this connection, if one is set. - pub fn remote_addr(&self) -> Option { - self.remote_addr - } -} - -impl Debug for ConnectionMetadata { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SmithyConnection") - .field("is_proxied", &self.is_proxied) - .field("remote_addr", &self.remote_addr) - .finish() - } -} +#[deprecated(note = "Moved to `aws_smithy_types::connection::ConnectionMetadata`.")] +pub type ConnectionMetadata = aws_smithy_types::connection::ConnectionMetadata; diff --git a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs index dd9b9cd021..df82965568 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::result::{ConnectorError, SdkError}; use aws_smithy_eventstream::frame::{ DecodedFrame, Message, MessageFrameDecoder, UnmarshallMessage, UnmarshalledMessage, }; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::result::{ConnectorError, SdkError}; use bytes::Buf; use bytes::Bytes; use bytes_utils::SegmentedBuf; @@ -276,10 +276,10 @@ impl Receiver { #[cfg(test)] mod tests { use super::{Receiver, UnmarshallMessage}; - use crate::result::SdkError; use aws_smithy_eventstream::error::Error as EventStreamError; use aws_smithy_eventstream::frame::{Header, HeaderValue, Message, UnmarshalledMessage}; use aws_smithy_types::body::SdkBody; + use aws_smithy_types::result::SdkError; use bytes::Bytes; use hyper::body::Body; use std::error::Error as StdError; diff --git a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs index d19690e727..f06f6b1cfe 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::result::SdkError; use aws_smithy_eventstream::frame::{MarshallMessage, SignMessage}; +use aws_smithy_types::body::SdkBody; +use aws_smithy_types::result::SdkError; use bytes::Bytes; use futures_core::Stream; use std::error::Error as StdError; @@ -140,7 +141,7 @@ impl MessageStreamAdapter { } impl Stream for MessageStreamAdapter { - type Item = Result>; + type Item = Result>>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.stream.as_mut().poll_next(cx) { @@ -195,12 +196,12 @@ impl Stream for MessageStreamAdapter; +//! Types for [`error`](aws_smithy_types::result::SdkError) responses. /// Builders for `SdkError` variant context. pub mod builders { - use super::*; - - macro_rules! source_only_error_builder { - ($errorName:ident, $builderName:ident, $sourceType:ident) => { - #[doc = concat!("Builder for [`", stringify!($errorName), "`](super::", stringify!($errorName), ").")] - #[derive(Debug, Default)] - pub struct $builderName { - source: Option<$sourceType>, - } - - impl $builderName { - #[doc = "Creates a new builder."] - pub fn new() -> Self { Default::default() } - - #[doc = "Sets the error source."] - pub fn source(mut self, source: impl Into<$sourceType>) -> Self { - self.source = Some(source.into()); - self - } - - #[doc = "Sets the error source."] - pub fn set_source(&mut self, source: Option<$sourceType>) -> &mut Self { - self.source = source; - self - } - - #[doc = "Builds the error context."] - pub fn build(self) -> $errorName { - $errorName { source: self.source.expect("source is required") } - } - } - }; - } - - source_only_error_builder!(ConstructionFailure, ConstructionFailureBuilder, BoxError); - source_only_error_builder!(TimeoutError, TimeoutErrorBuilder, BoxError); - source_only_error_builder!(DispatchFailure, DispatchFailureBuilder, ConnectorError); - - /// Builder for [`ResponseError`](super::ResponseError). - #[derive(Debug)] - pub struct ResponseErrorBuilder { - source: Option, - raw: Option, - } - - impl Default for ResponseErrorBuilder { - fn default() -> Self { - Self { - source: None, - raw: None, - } - } - } - - impl ResponseErrorBuilder { - /// Creates a new builder. - pub fn new() -> Self { - Default::default() - } - - /// Sets the error source. - pub fn source(mut self, source: impl Into) -> Self { - self.source = Some(source.into()); - self - } - - /// Sets the error source. - pub fn set_source(&mut self, source: Option) -> &mut Self { - self.source = source; - self - } - - /// Sets the raw response. - pub fn raw(mut self, raw: R) -> Self { - self.raw = Some(raw); - self - } + /// Builder for [`ConstructionFailure`](aws_smithy_types::result::ConstructionFailure). + #[deprecated( + note = "Moved to `aws_smithy_types::result::builders::ConstructionFailureBuilder`." + )] + pub type ConstructionFailureBuilder = + aws_smithy_types::result::builders::ConstructionFailureBuilder; - /// Sets the raw response. - pub fn set_raw(&mut self, raw: Option) -> &mut Self { - self.raw = raw; - self - } + /// Builder for [`TimeoutError`](aws_smithy_types::result::TimeoutError). + #[deprecated(note = "Moved to `aws_smithy_types::result::builders::TimeoutErrorBuilder`.")] + pub type TimeoutErrorBuilder = aws_smithy_types::result::builders::TimeoutErrorBuilder; - /// Builds the error context. - pub fn build(self) -> ResponseError { - ResponseError { - source: self.source.expect("source is required"), - raw: self.raw.expect("a raw response is required"), - } - } - } + /// Builder for [`DispatchFailure`](aws_smithy_types::result::DispatchFailure). + #[deprecated(note = "Moved to `aws_smithy_types::result::builders::DispatchFailureBuilder`.")] + pub type DispatchFailureBuilder = aws_smithy_types::result::builders::DispatchFailureBuilder; - /// Builder for [`ServiceError`](super::ServiceError). - #[derive(Debug)] - pub struct ServiceErrorBuilder { - source: Option, - raw: Option, - } + /// Builder for [`ResponseError`](aws_smithy_types::result::ResponseError). + #[deprecated(note = "Moved to `aws_smithy_types::result::builders::ResponseErrorBuilder`.")] + pub type ResponseErrorBuilder = aws_smithy_types::result::builders::ResponseErrorBuilder; - impl Default for ServiceErrorBuilder { - fn default() -> Self { - Self { - source: None, - raw: None, - } - } - } - - impl ServiceErrorBuilder { - /// Creates a new builder. - pub fn new() -> Self { - Default::default() - } - - /// Sets the error source. - pub fn source(mut self, source: impl Into) -> Self { - self.source = Some(source.into()); - self - } - - /// Sets the error source. - pub fn set_source(&mut self, source: Option) -> &mut Self { - self.source = source; - self - } - - /// Sets the raw response. - pub fn raw(mut self, raw: R) -> Self { - self.raw = Some(raw); - self - } - - /// Sets the raw response. - pub fn set_raw(&mut self, raw: Option) -> &mut Self { - self.raw = raw; - self - } - - /// Builds the error context. - pub fn build(self) -> ServiceError { - ServiceError { - source: self.source.expect("source is required"), - raw: self.raw.expect("a raw response is required"), - } - } - } + /// Builder for [`ServiceError`](aws_smithy_types::result::ServiceError). + #[deprecated(note = "Moved to `aws_smithy_types::result::builders::ServiceErrorBuilder`.")] + pub type ServiceErrorBuilder = + aws_smithy_types::result::builders::ServiceErrorBuilder; } -/// Error context for [`SdkError::ConstructionFailure`] -#[derive(Debug)] -pub struct ConstructionFailure { - source: BoxError, -} - -impl ConstructionFailure { - /// Creates a builder for this error context type. - pub fn builder() -> builders::ConstructionFailureBuilder { - builders::ConstructionFailureBuilder::new() - } -} +/// Error context for [`aws_smithy_types::result::ConstructionFailure`] +#[deprecated(note = "Moved to `aws_smithy_types::result::ConstructionFailure`.")] +pub type ConstructionFailure = aws_smithy_types::result::ConstructionFailure; -/// Error context for [`SdkError::TimeoutError`] -#[derive(Debug)] -pub struct TimeoutError { - source: BoxError, -} +/// Error context for [`aws_smithy_types::result::TimeoutError`] +#[deprecated(note = "Moved to `aws_smithy_types::result::TimeoutError`.")] +pub type TimeoutError = aws_smithy_types::result::TimeoutError; -impl TimeoutError { - /// Creates a builder for this error context type. - pub fn builder() -> builders::TimeoutErrorBuilder { - builders::TimeoutErrorBuilder::new() - } -} +/// Error context for [`aws_smithy_types::result::DispatchFailure`] +#[deprecated(note = "Moved to `aws_smithy_types::result::DispatchFailure`.")] +pub type DispatchFailure = aws_smithy_types::result::DispatchFailure; -/// Error context for [`SdkError::DispatchFailure`] -#[derive(Debug)] -pub struct DispatchFailure { - source: ConnectorError, -} - -impl DispatchFailure { - /// Creates a builder for this error context type. - pub fn builder() -> builders::DispatchFailureBuilder { - builders::DispatchFailureBuilder::new() - } - - /// Returns true if the error is an IO error - pub fn is_io(&self) -> bool { - self.source.is_io() - } - - /// Returns true if the error is an timeout error - pub fn is_timeout(&self) -> bool { - self.source.is_timeout() - } - - /// Returns true if the error is a user-caused error (e.g., invalid HTTP request) - pub fn is_user(&self) -> bool { - self.source.is_user() - } - - /// Returns true if the error is an unclassified error. - pub fn is_other(&self) -> bool { - self.source.is_other() - } - - /// Returns the optional error kind associated with an unclassified error - pub fn as_other(&self) -> Option { - self.source.as_other() - } - - /// Returns the inner error if it is a connector error - pub fn as_connector_error(&self) -> Option<&ConnectorError> { - Some(&self.source) - } -} - -/// Error context for [`SdkError::ResponseError`] -#[derive(Debug)] -pub struct ResponseError { - /// Error encountered while parsing the response - source: BoxError, - /// Raw response that was available - raw: R, -} - -impl ResponseError { - /// Creates a builder for this error context type. - pub fn builder() -> builders::ResponseErrorBuilder { - builders::ResponseErrorBuilder::new() - } - - /// Returns a reference to the raw response - pub fn raw(&self) -> &R { - &self.raw - } - - /// Converts this error context into the raw response - pub fn into_raw(self) -> R { - self.raw - } -} - -/// Error context for [`SdkError::ServiceError`] -#[derive(Debug)] -pub struct ServiceError { - /// Modeled service error - source: E, - /// Raw response from the service - raw: R, -} - -impl ServiceError { - /// Creates a builder for this error context type. - pub fn builder() -> builders::ServiceErrorBuilder { - builders::ServiceErrorBuilder::new() - } - - /// Returns the underlying error of type `E` - pub fn err(&self) -> &E { - &self.source - } - - /// Converts this error context into the underlying error `E` - pub fn into_err(self) -> E { - self.source - } - - /// Returns a reference to the raw response - pub fn raw(&self) -> &R { - &self.raw - } - - /// Converts this error context into the raw response - pub fn into_raw(self) -> R { - self.raw - } -} - -/// Constructs the unhandled variant of a code generated error. -/// -/// This trait exists so that [`SdkError::into_service_error`] can be infallible. -pub trait CreateUnhandledError { - /// Creates an unhandled error variant with the given `source` and error metadata. - fn create_unhandled_error( - source: Box, - meta: Option, - ) -> Self; -} +/// Error context for [`aws_smithy_types::result::ResponseError`] +#[deprecated(note = "Moved to `aws_smithy_types::result::ResponseError`.")] +pub type ResponseError = aws_smithy_types::result::ResponseError; /// Failed SDK Result -/// -/// When logging an error from the SDK, it is recommended that you either wrap the error in -/// [`DisplayErrorContext`](aws_smithy_types::error::display::DisplayErrorContext), use another -/// error reporter library that visits the error's cause/source chain, or call -/// [`Error::source`](std::error::Error::source) for more details about the underlying cause. -#[non_exhaustive] -#[derive(Debug)] -pub enum SdkError> { - /// The request failed during construction. It was not dispatched over the network. - ConstructionFailure(ConstructionFailure), +#[deprecated(note = "Moved to `aws_smithy_types::result::ServiceError`.")] +pub type ServiceError = aws_smithy_types::result::ServiceError; - /// The request failed due to a timeout. The request MAY have been sent and received. - TimeoutError(TimeoutError), - - /// The request failed during dispatch. An HTTP response was not received. The request MAY - /// have been sent. - DispatchFailure(DispatchFailure), - - /// A response was received but it was not parseable according the the protocol (for example - /// the server hung up without sending a complete response) - ResponseError(ResponseError), - - /// An error response was received from the service - ServiceError(ServiceError), -} - -impl SdkError { - /// Construct a `SdkError` for a construction failure - pub fn construction_failure(source: impl Into) -> Self { - Self::ConstructionFailure(ConstructionFailure { - source: source.into(), - }) - } - - /// Construct a `SdkError` for a timeout - pub fn timeout_error(source: impl Into) -> Self { - Self::TimeoutError(TimeoutError { - source: source.into(), - }) - } - - /// Construct a `SdkError` for a dispatch failure with a [`ConnectorError`] - pub fn dispatch_failure(source: ConnectorError) -> Self { - Self::DispatchFailure(DispatchFailure { source }) - } - - /// Construct a `SdkError` for a response error - pub fn response_error(source: impl Into, raw: R) -> Self { - Self::ResponseError(ResponseError { - source: source.into(), - raw, - }) - } - - /// Construct a `SdkError` for a service failure - pub fn service_error(source: E, raw: R) -> Self { - Self::ServiceError(ServiceError { source, raw }) - } - - /// Returns the underlying service error `E` if there is one - /// - /// If the `SdkError` is not a `ServiceError` (for example, the error is a network timeout), - /// then it will be converted into an unhandled variant of `E`. This makes it easy to match - /// on the service's error response while simultaneously bubbling up transient failures. - /// For example, handling the `NoSuchKey` error for S3's `GetObject` operation may look as - /// follows: - /// - /// ```no_run - /// # use aws_smithy_http::result::{SdkError, CreateUnhandledError}; - /// # #[derive(Debug)] enum GetObjectError { NoSuchKey(()), Other(()) } - /// # impl std::fmt::Display for GetObjectError { - /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unimplemented!() } - /// # } - /// # impl std::error::Error for GetObjectError {} - /// # impl CreateUnhandledError for GetObjectError { - /// # fn create_unhandled_error( - /// # _: Box, - /// # _: Option, - /// # ) -> Self { unimplemented!() } - /// # } - /// # fn example() -> Result<(), GetObjectError> { - /// # let sdk_err = SdkError::service_error(GetObjectError::NoSuchKey(()), ()); - /// match sdk_err.into_service_error() { - /// GetObjectError::NoSuchKey(_) => { - /// // handle NoSuchKey - /// } - /// err @ _ => return Err(err), - /// } - /// # Ok(()) - /// # } - /// ``` - pub fn into_service_error(self) -> E - where - E: std::error::Error + Send + Sync + CreateUnhandledError + 'static, - R: Debug + Send + Sync + 'static, - { - match self { - Self::ServiceError(context) => context.source, - _ => E::create_unhandled_error(self.into(), None), - } - } - - /// Converts this error into its error source. - /// - /// If there is no error source, then `Err(Self)` is returned. - pub fn into_source(self) -> Result, Self> - where - E: std::error::Error + Send + Sync + 'static, - { - use SdkError::*; - match self { - ConstructionFailure(context) => Ok(context.source), - TimeoutError(context) => Ok(context.source), - ResponseError(context) => Ok(context.source), - DispatchFailure(context) => Ok(context.source.into()), - ServiceError(context) => Ok(context.source.into()), - } - } - - /// Return a reference to this error's raw response, if it contains one. Otherwise, return `None`. - pub fn raw_response(&self) -> Option<&R> { - match self { - Self::ServiceError(inner) => Some(inner.raw()), - Self::ResponseError(inner) => Some(inner.raw()), - _ => None, - } - } - - /// Maps the service error type in `SdkError::ServiceError` - #[doc(hidden)] - pub fn map_service_error(self, map: impl FnOnce(E) -> E2) -> SdkError { - match self { - Self::ServiceError(context) => SdkError::::ServiceError(ServiceError { - source: map(context.source), - raw: context.raw, - }), - Self::ConstructionFailure(context) => SdkError::::ConstructionFailure(context), - Self::DispatchFailure(context) => SdkError::::DispatchFailure(context), - Self::ResponseError(context) => SdkError::::ResponseError(context), - Self::TimeoutError(context) => SdkError::::TimeoutError(context), - } - } -} - -impl Display for SdkError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - SdkError::ConstructionFailure(_) => write!(f, "failed to construct request"), - SdkError::TimeoutError(_) => write!(f, "request has timed out"), - SdkError::DispatchFailure(_) => write!(f, "dispatch failure"), - SdkError::ResponseError(_) => write!(f, "response error"), - SdkError::ServiceError(_) => write!(f, "service error"), - } - } -} - -impl Error for SdkError -where - E: Error + 'static, - R: Debug, -{ - fn source(&self) -> Option<&(dyn Error + 'static)> { - use SdkError::*; - match self { - ConstructionFailure(context) => Some(context.source.as_ref()), - TimeoutError(context) => Some(context.source.as_ref()), - ResponseError(context) => Some(context.source.as_ref()), - DispatchFailure(context) => Some(&context.source), - ServiceError(context) => Some(&context.source), - } - } -} - -impl ProvideErrorMetadata for SdkError -where - E: ProvideErrorMetadata, -{ - fn meta(&self) -> &aws_smithy_types::Error { - match self { - Self::ConstructionFailure(_) => &EMPTY_ERROR_METADATA, - Self::TimeoutError(_) => &EMPTY_ERROR_METADATA, - Self::DispatchFailure(_) => &EMPTY_ERROR_METADATA, - Self::ResponseError(_) => &EMPTY_ERROR_METADATA, - Self::ServiceError(err) => err.source.meta(), - } - } -} - -#[derive(Debug)] -enum ConnectorErrorKind { - /// A timeout occurred while processing the request - Timeout, - - /// A user-caused error (e.g., invalid HTTP request) - User, - - /// Socket/IO error - Io, - - /// An unclassified Error with an explicit error kind - Other(Option), -} +/// Failed SDK Result +#[deprecated(note = "Moved to `aws_smithy_types::result::SdkError`.")] +pub type SdkError = aws_smithy_types::result::SdkError; /// Error from the underlying Connector -/// -/// Connector exists to attach a `ConnectorErrorKind` to what would otherwise be an opaque `Box` -/// that comes off a potentially generic or dynamic connector. -/// The attached `kind` is used to determine what retry behavior should occur (if any) based on the -/// connector error. -#[derive(Debug)] -pub struct ConnectorError { - kind: ConnectorErrorKind, - source: BoxError, - connection: ConnectionStatus, -} - -#[non_exhaustive] -#[derive(Debug)] -pub(crate) enum ConnectionStatus { - /// This request was never connected to the remote - /// - /// This indicates the failure was during connection establishment - NeverConnected, - - /// It is unknown whether a connection was established - Unknown, - - /// The request connected to the remote prior to failure - Connected(ConnectionMetadata), -} - -impl Display for ConnectorError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.kind { - ConnectorErrorKind::Timeout => write!(f, "timeout"), - ConnectorErrorKind::User => write!(f, "user error"), - ConnectorErrorKind::Io => write!(f, "io error"), - ConnectorErrorKind::Other(_) => write!(f, "other"), - } - } -} - -impl Error for ConnectorError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(self.source.as_ref()) - } -} - -impl ConnectorError { - /// Construct a [`ConnectorError`] from an error caused by a timeout - /// - /// Timeout errors are typically retried on a new connection. - pub fn timeout(source: BoxError) -> Self { - Self { - kind: ConnectorErrorKind::Timeout, - source, - connection: ConnectionStatus::Unknown, - } - } - - /// Include connection information along with this error - pub fn with_connection(mut self, info: ConnectionMetadata) -> Self { - self.connection = ConnectionStatus::Connected(info); - self - } - - /// Set the connection status on this error to report that a connection was never established - pub fn never_connected(mut self) -> Self { - self.connection = ConnectionStatus::NeverConnected; - self - } - - /// Construct a [`ConnectorError`] from an error caused by the user (e.g. invalid HTTP request) - pub fn user(source: BoxError) -> Self { - Self { - kind: ConnectorErrorKind::User, - source, - connection: ConnectionStatus::Unknown, - } - } - - /// Construct a [`ConnectorError`] from an IO related error (e.g. socket hangup) - pub fn io(source: BoxError) -> Self { - Self { - kind: ConnectorErrorKind::Io, - source, - connection: ConnectionStatus::Unknown, - } - } - - /// Construct a [`ConnectorError`] from an different unclassified error. - /// - /// Optionally, an explicit `Kind` may be passed. - pub fn other(source: BoxError, kind: Option) -> Self { - Self { - source, - kind: ConnectorErrorKind::Other(kind), - connection: ConnectionStatus::Unknown, - } - } - - /// Returns true if the error is an IO error - pub fn is_io(&self) -> bool { - matches!(self.kind, ConnectorErrorKind::Io) - } - - /// Returns true if the error is an timeout error - pub fn is_timeout(&self) -> bool { - matches!(self.kind, ConnectorErrorKind::Timeout) - } - - /// Returns true if the error is a user-caused error (e.g., invalid HTTP request) - pub fn is_user(&self) -> bool { - matches!(self.kind, ConnectorErrorKind::User) - } - - /// Returns true if the error is an unclassified error. - pub fn is_other(&self) -> bool { - matches!(self.kind, ConnectorErrorKind::Other(..)) - } - - /// Returns the optional error kind associated with an unclassified error - pub fn as_other(&self) -> Option { - match &self.kind { - ConnectorErrorKind::Other(ek) => *ek, - _ => None, - } - } - - /// Grants ownership of this error's source. - pub fn into_source(self) -> BoxError { - self.source - } - - /// Returns metadata about the connection - /// - /// If a connection was established and provided by the internal connector, a connection will - /// be returned. - pub fn connection_metadata(&self) -> Option<&ConnectionMetadata> { - match &self.connection { - ConnectionStatus::NeverConnected => None, - ConnectionStatus::Unknown => None, - ConnectionStatus::Connected(conn) => Some(conn), - } - } -} +#[deprecated(note = "Moved to `aws_smithy_types::result::ConnectorError`.")] +pub type ConnectorError = aws_smithy_types::result::ConnectorError; diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 58ae350e68..51326dda6e 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -17,7 +17,6 @@ test-util = ["aws-smithy-types/test-util"] [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } -aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" http = "0.2.9" diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/http.rs index 321e70198d..945af7e53a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/http.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/http.rs @@ -57,7 +57,7 @@ use crate::client::orchestrator::{HttpRequest, HttpResponse}; use crate::client::runtime_components::sealed::ValidateConfig; use crate::client::runtime_components::RuntimeComponents; use crate::impl_shared_conversions; -use aws_smithy_http::result::ConnectorError; +use aws_smithy_types::result::ConnectorError; use std::fmt; use std::sync::Arc; use std::time::Duration; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 051f3ebf58..84982613ee 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -28,8 +28,8 @@ //! Use the [`ConfigBag`] instead. use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; -use aws_smithy_http::result::SdkError; use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::result::SdkError; use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use phase::Phase; use std::fmt::Debug; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 350c399e1e..385a14e0b0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -20,9 +20,9 @@ use crate::box_error::BoxError; use crate::client::interceptors::context::phase::Phase; use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorError; -use aws_smithy_http::result::{ConnectorError, SdkError}; use aws_smithy_types::body::SdkBody; use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use aws_smithy_types::result::{ConnectorError, SdkError}; use bytes::Bytes; use std::error::Error as StdError; use std::fmt; diff --git a/rust-runtime/aws-smithy-runtime/src/client/http/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/http/connection_poisoning.rs index 561ed7e6df..29205d7cc9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/http/connection_poisoning.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/http/connection_poisoning.rs @@ -4,7 +4,6 @@ */ use crate::client::retries::classifiers::run_classifiers_on_ctx; -use aws_smithy_http::connection::ConnectionMetadata; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, @@ -13,6 +12,7 @@ use aws_smithy_runtime_api::client::interceptors::Intercept; use aws_smithy_runtime_api::client::retries::classifiers::RetryAction; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::connection::ConnectionMetadata; use aws_smithy_types::retry::{ReconnectMode, RetryConfig}; use std::fmt; use std::sync::{Arc, Mutex}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/http/hyper_014.rs b/rust-runtime/aws-smithy-runtime/src/client/http/hyper_014.rs index abb643ab89..9246ba3830 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/http/hyper_014.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/http/hyper_014.rs @@ -6,8 +6,6 @@ use crate::client::http::connection_poisoning::CaptureSmithyConnection; use aws_smithy_async::future::timeout::TimedOutError; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; -use aws_smithy_http::connection::ConnectionMetadata; -use aws_smithy_http::result::ConnectorError; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::http::{ HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpClient, @@ -17,7 +15,9 @@ use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::shared::IntoShared; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::connection::ConnectionMetadata; use aws_smithy_types::error::display::DisplayErrorContext; +use aws_smithy_types::result::ConnectorError; use aws_smithy_types::retry::ErrorKind; use http::{Extensions, Uri}; use hyper::client::connect::{capture_connection, CaptureConnection, Connection, HttpInfo}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/http/test_util/dvr/replay.rs b/rust-runtime/aws-smithy-runtime/src/client/http/test_util/dvr/replay.rs index f24cb36fb2..14c6f13377 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/http/test_util/dvr/replay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/http/test_util/dvr/replay.rs @@ -4,7 +4,6 @@ */ use super::{Action, ConnectionId, Direction, Event, NetworkTraffic}; -use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::MediaType; use aws_smithy_runtime_api::client::http::{ HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpConnector, @@ -14,6 +13,7 @@ use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::shared::IntoShared; use aws_smithy_types::body::SdkBody; use aws_smithy_types::error::display::DisplayErrorContext; +use aws_smithy_types::result::ConnectorError; use bytes::{Bytes, BytesMut}; use http::{Request, Version}; use http_body::Body; diff --git a/rust-runtime/aws-smithy-runtime/src/client/http/test_util/infallible.rs b/rust-runtime/aws-smithy-runtime/src/client/http/test_util/infallible.rs index 2715b31c9e..b310d8a057 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/http/test_util/infallible.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/http/test_util/infallible.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http::result::ConnectorError; use aws_smithy_runtime_api::client::http::{ HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpClient, SharedHttpConnector, @@ -12,6 +11,7 @@ use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::shared::IntoShared; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::result::ConnectorError; use std::fmt; use std::sync::Arc; diff --git a/rust-runtime/aws-smithy-runtime/src/client/http/test_util/replay.rs b/rust-runtime/aws-smithy-runtime/src/client/http/test_util/replay.rs index e911237390..309ac9cba3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/http/test_util/replay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/http/test_util/replay.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; use aws_smithy_runtime_api::client::http::{ HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpConnector, @@ -11,6 +10,7 @@ use aws_smithy_runtime_api::client::http::{ use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::shared::IntoShared; +use aws_smithy_types::result::ConnectorError; use http::header::CONTENT_TYPE; use std::ops::Deref; use std::sync::{Arc, Mutex, MutexGuard}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 61d3948605..a63f611795 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -12,7 +12,6 @@ use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::{log_response_body, read_body}; use crate::client::timeout::{MaybeTimeout, MaybeTimeoutConfig, TimeoutKind}; use aws_smithy_async::rt::sleep::AsyncSleep; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::http::{HttpClient, HttpConnector, HttpConnectorSettings}; use aws_smithy_runtime_api::client::interceptors::context::{ @@ -30,6 +29,7 @@ use aws_smithy_runtime_api::client::ser_de::{ use aws_smithy_types::body::SdkBody; use aws_smithy_types::byte_stream::ByteStream; use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::result::SdkError; use aws_smithy_types::timeout::TimeoutConfig; use std::mem; use tracing::{debug, debug_span, instrument, trace, Instrument}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/operation.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/operation.rs index 752de03110..a7aeff1e28 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/operation.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/operation.rs @@ -12,7 +12,6 @@ use crate::client::orchestrator::endpoints::StaticUriEndpointResolver; use crate::client::retries::strategy::{NeverRetryStrategy, StandardRetryStrategy}; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::time::TimeSource; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver; use aws_smithy_runtime_api::client::auth::{ @@ -36,6 +35,7 @@ use aws_smithy_runtime_api::client::ser_de::{ }; use aws_smithy_runtime_api::shared::IntoShared; use aws_smithy_types::config_bag::{ConfigBag, Layer}; +use aws_smithy_types::result::SdkError; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use std::borrow::Cow; @@ -390,8 +390,8 @@ mod tests { use crate::client::http::test_util::{capture_request, ReplayEvent, StaticReplayClient}; use crate::client::retries::classifiers::HttpStatusCodeClassifier; use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; - use aws_smithy_http::result::ConnectorError; use aws_smithy_types::body::SdkBody; + use aws_smithy_types::result::ConnectorError; use std::convert::Infallible; #[tokio::test] diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index e2e26418eb..d18c4633e1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -5,10 +5,10 @@ use aws_smithy_async::future::timeout::Timeout; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; -use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::orchestrator::HttpResponse; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::result::SdkError; use aws_smithy_types::timeout::TimeoutConfig; use pin_project_lite::pin_project; use std::future::Future; @@ -171,10 +171,10 @@ mod tests { use aws_smithy_async::assert_elapsed; use aws_smithy_async::future::never::Never; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, TokioSleep}; - use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::orchestrator::HttpResponse; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{CloneableLayer, ConfigBag}; + use aws_smithy_types::result::SdkError; use aws_smithy_types::timeout::TimeoutConfig; use std::time::Duration; diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 42f50191a9..70e90d078f 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -32,6 +32,7 @@ pin-project-lite = "0.2.9" pin-utils = "0.1.0" ryu = "1.0.5" time = { version = "0.3.4", features = ["parsing"] } +tracing = "0.1" # ByteStream internals futures-core = "0.3.14" diff --git a/rust-runtime/aws-smithy-types/src/connection.rs b/rust-runtime/aws-smithy-types/src/connection.rs new file mode 100644 index 0000000000..99ae574232 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/connection.rs @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Types related to connection monitoring and management. + +use std::fmt::{Debug, Formatter}; +use std::net::SocketAddr; +use std::sync::Arc; + +/// Metadata that tracks the state of an active connection. +#[derive(Clone)] +pub struct ConnectionMetadata { + is_proxied: bool, + remote_addr: Option, + poison_fn: Arc, +} + +impl ConnectionMetadata { + /// Poison this connection, ensuring that it won't be reused. + pub fn poison(&self) { + tracing::info!("smithy connection was poisoned"); + (self.poison_fn)() + } + + /// Create a new [`ConnectionMetadata`]. + pub fn new( + is_proxied: bool, + remote_addr: Option, + poison: impl Fn() + Send + Sync + 'static, + ) -> Self { + Self { + is_proxied, + remote_addr, + poison_fn: Arc::new(poison), + } + } + + /// Get the remote address for this connection, if one is set. + pub fn remote_addr(&self) -> Option { + self.remote_addr + } +} + +impl Debug for ConnectionMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SmithyConnection") + .field("is_proxied", &self.is_proxied) + .field("remote_addr", &self.remote_addr) + .finish() + } +} diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 7e805c310c..c2b76a65a5 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -18,10 +18,12 @@ pub mod body; pub mod byte_stream; /// A typemap for storing configuration. pub mod config_bag; +pub mod connection; pub mod date_time; pub mod endpoint; pub mod error; pub mod primitive; +pub mod result; pub mod retry; pub mod timeout; diff --git a/rust-runtime/aws-smithy-types/src/result.rs b/rust-runtime/aws-smithy-types/src/result.rs new file mode 100644 index 0000000000..cfec6fd975 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/result.rs @@ -0,0 +1,657 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Types for [error](SdkError) responses. + +use crate::connection::ConnectionMetadata; +use crate::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA}; +use crate::error::ErrorMetadata; +use crate::retry::ErrorKind; +use std::error::Error; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; + +type BoxError = Box; + +/// Builders for `SdkError` variant context. +pub mod builders { + use super::*; + + macro_rules! source_only_error_builder { + ($errorName:ident, $builderName:ident, $sourceType:ident) => { + #[doc = concat!("Builder for [`", stringify!($errorName), "`](super::", stringify!($errorName), ").")] + #[derive(Debug, Default)] + pub struct $builderName { + source: Option<$sourceType>, + } + + impl $builderName { + #[doc = "Creates a new builder."] + pub fn new() -> Self { Default::default() } + + #[doc = "Sets the error source."] + pub fn source(mut self, source: impl Into<$sourceType>) -> Self { + self.source = Some(source.into()); + self + } + + #[doc = "Sets the error source."] + pub fn set_source(&mut self, source: Option<$sourceType>) -> &mut Self { + self.source = source; + self + } + + #[doc = "Builds the error context."] + pub fn build(self) -> $errorName { + $errorName { source: self.source.expect("source is required") } + } + } + }; + } + + source_only_error_builder!(ConstructionFailure, ConstructionFailureBuilder, BoxError); + source_only_error_builder!(TimeoutError, TimeoutErrorBuilder, BoxError); + source_only_error_builder!(DispatchFailure, DispatchFailureBuilder, ConnectorError); + + /// Builder for [`ResponseError`](super::ResponseError). + #[derive(Debug)] + pub struct ResponseErrorBuilder { + source: Option, + raw: Option, + } + + impl Default for ResponseErrorBuilder { + fn default() -> Self { + Self { + source: None, + raw: None, + } + } + } + + impl ResponseErrorBuilder { + /// Creates a new builder. + pub fn new() -> Self { + Default::default() + } + + /// Sets the error source. + pub fn source(mut self, source: impl Into) -> Self { + self.source = Some(source.into()); + self + } + + /// Sets the error source. + pub fn set_source(&mut self, source: Option) -> &mut Self { + self.source = source; + self + } + + /// Sets the raw response. + pub fn raw(mut self, raw: R) -> Self { + self.raw = Some(raw); + self + } + + /// Sets the raw response. + pub fn set_raw(&mut self, raw: Option) -> &mut Self { + self.raw = raw; + self + } + + /// Builds the error context. + pub fn build(self) -> ResponseError { + ResponseError { + source: self.source.expect("source is required"), + raw: self.raw.expect("a raw response is required"), + } + } + } + + /// Builder for [`ServiceError`](super::ServiceError). + #[derive(Debug)] + pub struct ServiceErrorBuilder { + source: Option, + raw: Option, + } + + impl Default for ServiceErrorBuilder { + fn default() -> Self { + Self { + source: None, + raw: None, + } + } + } + + impl ServiceErrorBuilder { + /// Creates a new builder. + pub fn new() -> Self { + Default::default() + } + + /// Sets the error source. + pub fn source(mut self, source: impl Into) -> Self { + self.source = Some(source.into()); + self + } + + /// Sets the error source. + pub fn set_source(&mut self, source: Option) -> &mut Self { + self.source = source; + self + } + + /// Sets the raw response. + pub fn raw(mut self, raw: R) -> Self { + self.raw = Some(raw); + self + } + + /// Sets the raw response. + pub fn set_raw(&mut self, raw: Option) -> &mut Self { + self.raw = raw; + self + } + + /// Builds the error context. + pub fn build(self) -> ServiceError { + ServiceError { + source: self.source.expect("source is required"), + raw: self.raw.expect("a raw response is required"), + } + } + } +} + +/// Error context for [`SdkError::ConstructionFailure`] +#[derive(Debug)] +pub struct ConstructionFailure { + source: BoxError, +} + +impl ConstructionFailure { + /// Creates a builder for this error context type. + pub fn builder() -> builders::ConstructionFailureBuilder { + builders::ConstructionFailureBuilder::new() + } +} + +/// Error context for [`SdkError::TimeoutError`] +#[derive(Debug)] +pub struct TimeoutError { + source: BoxError, +} + +impl TimeoutError { + /// Creates a builder for this error context type. + pub fn builder() -> builders::TimeoutErrorBuilder { + builders::TimeoutErrorBuilder::new() + } +} + +/// Error context for [`SdkError::DispatchFailure`] +#[derive(Debug)] +pub struct DispatchFailure { + source: ConnectorError, +} + +impl DispatchFailure { + /// Creates a builder for this error context type. + pub fn builder() -> builders::DispatchFailureBuilder { + builders::DispatchFailureBuilder::new() + } + + /// Returns true if the error is an IO error + pub fn is_io(&self) -> bool { + self.source.is_io() + } + + /// Returns true if the error is an timeout error + pub fn is_timeout(&self) -> bool { + self.source.is_timeout() + } + + /// Returns true if the error is a user-caused error (e.g., invalid HTTP request) + pub fn is_user(&self) -> bool { + self.source.is_user() + } + + /// Returns true if the error is an unclassified error. + pub fn is_other(&self) -> bool { + self.source.is_other() + } + + /// Returns the optional error kind associated with an unclassified error + pub fn as_other(&self) -> Option { + self.source.as_other() + } + + /// Returns the inner error if it is a connector error + pub fn as_connector_error(&self) -> Option<&ConnectorError> { + Some(&self.source) + } +} + +/// Error context for [`SdkError::ResponseError`] +#[derive(Debug)] +pub struct ResponseError { + /// Error encountered while parsing the response + source: BoxError, + /// Raw response that was available + raw: R, +} + +impl ResponseError { + /// Creates a builder for this error context type. + pub fn builder() -> builders::ResponseErrorBuilder { + builders::ResponseErrorBuilder::new() + } + + /// Returns a reference to the raw response + pub fn raw(&self) -> &R { + &self.raw + } + + /// Converts this error context into the raw response + pub fn into_raw(self) -> R { + self.raw + } +} + +/// Error context for [`SdkError::ServiceError`] +#[derive(Debug)] +pub struct ServiceError { + /// Modeled service error + source: E, + /// Raw response from the service + raw: R, +} + +impl ServiceError { + /// Creates a builder for this error context type. + pub fn builder() -> builders::ServiceErrorBuilder { + builders::ServiceErrorBuilder::new() + } + + /// Returns the underlying error of type `E` + pub fn err(&self) -> &E { + &self.source + } + + /// Converts this error context into the underlying error `E` + pub fn into_err(self) -> E { + self.source + } + + /// Returns a reference to the raw response + pub fn raw(&self) -> &R { + &self.raw + } + + /// Converts this error context into the raw response + pub fn into_raw(self) -> R { + self.raw + } +} + +/// Constructs the unhandled variant of a code generated error. +/// +/// This trait exists so that [`SdkError::into_service_error`] can be infallible. +pub trait CreateUnhandledError { + /// Creates an unhandled error variant with the given `source` and error metadata. + fn create_unhandled_error( + source: Box, + meta: Option, + ) -> Self; +} + +/// Failed SDK Result +/// +/// When logging an error from the SDK, it is recommended that you either wrap the error in +/// [`DisplayErrorContext`](crate::error::display::DisplayErrorContext), use another +/// error reporter library that visits the error's cause/source chain, or call +/// [`Error::source`](std::error::Error::source) for more details about the underlying cause. +#[non_exhaustive] +#[derive(Debug)] +pub enum SdkError { + /// The request failed during construction. It was not dispatched over the network. + ConstructionFailure(ConstructionFailure), + + /// The request failed due to a timeout. The request MAY have been sent and received. + TimeoutError(TimeoutError), + + /// The request failed during dispatch. An HTTP response was not received. The request MAY + /// have been sent. + DispatchFailure(DispatchFailure), + + /// A response was received but it was not parseable according the the protocol (for example + /// the server hung up without sending a complete response) + ResponseError(ResponseError), + + /// An error response was received from the service + ServiceError(ServiceError), +} + +impl SdkError { + /// Construct a `SdkError` for a construction failure + pub fn construction_failure(source: impl Into) -> Self { + Self::ConstructionFailure(ConstructionFailure { + source: source.into(), + }) + } + + /// Construct a `SdkError` for a timeout + pub fn timeout_error(source: impl Into) -> Self { + Self::TimeoutError(TimeoutError { + source: source.into(), + }) + } + + /// Construct a `SdkError` for a dispatch failure with a [`ConnectorError`] + pub fn dispatch_failure(source: ConnectorError) -> Self { + Self::DispatchFailure(DispatchFailure { source }) + } + + /// Construct a `SdkError` for a response error + pub fn response_error(source: impl Into, raw: R) -> Self { + Self::ResponseError(ResponseError { + source: source.into(), + raw, + }) + } + + /// Construct a `SdkError` for a service failure + pub fn service_error(source: E, raw: R) -> Self { + Self::ServiceError(ServiceError { source, raw }) + } + + /// Returns the underlying service error `E` if there is one + /// + /// If the `SdkError` is not a `ServiceError` (for example, the error is a network timeout), + /// then it will be converted into an unhandled variant of `E`. This makes it easy to match + /// on the service's error response while simultaneously bubbling up transient failures. + /// For example, handling the `NoSuchKey` error for S3's `GetObject` operation may look as + /// follows: + /// + /// ```no_run + /// # use aws_smithy_types::result::{SdkError, CreateUnhandledError}; + /// # #[derive(Debug)] enum GetObjectError { NoSuchKey(()), Other(()) } + /// # impl std::fmt::Display for GetObjectError { + /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unimplemented!() } + /// # } + /// # impl std::error::Error for GetObjectError {} + /// # impl CreateUnhandledError for GetObjectError { + /// # fn create_unhandled_error( + /// # _: Box, + /// # _: Option, + /// # ) -> Self { unimplemented!() } + /// # } + /// # fn example() -> Result<(), GetObjectError> { + /// # let sdk_err = SdkError::service_error(GetObjectError::NoSuchKey(()), ()); + /// match sdk_err.into_service_error() { + /// GetObjectError::NoSuchKey(_) => { + /// // handle NoSuchKey + /// } + /// err @ _ => return Err(err), + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn into_service_error(self) -> E + where + E: std::error::Error + Send + Sync + CreateUnhandledError + 'static, + R: Debug + Send + Sync + 'static, + { + match self { + Self::ServiceError(context) => context.source, + _ => E::create_unhandled_error(self.into(), None), + } + } + + /// Converts this error into its error source. + /// + /// If there is no error source, then `Err(Self)` is returned. + pub fn into_source(self) -> Result, Self> + where + E: std::error::Error + Send + Sync + 'static, + { + use SdkError::*; + match self { + ConstructionFailure(context) => Ok(context.source), + TimeoutError(context) => Ok(context.source), + ResponseError(context) => Ok(context.source), + DispatchFailure(context) => Ok(context.source.into()), + ServiceError(context) => Ok(context.source.into()), + } + } + + /// Return a reference to this error's raw response, if it contains one. Otherwise, return `None`. + pub fn raw_response(&self) -> Option<&R> { + match self { + Self::ServiceError(inner) => Some(inner.raw()), + Self::ResponseError(inner) => Some(inner.raw()), + _ => None, + } + } + + /// Maps the service error type in `SdkError::ServiceError` + #[doc(hidden)] + pub fn map_service_error(self, map: impl FnOnce(E) -> E2) -> SdkError { + match self { + Self::ServiceError(context) => SdkError::::ServiceError(ServiceError { + source: map(context.source), + raw: context.raw, + }), + Self::ConstructionFailure(context) => SdkError::::ConstructionFailure(context), + Self::DispatchFailure(context) => SdkError::::DispatchFailure(context), + Self::ResponseError(context) => SdkError::::ResponseError(context), + Self::TimeoutError(context) => SdkError::::TimeoutError(context), + } + } +} + +impl Display for SdkError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + SdkError::ConstructionFailure(_) => write!(f, "failed to construct request"), + SdkError::TimeoutError(_) => write!(f, "request has timed out"), + SdkError::DispatchFailure(_) => write!(f, "dispatch failure"), + SdkError::ResponseError(_) => write!(f, "response error"), + SdkError::ServiceError(_) => write!(f, "service error"), + } + } +} + +impl Error for SdkError +where + E: Error + 'static, + R: Debug, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + use SdkError::*; + match self { + ConstructionFailure(context) => Some(context.source.as_ref()), + TimeoutError(context) => Some(context.source.as_ref()), + ResponseError(context) => Some(context.source.as_ref()), + DispatchFailure(context) => Some(&context.source), + ServiceError(context) => Some(&context.source), + } + } +} + +impl ProvideErrorMetadata for SdkError +where + E: ProvideErrorMetadata, +{ + fn meta(&self) -> &crate::Error { + match self { + Self::ConstructionFailure(_) => &EMPTY_ERROR_METADATA, + Self::TimeoutError(_) => &EMPTY_ERROR_METADATA, + Self::DispatchFailure(_) => &EMPTY_ERROR_METADATA, + Self::ResponseError(_) => &EMPTY_ERROR_METADATA, + Self::ServiceError(err) => err.source.meta(), + } + } +} + +#[derive(Debug)] +enum ConnectorErrorKind { + /// A timeout occurred while processing the request + Timeout, + + /// A user-caused error (e.g., invalid HTTP request) + User, + + /// Socket/IO error + Io, + + /// An unclassified Error with an explicit error kind + Other(Option), +} + +/// Error from the underlying Connector +/// +/// Connector exists to attach a `ConnectorErrorKind` to what would otherwise be an opaque `Box` +/// that comes off a potentially generic or dynamic connector. +/// The attached `kind` is used to determine what retry behavior should occur (if any) based on the +/// connector error. +#[derive(Debug)] +pub struct ConnectorError { + kind: ConnectorErrorKind, + source: BoxError, + connection: ConnectionStatus, +} + +#[non_exhaustive] +#[derive(Debug)] +pub(crate) enum ConnectionStatus { + /// This request was never connected to the remote + /// + /// This indicates the failure was during connection establishment + NeverConnected, + + /// It is unknown whether a connection was established + Unknown, + + /// The request connected to the remote prior to failure + Connected(ConnectionMetadata), +} + +impl Display for ConnectorError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.kind { + ConnectorErrorKind::Timeout => write!(f, "timeout"), + ConnectorErrorKind::User => write!(f, "user error"), + ConnectorErrorKind::Io => write!(f, "io error"), + ConnectorErrorKind::Other(_) => write!(f, "other"), + } + } +} + +impl Error for ConnectorError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(self.source.as_ref()) + } +} + +impl ConnectorError { + /// Construct a [`ConnectorError`] from an error caused by a timeout + /// + /// Timeout errors are typically retried on a new connection. + pub fn timeout(source: BoxError) -> Self { + Self { + kind: ConnectorErrorKind::Timeout, + source, + connection: ConnectionStatus::Unknown, + } + } + + /// Include connection information along with this error + pub fn with_connection(mut self, info: ConnectionMetadata) -> Self { + self.connection = ConnectionStatus::Connected(info); + self + } + + /// Set the connection status on this error to report that a connection was never established + pub fn never_connected(mut self) -> Self { + self.connection = ConnectionStatus::NeverConnected; + self + } + + /// Construct a [`ConnectorError`] from an error caused by the user (e.g. invalid HTTP request) + pub fn user(source: BoxError) -> Self { + Self { + kind: ConnectorErrorKind::User, + source, + connection: ConnectionStatus::Unknown, + } + } + + /// Construct a [`ConnectorError`] from an IO related error (e.g. socket hangup) + pub fn io(source: BoxError) -> Self { + Self { + kind: ConnectorErrorKind::Io, + source, + connection: ConnectionStatus::Unknown, + } + } + + /// Construct a [`ConnectorError`] from an different unclassified error. + /// + /// Optionally, an explicit `Kind` may be passed. + pub fn other(source: BoxError, kind: Option) -> Self { + Self { + source, + kind: ConnectorErrorKind::Other(kind), + connection: ConnectionStatus::Unknown, + } + } + + /// Returns true if the error is an IO error + pub fn is_io(&self) -> bool { + matches!(self.kind, ConnectorErrorKind::Io) + } + + /// Returns true if the error is an timeout error + pub fn is_timeout(&self) -> bool { + matches!(self.kind, ConnectorErrorKind::Timeout) + } + + /// Returns true if the error is a user-caused error (e.g., invalid HTTP request) + pub fn is_user(&self) -> bool { + matches!(self.kind, ConnectorErrorKind::User) + } + + /// Returns true if the error is an unclassified error. + pub fn is_other(&self) -> bool { + matches!(self.kind, ConnectorErrorKind::Other(..)) + } + + /// Returns the optional error kind associated with an unclassified error + pub fn as_other(&self) -> Option { + match &self.kind { + ConnectorErrorKind::Other(ek) => *ek, + _ => None, + } + } + + /// Grants ownership of this error's source. + pub fn into_source(self) -> BoxError { + self.source + } + + /// Returns metadata about the connection + /// + /// If a connection was established and provided by the internal connector, a connection will + /// be returned. + pub fn connection_metadata(&self) -> Option<&ConnectionMetadata> { + match &self.connection { + ConnectionStatus::NeverConnected => None, + ConnectionStatus::Unknown => None, + ConnectionStatus::Connected(conn) => Some(conn), + } + } +}