From 9235afbca5390ea7074675ca7dbb48e69e0e228e Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 26 Aug 2022 23:00:14 +0000 Subject: [PATCH 1/6] Add runtime middleware --- .../aws-smithy-http-server/src/extension.rs | 32 ++- .../aws-smithy-http-server/src/lib.rs | 4 + .../src/make_service.rs | 42 +++ .../src/operation/handler.rs | 153 ++++++++++ .../src/operation/mod.rs | 268 ++++++++++++++++++ .../src/operation/operation_service.rs | 133 +++++++++ .../src/operation/shape.rs | 45 +++ .../src/operation/upgrade.rs | 213 ++++++++++++++ .../aws-smithy-http-server/src/rejection.rs | 21 ++ .../aws-smithy-http-server/src/request.rs | 75 ++++- 10 files changed, 983 insertions(+), 3 deletions(-) create mode 100644 rust-runtime/aws-smithy-http-server/src/make_service.rs create mode 100644 rust-runtime/aws-smithy-http-server/src/operation/handler.rs create mode 100644 rust-runtime/aws-smithy-http-server/src/operation/mod.rs create mode 100644 rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs create mode 100644 rust-runtime/aws-smithy-http-server/src/operation/shape.rs create mode 100644 rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index bf06b96d99..3f08dffc8e 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -50,9 +50,14 @@ use std::ops::Deref; +use http::StatusCode; use thiserror::Error; -use crate::request::RequestParts; +use crate::{ + body::{empty, BoxBody}, + request::{FromParts, RequestParts}, + response::IntoResponse, +}; /// Extension type used to store information about Smithy operations in HTTP responses. /// This extension type is set when it has been correctly determined that the request should be @@ -165,6 +170,31 @@ impl Deref for Extension { } } +/// The extension has not been added to the [`Request`](http::Request) or has been previously removed. +#[derive(Debug, Error)] +#[error("the `Extension` is not present in the `http::Request`")] +pub struct MissingExtension; + +impl IntoResponse for MissingExtension { + fn into_response(self) -> http::Response { + http::Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(empty()) + .expect("invalid HTTP response for missing extensions; please file a bug report under https://github.com/awslabs/smithy-rs/issues") + } +} + +impl FromParts for Extension +where + T: Clone + Send + Sync + 'static, +{ + type Rejection = MissingExtension; + + fn from_parts(parts: &mut http::request::Parts) -> Result { + parts.extensions.remove::().map(Extension).ok_or(MissingExtension) + } +} + /// Extract an [`Extension`] from a request. /// This is essentially the implementation of `FromRequest` for `Extension`, but with a /// protocol-agnostic rejection type. The actual code-generated implementation simply delegates to diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 0b2cb95484..8301312529 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -16,6 +16,10 @@ pub mod extension; #[doc(hidden)] pub mod logging; #[doc(hidden)] +pub mod make_service; +#[doc(hidden)] +pub mod operation; +#[doc(hidden)] pub mod protocols; #[doc(hidden)] pub mod rejection; diff --git a/rust-runtime/aws-smithy-http-server/src/make_service.rs b/rust-runtime/aws-smithy-http-server/src/make_service.rs new file mode 100644 index 0000000000..cf61fddd97 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/make_service.rs @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::{ + convert::Infallible, + future::{ready, Ready}, + task::{Context, Poll}, +}; + +use tower::Service; + +/// A basic [`MakeService`](tower::MakeService) which [`Clone`]s the inner service. +pub struct IntoMakeService { + svc: S, +} + +impl IntoMakeService { + /// Constructs a new [`IntoMakeService`]. + pub fn new(svc: S) -> Self { + Self { svc } + } +} + +impl Service for IntoMakeService +where + S: Clone, +{ + type Response = S; + type Error = Infallible; + type Future = Ready>; + + #[inline] + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _target: T) -> Self::Future { + ready(Ok(self.svc.clone())) + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs new file mode 100644 index 0000000000..5ab62c5274 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -0,0 +1,153 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::{ + convert::Infallible, + future::Future, + marker::PhantomData, + task::{Context, Poll}, +}; + +use futures_util::{ + future::{Map, MapErr}, + FutureExt, TryFutureExt, +}; +use tower::Service; + +use super::{OperationError, OperationShape}; + +/// A utility trait used to provide an even interface for all handlers. +pub trait Handler +where + Op: OperationShape, +{ + type Future: Future>; + + fn call(&mut self, input: Op::Input, exts: Exts) -> Self::Future; +} + +/// A utility trait used to provide an even interface over return types `Result`/`Ok`. +trait ToResult { + fn into_result(self) -> Result; +} + +// We can convert from `Result` to `Result`. +impl ToResult for Result { + fn into_result(self) -> Result { + self + } +} + +// We can convert from `Ok` to `Result`. +impl ToResult for Ok { + fn into_result(self) -> Result { + Ok(self) + } +} + +// fn(Input) -> Output +impl Handler for F +where + Op: OperationShape, + F: Fn(Op::Input) -> Fut, + Fut: Future, + Fut::Output: ToResult, +{ + type Future = Map Result>; + + fn call(&mut self, input: Op::Input, _exts: ()) -> Self::Future { + (self)(input).map(ToResult::into_result) + } +} + +// fn(Input, Ext0) -> Output +impl Handler for F +where + Op: OperationShape, + F: Fn(Op::Input, Ext0) -> Fut, + Fut: Future, + Fut::Output: ToResult, +{ + type Future = Map Result>; + + fn call(&mut self, input: Op::Input, exts: (Ext0,)) -> Self::Future { + (self)(input, exts.0).map(ToResult::into_result) + } +} + +// fn(Input, Ext0, Ext1) -> Output +impl Handler for F +where + Op: OperationShape, + F: Fn(Op::Input, Ext0, Ext1) -> Fut, + Fut: Future, + Fut::Output: ToResult, +{ + type Future = Map Result>; + + fn call(&mut self, input: Op::Input, exts: (Ext0, Ext1)) -> Self::Future { + (self)(input, exts.0, exts.1).map(ToResult::into_result) + } +} + +/// An extension trait for [`Handler`]. +pub trait HandlerExt: Handler +where + Op: OperationShape, +{ + /// Convert the [`Handler`] into a [`Service`]. + fn into_service(self) -> IntoService + where + Self: Sized, + { + IntoService { + handler: self, + _operation: PhantomData, + } + } +} + +impl HandlerExt for H +where + Op: OperationShape, + H: Handler, +{ +} + +/// A [`Service`] provided for every [`Handler`]. +pub struct IntoService { + handler: H, + _operation: PhantomData, +} + +impl Clone for IntoService +where + H: Clone, +{ + fn clone(&self) -> Self { + Self { + handler: self.handler.clone(), + _operation: PhantomData, + } + } +} + +impl Service<(Op::Input, Exts)> for IntoService +where + Op: OperationShape, + H: Handler, +{ + type Response = Op::Output; + type Error = OperationError; + type Future = MapErr Self::Error>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { + self.handler.call(input, exts).map_err(OperationError::Model) + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs new file mode 100644 index 0000000000..27a9fae8d8 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -0,0 +1,268 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! # Operations +//! +//! The shape of a [Smithy operation] is modelled by the [`OperationShape`] trait, it's associated types +//! [`OperationShape::Input`], [`OperationShape::Output`], and [`OperationShape::Error`] map to the structures +//! representing the Smithy inputs, outputs, and errors respectively. When an operation error is not specified +//! [`OperationShape::Error`] is [`Infallible`](std::convert::Infallible). +//! +//! A ZST for each Smithy operation is generated and [`OperationShape`] should be implemented on it, this will be used +//! as a helper which provides static methods and parameterizes other traits. +//! +//! The model +//! +//! ```smithy +//! operation GetShopping { +//! input: CartIdentifier, +//! output: ShoppingCart, +//! errors: [...] +//! } +//! ``` +//! +//! is identified with the implementation +//! +//! ```rust +//! # use aws_smithy_http_server::operation::OperationShape; +//! # pub struct CartIdentifier; +//! # pub struct ShoppingCart; +//! # pub enum GetShoppingError {} +//! pub struct GetShopping; +//! +//! impl OperationShape for GetShopping { +//! const NAME: &'static str = "GetShopping"; +//! +//! type Input = CartIdentifier; +//! type Output = ShoppingCart; +//! type Error = GetShoppingError; +//! } +//! ``` +//! +//! The behavior of a Smithy operation is encoded by an [`Operation`]. The [`OperationShape`] ZSTs can be used to +//! construct specific operations using [`OperationShapeExt::from_handler`] and [`OperationShapeExt::from_service`]. +//! The [from_handler](OperationShapeExt::from_handler) constructor takes a [`Handler`] whereas the +//! [from_service](OperationShapeExt::from_service) takes a [`OperationService`]. Both traits serve a similar purpose - +//! they provide a common interface over a class of structures. +//! +//! ## [`Handler`] +//! +//! The [`Handler`] trait is implemented by all closures which accept [`OperationShape::Input`] as their first +//! argument, the remaining arguments implement [`FromParts`](crate::request::FromParts), and return either +//! [`OperationShape::Output`] when [`OperationShape::Error`] is [`Infallible`](std::convert::Infallible) or +//! [`Result`]<[`OperationShape::Output`],[`OperationShape::Error`]>. The following are examples of closures which +//! implement [`Handler`]: +//! +//! ```rust +//! # use aws_smithy_http_server::Extension; +//! # pub struct CartIdentifier; +//! # pub struct ShoppingCart; +//! # pub enum GetShoppingError {} +//! # pub struct Context; +//! # pub struct ExtraContext; +//! // Simple handler where no error is given. +//! async fn handler_a(input: CartIdentifier) -> ShoppingCart { +//! todo!() +//! } +//! +//! // Handler with an extension where no error is given. +//! async fn handler_b(input: CartIdentifier, ext: Extension) -> ShoppingCart { +//! todo!() +//! } +//! +//! // More than one extension can be provided. +//! async fn handler_c(input: CartIdentifier, ext_1: Extension, ext_2: Extension) -> ShoppingCart { +//! todo!() +//! } +//! +//! // When an error is given we must return a `Result`. +//! async fn handler_d(input: CartIdentifier, ext: Extension) -> Result { +//! todo!() +//! } +//! ``` +//! +//! ## [`OperationService`] +//! +//! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with +//! `Response = Op::Output`, and `Error = OperationError`. +//! +//! We use [`OperationError`], with a `PollError` not constrained by the model, to allow the user to provide a custom +//! [`Service::poll_ready`](tower::Service::poll_ready) implementation. +//! +//! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]: +//! +//! - `Service`. +//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = GetShoppingCartError>`. +//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = GetShoppingCartError)`. +//! +//! Notice the parallels between [`OperationService`] and [`Handler`]. +//! +//! ## Constructing an [`Operation`] +//! +//! The following is an example of using both construction approaches: +//! +//! ```rust +//! # use std::task::{Poll, Context}; +//! # use aws_smithy_http_server::operation::*; +//! # use tower::Service; +//! # pub struct CartIdentifier; +//! # pub struct ShoppingCart; +//! # pub enum GetShoppingError {} +//! # pub struct GetShopping; +//! # impl OperationShape for GetShopping { +//! # const NAME: &'static str = "GetShopping"; +//! # +//! # type Input = CartIdentifier; +//! # type Output = ShoppingCart; +//! # type Error = GetShoppingError; +//! # } +//! # type OpFuture = std::future::Ready>>; +//! // Construction of an `Operation` from a `Handler`. +//! +//! async fn op_handler(input: CartIdentifier) -> Result { +//! todo!() +//! } +//! +//! let operation = GetShopping::from_handler(op_handler); +//! +//! // Construction of an `Operation` from a `Service`. +//! +//! pub struct PollError; +//! +//! pub struct OpService; +//! +//! impl Service<(CartIdentifier, ())> for OpService { +//! type Response = ShoppingCart; +//! type Error = OperationError; +//! type Future = OpFuture; +//! +//! fn poll_ready(&mut self, cx: &mut Context) -> Poll> { +//! // NOTE: This MUST NOT return `Err(OperationError::Model(_))`. +//! todo!() +//! } +//! +//! fn call(&mut self, request: (CartIdentifier, ())) -> Self::Future { +//! // NOTE: This MUST NOT return `Err(OperationError::Poll(_))`. +//! todo!() +//! } +//! } +//! +//! let operation = GetShopping::from_service(OpService); +//! +//! ``` +//! +//! ## Upgrading Smithy services to HTTP services +//! +//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is +//! constructed they are converted to a normal form +//! `Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>`. The +//! [`UpgradeLayer`] acts upon such services by converting them to +//! `Service`. +//! +//! Note that the `PollError` is still exposed, for two reasons: +//! +//! - Smithy is agnostic to `PollError` and therefore we have no prescribed way to serialize it to a [`http::Response`] +//! , unlike the operation errors. +//! - The intention of `PollError` is to signal that the underlying service is no longer able to take requests, so +//! should be discarded. See [`Service::poll_ready`](tower::Service::poll_ready). +//! +//! The [`UpgradeLayer`] and it's [`Layer::Service`] [`Upgrade`] are both parameterized by a protocol. This allows +//! for upgrading to `Service` to be protocol dependent. +//! +//! The [`Operation::upgrade`] will apply [`UpgradeLayer`] to `S` then apply the [`Layer`] `L`. The service builder +//! provided to the user will perform this composition on `build`. +//! +//! [Smithy operation]: https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation + +mod handler; +mod operation_service; +mod shape; +mod upgrade; + +use tower::{ + layer::util::{Identity, Stack}, + Layer, +}; + +pub use handler::*; +pub use operation_service::*; +pub use shape::*; +pub use upgrade::*; + +/// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`] `L`. +/// +/// The `L` is held and applied lazily during [`Operation::upgrade`]. +pub struct Operation { + inner: S, + layer: L, +} + +type StackedUpgradeService = , L> as Layer>::Service; + +impl Operation { + /// Takes the [`Operation`], containing the inner [`Service`](tower::Service) `S`, the HTTP [`Layer`] `L` and + /// composes them together using [`UpgradeLayer`] for a specific protocol and [`OperationShape`]. + /// + /// The composition is made explicit in the method constraints and return type. + pub fn upgrade(self) -> StackedUpgradeService + where + UpgradeLayer: Layer, + L: Layer< as Layer>::Service>, + { + let Self { inner, layer } = self; + let layer = Stack::new(UpgradeLayer::new(), layer); + layer.layer(inner) + } +} + +impl Operation> { + /// Creates an [`Operation`] from a [`Service`](tower::Service). + pub fn from_service(inner: S) -> Self + where + Op: OperationShape, + S: OperationService, + { + Self { + inner: inner.into_unflatten(), + layer: Identity::new(), + } + } +} + +impl Operation> { + /// Creates an [`Operation`] from a [`Handler`]. + pub fn from_handler(handler: H) -> Self + where + Op: OperationShape, + H: Handler, + { + Self { + inner: handler.into_service(), + layer: Identity::new(), + } + } +} + +impl Operation { + /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. + pub fn layer(self, layer: NewL) -> Operation> { + Operation { + inner: self.inner, + layer: Stack::new(self.layer, layer), + } + } +} + +/// A marker struct indicating an [`Operation`] has not been set in a builder. +pub struct OperationNotSet; + +/// The operation [`Service`](tower::Service) has two classes of failure modes - the failure models specified by +/// the Smithy model and failures to [`Service::poll_ready`](tower::Service::poll_ready). +pub enum OperationError { + /// An error modelled by the Smithy model occurred. + Model(ModelError), + /// A [`Service::poll_ready`](tower::Service::poll_ready) failure occurred. + Poll(PollError), +} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs new file mode 100644 index 0000000000..10d2acbd2d --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs @@ -0,0 +1,133 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::{ + marker::PhantomData, + task::{Context, Poll}, +}; + +use tower::Service; + +use super::{OperationError, OperationShape}; + +/// A utility trait used to provide an even interface for all operation services. +/// +/// This serves to take [`Service`]s of the form `Service<(Op::Input, Ext0, Ext1, ...)>` to the canonical +/// representation of `Service<(Input, (Ext0, Ext1, ...))>` inline with +/// [`IntoService`](super::IntoService). +pub trait OperationService: + Service> +where + Op: OperationShape, +{ + type Normalized; + + // Normalize the request type. + fn normalize(input: Op::Input, exts: Exts) -> Self::Normalized; +} + +// `Service` +impl OperationService for S +where + Op: OperationShape, + S: Service>, +{ + type Normalized = Op::Input; + + fn normalize(input: Op::Input, _exts: ()) -> Self::Normalized { + input + } +} + +// `Service<(Op::Input, Ext0)>` +impl OperationService for S +where + Op: OperationShape, + S: Service<(Op::Input, Ext0), Response = Op::Output, Error = OperationError>, +{ + type Normalized = (Op::Input, Ext0); + + fn normalize(input: Op::Input, exts: (Ext0,)) -> Self::Normalized { + (input, exts.0) + } +} + +// `Service<(Op::Input, Ext0, Ext1)>` +impl OperationService for S +where + Op: OperationShape, + S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = OperationError>, +{ + type Normalized = (Op::Input, Ext0, Ext1); + + fn normalize(input: Op::Input, exts: (Ext0, Ext1)) -> Self::Normalized { + (input, exts.0, exts.1) + } +} + +/// An extension trait of [`OperationService`]. +pub trait OperationServiceExt: OperationService +where + Op: OperationShape, +{ + /// Convert the [`OperationService`] into a canonicalized [`Service`]. + fn into_unflatten(self) -> Normalize + where + Self: Sized, + { + Normalize { + inner: self, + _operation: PhantomData, + _poll_error: PhantomData, + } + } +} + +impl OperationServiceExt for F +where + Op: OperationShape, + F: OperationService, +{ +} + +/// A [`Service`] normalizing the request type of a [`OperationService`]. +#[derive(Debug)] +pub struct Normalize { + inner: S, + _operation: PhantomData, + _poll_error: PhantomData, +} + +impl Clone for Normalize +where + S: Clone, +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + _operation: PhantomData, + _poll_error: PhantomData, + } + } +} + +impl Service<(Op::Input, Exts)> for Normalize +where + Op: OperationShape, + S: OperationService, +{ + type Response = S::Response; + type Error = S::Error; + type Future = >::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { + let req = S::normalize(input, exts); + self.inner.call(req) + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs new file mode 100644 index 0000000000..9990326279 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -0,0 +1,45 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::{Handler, IntoService, Normalize, Operation, OperationService}; + +/// Models the [Smithy Operation shape]. +/// +/// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation +pub trait OperationShape { + /// The name of the operation. + const NAME: &'static str; + + /// The operation input. + type Input; + /// The operation output. + type Output; + /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error + /// exists. + type Error; +} + +/// An extension trait over [`OperationShape`]. +pub trait OperationShapeExt: OperationShape { + /// Creates a new [`Operation`] for well-formed [`Handler`]s. + fn from_handler(handler: H) -> Operation> + where + H: Handler, + Self: Sized, + { + Operation::from_handler(handler) + } + + /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. + fn from_service(svc: S) -> Operation> + where + S: OperationService, + Self: Sized, + { + Operation::from_service(svc) + } +} + +impl OperationShapeExt for S where S: OperationShape {} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs new file mode 100644 index 0000000000..55f751936b --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -0,0 +1,213 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(clippy::type_complexity)] + +use std::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_util::ready; +use pin_project_lite::pin_project; +use tower::{Layer, Service}; + +use crate::{ + request::{FromParts, FromRequest}, + response::IntoResponse, +}; + +use super::{OperationError, OperationShape}; + +/// A [`Layer`] responsible for taking an operation [`Service`], accepting and returning Smithy +/// types and converting it into a [`Service`] taking and returning [`http`] types. +/// +/// See [`Upgrade`]. +#[derive(Debug, Clone)] +pub struct UpgradeLayer { + _protocol: PhantomData, + _operation: PhantomData, + _exts: PhantomData, + _body: PhantomData, +} + +impl Default for UpgradeLayer { + fn default() -> Self { + Self { + _protocol: PhantomData, + _operation: PhantomData, + _exts: PhantomData, + _body: PhantomData, + } + } +} + +impl UpgradeLayer { + /// Creates a new [`UpgradeLayer`]. + pub fn new() -> Self { + Self::default() + } +} + +impl Layer for UpgradeLayer { + type Service = Upgrade; + + fn layer(&self, inner: S) -> Self::Service { + Upgrade { + _protocol: PhantomData, + _operation: PhantomData, + _body: PhantomData, + _exts: PhantomData, + inner, + } + } +} + +/// An alias allowing for quick access to [`UpgradeLayer`]s target [`Service`]. +pub type UpgradedService = as Layer>::Service; + +/// A [`Service`] responsible for wrapping an operation [`Service`] accepting and returning Smithy +/// types, and converting it into a [`Service`] accepting and returning [`http`] types. +pub struct Upgrade { + _protocol: PhantomData, + _operation: PhantomData, + _exts: PhantomData, + _body: PhantomData, + inner: S, +} + +impl Clone for Upgrade +where + S: Clone, +{ + fn clone(&self) -> Self { + Self { + _protocol: PhantomData, + _operation: PhantomData, + _body: PhantomData, + _exts: PhantomData, + inner: self.inner.clone(), + } + } +} + +pin_project! { + /// The [`Service::Future`] of [`Upgrade`]. + pub struct UpgradeFuture + where + Operation: OperationShape, + (Operation::Input, Exts): FromRequest, + S: Service<(Operation::Input, Exts)>, + { + service: S, + #[pin] + inner: Inner<<(Operation::Input, Exts) as FromRequest>::Future, S::Future> + } +} + +pin_project! { + #[project = InnerProj] + #[project_replace = InnerProjReplace] + enum Inner { + FromRequest { + #[pin] + inner: FromFut + }, + Inner { + #[pin] + call: HandlerFut + } + } +} + +impl Future for UpgradeFuture +where + // `Op` is used to specify the operation shape + Op: OperationShape, + // Smithy input must convert from a HTTP request + Op::Input: FromRequest, + // Smithy output must convert into a HTTP response + Op::Output: IntoResponse

