Unverified Commit e322a2d7 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Port middleware connectors to the orchestrator (#2970)



This PR ports all the connectors from the `aws-smithy-client` crate into
`aws-smithy-runtime` implementing the new `HttpConnector` trait. The old
connectors are left in place for now, and follow up PRs will remove them
as well as revise the generated configs to take `HttpConnector` impls
rather than `DynConnector`.

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Co-authored-by: default avatarZelda Hessler <zhessler@amazon.com>
parent 8a3b8f3a
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -103,6 +103,30 @@ references = ["smithy-rs#2964"]
meta = { "breaking" = false, "tada" = false, "bug" = false, target = "client" }
author = "rcoh"

[[smithy-rs]]
message = "`aws_smithy_client::hyper_ext::Adapter` was moved/renamed to `aws_smithy_runtime::client::connectors::hyper_connector::HyperConnector`."
references = ["smithy-rs#2970"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "jdisanti"

[[smithy-rs]]
message = "Test connectors moved into `aws_smithy_runtime::client::connectors::test_util` behind the `test-util` feature."
references = ["smithy-rs#2970"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "jdisanti"

[[smithy-rs]]
message = "DVR's RecordingConnection and ReplayingConnection were renamed to RecordingConnector and ReplayingConnector respectively."
references = ["smithy-rs#2970"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "jdisanti"

[[smithy-rs]]
message = "TestConnection was renamed to EventConnector."
references = ["smithy-rs#2970"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "jdisanti"

[[aws-sdk-rust]]
message = "Remove `once_cell` from public API"
references = ["smithy-rs#2973"]
+2 −4
Original line number Diff line number Diff line
@@ -169,10 +169,8 @@ impl DynConnector {
    pub fn call_lite(
        &mut self,
        req: http::Request<SdkBody>,
    ) -> BoxFuture<http::Response<SdkBody>, Box<dyn std::error::Error + Send + Sync + 'static>>
    {
        let future = Service::call(self, req);
        Box::pin(async move { future.await.map_err(|err| Box::new(err) as _) })
    ) -> BoxFuture<http::Response<SdkBody>, ConnectorError> {
        Service::call(self, req)
    }
}

+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ aws-smithy-http = { path = "../aws-smithy-http" }
aws-smithy-types = { path = "../aws-smithy-types" }
bytes = "1"
http = "0.2.3"
pin-project-lite = "0.2"
tokio = { version = "1.25", features = ["sync"] }
tracing = "0.1"
zeroize = { version = "1", optional = true }
+0 −1
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ pub mod runtime_plugin;

pub mod auth;

/// Smithy connectors and related code.
pub mod connectors;

pub mod ser_de;
+85 −3
Original line number Diff line number Diff line
@@ -3,9 +3,91 @@
 * SPDX-License-Identifier: Apache-2.0
 */

use crate::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse};
//! Smithy connectors and related code.
//!
//! # What is a connector?
//!
//! When we talk about connectors, we are referring to the [`HttpConnector`] trait, and implementations of
//! that trait. This trait simply takes a HTTP request, and returns a future with the response for that
//! request.
//!
//! This is slightly different from what a connector is in other libraries such as
//! [`hyper`](https://crates.io/crates/hyper). In hyper 0.x, the connector is a
//! [`tower`](https://crates.io/crates/tower) `Service` that takes a `Uri` and returns
//! a future with something that implements `AsyncRead + AsyncWrite`.
//!
//! The [`HttpConnector`](crate::client::connectors::HttpConnector) is designed to be a layer on top of
//! whole HTTP libraries, such as hyper, which allows Smithy clients to be agnostic to the underlying HTTP
//! transport layer. This also makes it easy to write tests with a fake HTTP connector, and several
//! such test connector implementations are availble in [`aws-smithy-runtime`](https://crates.io/crates/aws-smithy-runtime).
//!
//! # Responsibilities of a connector
//!
//! A connector primarily makes HTTP requests, but can also be used to implement connect and read
//! timeouts. The `HyperConnector` in [`aws-smithy-runtime`](https://crates.io/crates/aws-smithy-runtime)
//! is an example where timeouts are implemented as part of the connector.
//!
//! Connectors are also responsible for DNS lookup, TLS, connection reuse, pooling, and eviction.
//! The Smithy clients have no knowledge of such concepts.

use crate::client::orchestrator::{HttpRequest, HttpResponse};
use aws_smithy_async::future::now_or_later::NowOrLater;
use aws_smithy_http::result::ConnectorError;
use pin_project_lite::pin_project;
use std::fmt;
use std::future::Future as StdFuture;
use std::pin::Pin;
use std::sync::Arc;
use std::task::Poll;

type BoxFuture = Pin<Box<dyn StdFuture<Output = Result<HttpResponse, ConnectorError>> + Send>>;

pin_project! {
    /// Future for [`HttpConnector::call`].
    pub struct HttpConnectorFuture {
        #[pin]
        inner: NowOrLater<Result<HttpResponse, ConnectorError>, BoxFuture>,
    }
}

impl HttpConnectorFuture {
    /// Create a new `HttpConnectorFuture` with the given future.
    pub fn new<F>(future: F) -> Self
    where
        F: StdFuture<Output = Result<HttpResponse, ConnectorError>> + Send + 'static,
    {
        Self {
            inner: NowOrLater::new(Box::pin(future)),
        }
    }

    /// Create a new `HttpConnectorFuture` with the given boxed future.
    ///
    /// Use this if you already have a boxed future to avoid double boxing it.
    pub fn new_boxed(
        future: Pin<Box<dyn StdFuture<Output = Result<HttpResponse, ConnectorError>> + Send>>,
    ) -> Self {
        Self {
            inner: NowOrLater::new(future),
        }
    }

    /// Create a `HttpConnectorFuture` that is immediately ready with the given result.
    pub fn ready(result: Result<HttpResponse, ConnectorError>) -> Self {
        Self {
            inner: NowOrLater::ready(result),
        }
    }
}

impl StdFuture for HttpConnectorFuture {
    type Output = Result<HttpResponse, ConnectorError>;

    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        this.inner.poll(cx)
    }
}

/// Trait with a `call` function that asynchronously converts a request into a response.
///
@@ -16,7 +98,7 @@ use std::sync::Arc;
/// for testing.
pub trait HttpConnector: Send + Sync + fmt::Debug {
    /// Asynchronously converts a request into a response.
    fn call(&self, request: HttpRequest) -> BoxFuture<HttpResponse>;
    fn call(&self, request: HttpRequest) -> HttpConnectorFuture;
}

/// A shared [`HttpConnector`] implementation.
@@ -31,7 +113,7 @@ impl SharedHttpConnector {
}

impl HttpConnector for SharedHttpConnector {
    fn call(&self, request: HttpRequest) -> BoxFuture<HttpResponse> {
    fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
        (*self.0).call(request)
    }
}
Loading