, + // Smithy error must convert into a HTTP response + OpError: IntoResponse

, + + // Must be able to convert extensions + E: FromParts

, + + // The signature of the inner service is correct + S: Service<(Op::Input, E), Response = Op::Output, Error = OperationError>, +{ + type Output = Result, PollError>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + let mut this = self.as_mut().project(); + let this2 = this.inner.as_mut().project(); + + let call = match this2 { + InnerProj::FromRequest { inner } => { + let result = ready!(inner.poll(cx)); + match result { + Ok(ok) => this.service.call(ok), + Err(err) => return Poll::Ready(Ok(err.into_response())), + } + } + InnerProj::Inner { call } => { + let result = ready!(call.poll(cx)); + let output = match result { + Ok(ok) => ok.into_response(), + Err(OperationError::Model(err)) => err.into_response(), + Err(OperationError::Poll(_)) => { + unreachable!("poll error should not be raised") + } + }; + return Poll::Ready(Ok(output)); + } + }; + + this.inner.as_mut().project_replace(Inner::Inner { call }); + } + } +} + +impl Service> for Upgrade +where + // `Op` is used to specify the operation shape + Op: OperationShape, + // Smithy input must convert from a HTTP request + Op::Input: FromRequest, + // Smithy output must convert into a HTTP response + Op::Output: IntoResponse

, + // Smithy error must convert into a HTTP response + OpError: IntoResponse

, + + // Must be able to convert extensions + E: FromParts

, + + // The signature of the inner service is correct + S: Service<(Op::Input, E), Response = Op::Output, Error = OperationError> + Clone, +{ + type Response = http::Response; + type Error = PollError; + type Future = UpgradeFuture; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx).map_err(|err| match err { + OperationError::Poll(err) => err, + OperationError::Model(_) => unreachable!("operation error should not be raised"), + }) + } + + fn call(&mut self, req: http::Request) -> Self::Future { + UpgradeFuture { + service: self.inner.clone(), + inner: Inner::FromRequest { + inner: <(Op::Input, E) as FromRequest>::from_request(req), + }, + } + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index f5accce847..a382902156 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -43,6 +43,8 @@ use strum_macros::Display; +use crate::response::IntoResponse; + /// Rejection used for when failing to extract an [`crate::Extension`] from an incoming [request's /// extensions]. Contains one variant for each way the extractor can fail. /// @@ -265,3 +267,22 @@ convert_to_request_rejection!(hyper::Error, HttpBody); // Required in order to accept Lambda HTTP requests using `Router`. convert_to_request_rejection!(lambda_http::Error, HttpBody); + +/// A sum type rejection, implementing [`IntoResponse`] when both variants do. +pub enum EitherRejection { + Left(Left), + Right(Right), +} + +impl IntoResponse

for EitherRejection +where + L: IntoResponse

, + R: IntoResponse

, +{ + fn into_response(self) -> http::Response { + match self { + EitherRejection::Left(left) => left.into_response(), + EitherRejection::Right(right) => right.into_response(), + } + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/request.rs b/rust-runtime/aws-smithy-http-server/src/request.rs index 6b28d255b6..000e230fbf 100644 --- a/rust-runtime/aws-smithy-http-server/src/request.rs +++ b/rust-runtime/aws-smithy-http-server/src/request.rs @@ -32,7 +32,15 @@ * DEALINGS IN THE SOFTWARE. */ -use http::{Extensions, HeaderMap, Request, Uri}; +use std::future::{ready, Future, Ready}; + +use futures_util::{ + future::{try_join, MapErr, MapOk, TryJoin}, + TryFutureExt, +}; +use http::{request::Parts, Extensions, HeaderMap, Request, Uri}; + +use crate::{rejection::EitherRejection, response::IntoResponse}; #[doc(hidden)] #[derive(Debug)] @@ -54,7 +62,7 @@ impl RequestParts { #[doc(hidden)] pub fn new(req: Request) -> Self { let ( - http::request::Parts { + Parts { uri, headers, extensions, @@ -99,3 +107,66 @@ impl RequestParts { self.extensions.as_ref() } } + +/// Provides a protocol aware extraction from a [`Request`]. This borrows the +/// [`Parts`], in contrast to [`FromRequest`]. +pub trait FromParts: Sized { + type Rejection: IntoResponse; + + /// Extracts `self` from a [`Parts`] synchronously. + fn from_parts(parts: &mut Parts) -> Result; +} + +impl FromParts

for (T1, T2) +where + T1: FromParts

, + T2: FromParts

, +{ + type Rejection = EitherRejection; + + fn from_parts(parts: &mut Parts) -> Result { + let t1 = T1::from_parts(parts).map_err(EitherRejection::Left)?; + let t2 = T2::from_parts(parts).map_err(EitherRejection::Right)?; + Ok((t1, t2)) + } +} + +/// Provides a protocol aware extraction from a [`Request`]. This consumes the +/// [`Request`], in contrast to [`FromParts`]. +pub trait FromRequest: Sized { + type Rejection: IntoResponse; + type Future: Future>; + + /// Extracts `self` from a [`Request`] asynchronously. + fn from_request(request: Request) -> Self::Future; +} + +impl<'a, P, B, T1> FromRequest for (T1,) +where + T1: FromRequest, +{ + type Rejection = T1::Rejection; + type Future = MapOk (T1,)>; + + fn from_request(request: Request) -> Self::Future { + T1::from_request(request).map_ok(|t1| (t1,)) + } +} + +impl FromRequest for (T1, T2) +where + T1: FromRequest, + T2: FromParts

, +{ + type Rejection = EitherRejection; + type Future = TryJoin Self::Rejection>, Ready>>; + + fn from_request(request: Request) -> Self::Future { + let (mut parts, body) = request.into_parts(); + let t2_result = T2::from_parts(&mut parts).map_err(EitherRejection::Right); + try_join( + T1::from_request(Request::from_parts(parts, body)).map_err(EitherRejection::Left), + ready(t2_result), + ) + } +} From 5ec3d8732f8d86bfd9b3f6b09b0845d911872e35 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Tue, 30 Aug 2022 10:19:01 +0000 Subject: [PATCH 2/6] Improve documentation --- .../src/operation/handler.rs | 2 +- .../src/operation/mod.rs | 26 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs index 5ab62c5274..c1e3979889 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -40,7 +40,7 @@ impl ToResult for Result { } } -// We can convert from `Ok` to `Result`. +// We can convert from `Ok` to `Result`. impl ToResult for Ok { fn into_result(self) -> Result { Ok(self) diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 27a9fae8d8..94270bff85 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -93,9 +93,9 @@ //! //! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]: //! -//! - `Service`. -//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = GetShoppingCartError>`. -//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = GetShoppingCartError)`. +//! - `Service>`. +//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = OperationError>`. +//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = OperationError)`. //! //! Notice the parallels between [`OperationService`] and [`Handler`]. //! @@ -133,7 +133,7 @@ //! //! pub struct OpService; //! -//! impl Service<(CartIdentifier, ())> for OpService { +//! impl Service for OpService { //! type Response = ShoppingCart; //! type Error = OperationError; //! type Future = OpFuture; @@ -202,6 +202,14 @@ pub struct Operation { type StackedUpgradeService = , L> as Layer>::Service; impl Operation { + /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. + pub fn layer(self, layer: NewL) -> Operation> { + Operation { + inner: self.inner, + layer: Stack::new(self.layer, layer), + } + } + /// Takes the [`Operation`], containing the inner [`Service`](tower::Service) `S`, the HTTP [`Layer`] `L` and /// composes them together using [`UpgradeLayer`] for a specific protocol and [`OperationShape`]. /// @@ -245,16 +253,6 @@ impl Operation> { } } -impl Operation { - /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. - pub fn layer(self, layer: NewL) -> Operation> { - Operation { - inner: self.inner, - layer: Stack::new(self.layer, layer), - } - } -} - /// A marker struct indicating an [`Operation`] has not been set in a builder. pub struct OperationNotSet; From d3e8580541c050e55610b10728cfa78d33271f12 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Tue, 30 Aug 2022 10:26:54 +0000 Subject: [PATCH 3/6] cargo clippy --- .../src/operation/upgrade.rs | 32 +++++++++---------- .../aws-smithy-http-server/src/request.rs | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index 55f751936b..0aa9e9487b 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#![allow(clippy::type_complexity)] - use std::{ future::Future, marker::PhantomData, @@ -95,20 +93,6 @@ where } } -pin_project! { - /// The [`Service::Future`] of [`Upgrade`]. - pub struct UpgradeFuture - where - Operation: OperationShape, - (Operation::Input, Exts): FromRequest, - S: Service<(Operation::Input, Exts)>, - { - service: S, - #[pin] - inner: Inner<<(Operation::Input, Exts) as FromRequest>::Future, S::Future> - } -} - pin_project! { #[project = InnerProj] #[project_replace = InnerProjReplace] @@ -124,6 +108,22 @@ pin_project! { } } +type InnerAlias = Inner<<(Input, Exts) as FromRequest>::Future, Fut>; + +pin_project! { + /// The [`Service::Future`] of [`Upgrade`]. + pub struct UpgradeFuture + where + Operation: OperationShape, + (Operation::Input, Exts): FromRequest, + S: Service<(Operation::Input, Exts)>, + { + service: S, + #[pin] + inner: InnerAlias + } +} + impl Future for UpgradeFuture where // `Op` is used to specify the operation shape diff --git a/rust-runtime/aws-smithy-http-server/src/request.rs b/rust-runtime/aws-smithy-http-server/src/request.rs index 000e230fbf..2775a68cf5 100644 --- a/rust-runtime/aws-smithy-http-server/src/request.rs +++ b/rust-runtime/aws-smithy-http-server/src/request.rs @@ -141,7 +141,7 @@ pub trait FromRequest: Sized { fn from_request(request: Request) -> Self::Future; } -impl<'a, P, B, T1> FromRequest for (T1,) +impl FromRequest for (T1,) where T1: FromRequest, { From cc2bdb39522fa0a2717e71f4483c3733bccecf25 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Tue, 30 Aug 2022 12:08:05 +0000 Subject: [PATCH 4/6] Address feedback --- .../aws-smithy-http-server/src/extension.rs | 7 +++--- .../src/operation/handler.rs | 22 +++++++++---------- .../src/operation/mod.rs | 20 ++++++++--------- .../src/operation/upgrade.rs | 20 ++++++++--------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 3f08dffc8e..d0b4c1cdd9 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -177,10 +177,9 @@ pub struct MissingExtension; impl IntoResponse for MissingExtension { fn into_response(self) -> http::Response { - http::Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(empty()) - .expect("invalid HTTP response for missing extensions; please file a bug report under https://github.com/awslabs/smithy-rs/issues") + let mut response = http::Response::new(empty()); + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + response } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs index c1e3979889..4f0c8255e9 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -18,7 +18,7 @@ use tower::Service; use super::{OperationError, OperationShape}; -/// A utility trait used to provide an even interface for all handlers. +/// A utility trait used to provide an even interface for all operation handlers. pub trait Handler where Op: OperationShape, @@ -29,19 +29,19 @@ where } /// A utility trait used to provide an even interface over return types `Result`/`Ok`. -trait ToResult { +trait IntoResult { fn into_result(self) -> Result; } // We can convert from `Result` to `Result`. -impl ToResult for Result { +impl IntoResult for Result { fn into_result(self) -> Result { self } } -// We can convert from `Ok` to `Result`. -impl ToResult for Ok { +// We can convert from `T` to `Result`. +impl IntoResult for Ok { fn into_result(self) -> Result { Ok(self) } @@ -53,12 +53,12 @@ where Op: OperationShape, F: Fn(Op::Input) -> Fut, Fut: Future, - Fut::Output: ToResult, + Fut::Output: IntoResult, { type Future = Map Result>; fn call(&mut self, input: Op::Input, _exts: ()) -> Self::Future { - (self)(input).map(ToResult::into_result) + (self)(input).map(IntoResult::into_result) } } @@ -68,12 +68,12 @@ where Op: OperationShape, F: Fn(Op::Input, Ext0) -> Fut, Fut: Future, - Fut::Output: ToResult, + Fut::Output: IntoResult, { type Future = Map Result>; fn call(&mut self, input: Op::Input, exts: (Ext0,)) -> Self::Future { - (self)(input, exts.0).map(ToResult::into_result) + (self)(input, exts.0).map(IntoResult::into_result) } } @@ -83,12 +83,12 @@ where Op: OperationShape, F: Fn(Op::Input, Ext0, Ext1) -> Fut, Fut: Future, - Fut::Output: ToResult, + Fut::Output: IntoResult, { type Future = Map Result>; fn call(&mut self, input: Op::Input, exts: (Ext0, Ext1)) -> Self::Future { - (self)(input, exts.0, exts.1).map(ToResult::into_result) + (self)(input, exts.0, exts.1).map(IntoResult::into_result) } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 94270bff85..7332e63ded 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -3,15 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! # Operations +//! # Operations. //! //! The shape of a [Smithy operation] is modelled by the [`OperationShape`] trait, it's associated types //! [`OperationShape::Input`], [`OperationShape::Output`], and [`OperationShape::Error`] map to the structures //! representing the Smithy inputs, outputs, and errors respectively. When an operation error is not specified //! [`OperationShape::Error`] is [`Infallible`](std::convert::Infallible). //! -//! A ZST for each Smithy operation is generated and [`OperationShape`] should be implemented on it, this will be used -//! as a helper which provides static methods and parameterizes other traits. +//! We should generate a zero-sized type (ZST) for each Smithy operation and [`OperationShape`] should be implemented +//! on it. This will be used as a helper - providing static methods and parameterizing other traits. //! //! The model //! @@ -25,7 +25,7 @@ //! //! is identified with the implementation //! -//! ```rust +//! ```rust,no_run //! # use aws_smithy_http_server::operation::OperationShape; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; @@ -55,7 +55,7 @@ //! [`Result`]<[`OperationShape::Output`],[`OperationShape::Error`]>. The following are examples of closures which //! implement [`Handler`]: //! -//! ```rust +//! ```rust,no_run //! # use aws_smithy_http_server::Extension; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; @@ -86,7 +86,7 @@ //! ## [`OperationService`] //! //! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with -//! `Response = Op::Output`, and `Error = OperationError`. +//! `Response = Op::Output`, and `Error = OperationError`. //! //! We use [`OperationError`], with a `PollError` not constrained by the model, to allow the user to provide a custom //! [`Service::poll_ready`](tower::Service::poll_ready) implementation. @@ -103,7 +103,7 @@ //! //! The following is an example of using both construction approaches: //! -//! ```rust +//! ```rust,no_run //! # use std::task::{Poll, Context}; //! # use aws_smithy_http_server::operation::*; //! # use tower::Service; @@ -143,7 +143,7 @@ //! todo!() //! } //! -//! fn call(&mut self, request: (CartIdentifier, ())) -> Self::Future { +//! fn call(&mut self, request: CartIdentifier) -> Self::Future { //! // NOTE: This MUST NOT return `Err(OperationError::Poll(_))`. //! todo!() //! } @@ -156,7 +156,7 @@ //! ## Upgrading Smithy services to HTTP services //! //! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is -//! constructed they are converted to a normal form +//! constructed they are converted to a canonical form //! `Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>`. The //! [`UpgradeLayer`] acts upon such services by converting them to //! `Service`. @@ -262,5 +262,5 @@ pub enum OperationError { /// An error modelled by the Smithy model occurred. Model(ModelError), /// A [`Service::poll_ready`](tower::Service::poll_ready) failure occurred. - Poll(PollError), + PollReady(PollError), } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index 0aa9e9487b..17dd7b29ae 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -124,7 +124,7 @@ pin_project! { } } -impl Future for UpgradeFuture +impl Future for UpgradeFuture where // `Op` is used to specify the operation shape Op: OperationShape, @@ -136,10 +136,10 @@ where OpError: IntoResponse

, // Must be able to convert extensions - E: FromParts

, + Exts: FromParts

, // The signature of the inner service is correct - S: Service<(Op::Input, E), Response = Op::Output, Error = OperationError>, + S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>, { type Output = Result, PollError>; @@ -161,7 +161,7 @@ where let output = match result { Ok(ok) => ok.into_response(), Err(OperationError::Model(err)) => err.into_response(), - Err(OperationError::Poll(_)) => { + Err(OperationError::PollReady(_)) => { unreachable!("poll error should not be raised") } }; @@ -174,7 +174,7 @@ where } } -impl Service> for Upgrade +impl Service> for Upgrade where // `Op` is used to specify the operation shape Op: OperationShape, @@ -186,18 +186,18 @@ where OpError: IntoResponse

, // Must be able to convert extensions - E: FromParts

, + Exts: FromParts

, // The signature of the inner service is correct - S: Service<(Op::Input, E), Response = Op::Output, Error = OperationError> + Clone, + S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError> + Clone, { type Response = http::Response; type Error = PollError; - type Future = UpgradeFuture; + type Future = UpgradeFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx).map_err(|err| match err { - OperationError::Poll(err) => err, + OperationError::PollReady(err) => err, OperationError::Model(_) => unreachable!("operation error should not be raised"), }) } @@ -206,7 +206,7 @@ where UpgradeFuture { service: self.inner.clone(), inner: Inner::FromRequest { - inner: <(Op::Input, E) as FromRequest>::from_request(req), + inner: <(Op::Input, Exts) as FromRequest>::from_request(req), }, } } From f7d08928b2e860385f9d7d1ff08cb974b3073d3a Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Tue, 30 Aug 2022 12:52:12 +0000 Subject: [PATCH 5/6] Address feedback --- .../aws-smithy-http-server/src/lib.rs | 2 - .../src/make_service.rs | 42 ------------------- .../src/operation/mod.rs | 12 +++--- 3 files changed, 6 insertions(+), 50 deletions(-) delete mode 100644 rust-runtime/aws-smithy-http-server/src/make_service.rs diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 8301312529..b032060bbc 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -16,8 +16,6 @@ pub mod extension; #[doc(hidden)] pub mod logging; #[doc(hidden)] -pub mod make_service; -#[doc(hidden)] pub mod operation; #[doc(hidden)] pub mod protocols; diff --git a/rust-runtime/aws-smithy-http-server/src/make_service.rs b/rust-runtime/aws-smithy-http-server/src/make_service.rs deleted file mode 100644 index cf61fddd97..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/make_service.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::{ - convert::Infallible, - future::{ready, Ready}, - task::{Context, Poll}, -}; - -use tower::Service; - -/// A basic [`MakeService`](tower::MakeService) which [`Clone`]s the inner service. -pub struct IntoMakeService { - svc: S, -} - -impl IntoMakeService { - /// Constructs a new [`IntoMakeService`]. - pub fn new(svc: S) -> Self { - Self { svc } - } -} - -impl Service for IntoMakeService -where - S: Clone, -{ - type Response = S; - type Error = Infallible; - type Future = Ready>; - - #[inline] - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _target: T) -> Self::Future { - ready(Ok(self.svc.clone())) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 7332e63ded..0aefe25e3a 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -5,7 +5,7 @@ //! # Operations. //! -//! The shape of a [Smithy operation] is modelled by the [`OperationShape`] trait, it's associated types +//! The shape of a [Smithy operation] is modelled by the [`OperationShape`] trait. Its associated types //! [`OperationShape::Input`], [`OperationShape::Output`], and [`OperationShape::Error`] map to the structures //! representing the Smithy inputs, outputs, and errors respectively. When an operation error is not specified //! [`OperationShape::Error`] is [`Infallible`](std::convert::Infallible). @@ -62,12 +62,12 @@ //! # pub enum GetShoppingError {} //! # pub struct Context; //! # pub struct ExtraContext; -//! // Simple handler where no error is given. +//! // Simple handler where no error is modelled. //! async fn handler_a(input: CartIdentifier) -> ShoppingCart { //! todo!() //! } //! -//! // Handler with an extension where no error is given. +//! // Handler with an extension where no error is modelled. //! async fn handler_b(input: CartIdentifier, ext: Extension) -> ShoppingCart { //! todo!() //! } @@ -77,7 +77,7 @@ //! todo!() //! } //! -//! // When an error is given we must return a `Result`. +//! // When an error is modelled we must return a `Result`. //! async fn handler_d(input: CartIdentifier, ext: Extension) -> Result { //! todo!() //! } @@ -256,8 +256,8 @@ impl Operation> { /// A marker struct indicating an [`Operation`] has not been set in a builder. pub struct OperationNotSet; -/// The operation [`Service`](tower::Service) has two classes of failure modes - the failure models specified by -/// the Smithy model and failures to [`Service::poll_ready`](tower::Service::poll_ready). +/// The operation [`Service`](tower::Service) has two classes of failure modes - those specified by the Smithy model +/// and those associated with [`Service::poll_ready`](tower::Service::poll_ready). pub enum OperationError { /// An error modelled by the Smithy model occurred. Model(ModelError), From 7eb8e1aee55bb4ee235fc360b72ef6c10504d21c Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Wed, 31 Aug 2022 13:14:53 +0000 Subject: [PATCH 6/6] Rename into_unflatten to canonicalize --- rust-runtime/aws-smithy-http-server/src/operation/mod.rs | 2 +- .../aws-smithy-http-server/src/operation/operation_service.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 0aefe25e3a..70199714c2 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -233,7 +233,7 @@ impl Operation> { S: OperationService, { Self { - inner: inner.into_unflatten(), + inner: inner.canonicalize(), layer: Identity::new(), } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs index 10d2acbd2d..150cd2c65a 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs @@ -73,7 +73,7 @@ where Op: OperationShape, { /// Convert the [`OperationService`] into a canonicalized [`Service`]. - fn into_unflatten(self) -> Normalize + fn canonicalize(self) -> Normalize where Self: Sized, {