From 8f8094052055c7adaa0cea3f95124d2fdc753b12 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 19 Aug 2022 12:58:50 +0000 Subject: [PATCH] Allow handler extensions --- generated/examples/usage.rs | 41 ++++-- generated/src/operations/empty_operation.rs | 2 +- .../src/operations/get_pokemon_species.rs | 2 +- generated/src/services/pokemon_service.rs | 14 +- runtime/src/operation/flattened.rs | 135 ++++++++++++++++++ runtime/src/operation/handler.rs | 121 ++++++++-------- runtime/src/operation/http_conversions.rs | 107 ++++++++++++++ runtime/src/operation/mod.rs | 42 ++++-- runtime/src/operation/shape.rs | 31 ++-- runtime/src/operation/upgrade.rs | 87 ++++++----- 10 files changed, 425 insertions(+), 157 deletions(-) create mode 100644 runtime/src/operation/flattened.rs create mode 100644 runtime/src/operation/http_conversions.rs diff --git a/generated/examples/usage.rs b/generated/examples/usage.rs index 2b31304..a490b67 100644 --- a/generated/examples/usage.rs +++ b/generated/examples/usage.rs @@ -1,34 +1,35 @@ use std::{convert::Infallible, future::Ready, task::Poll}; use generated::{operations::*, services::*, structures::*}; -use runtime::operation::{AdjoinState, OperationError, OperationShapeExt}; +use runtime::operation::{Extension, OperationError, OperationShapeExt}; use tower::{util::MapResponseLayer, Service}; -/// Fallible handler with state +// Fallible handler with extensions. async fn get_pokemon_species_stateful( _input: GetPokemonSpeciesInput, - _state: usize, + _ext_a: Extension, + _ext_b: Extension, ) -> Result { todo!() } -/// Fallible handler without state +// Fallible handler without extensions. async fn get_pokemon_species( _input: GetPokemonSpeciesInput, ) -> Result { todo!() } -/// Infallible handler without state +// Infallible handler without extensions. async fn empty_operation(_input: EmptyOperationInput) -> EmptyOperationOutput { todo!() } -/// Bespoke implementation of `EmptyOperation`. +// Bespoke implementation of `EmptyOperation`. #[derive(Clone)] -struct EmptyOperationService; +struct EmptyOperationServiceA; -impl Service for EmptyOperationService { +impl Service for EmptyOperationServiceA { type Response = EmptyOperationOutput; type Error = OperationError; type Future = Ready>; @@ -42,13 +43,31 @@ impl Service for EmptyOperationService { } } +// Bespoke implementation of `EmptyOperation` with an extension. +#[derive(Clone)] +struct EmptyOperationServiceB; + +impl Service<(EmptyOperationInput, Extension)> for EmptyOperationServiceB { + type Response = EmptyOperationOutput; + type Error = OperationError; + type Future = Ready>; + + fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll> { + todo!() + } + + fn call(&mut self, _req: (EmptyOperationInput, Extension)) -> Self::Future { + todo!() + } +} + fn main() { // Various ways of constructing operations let _get_pokemon_species = GetPokemonSpecies::from_handler(get_pokemon_species); let _empty_operation = EmptyOperation::from_handler(empty_operation); - let empty_operation = EmptyOperation::from_service(EmptyOperationService); - let get_pokemon_species = - GetPokemonSpecies::from_handler(get_pokemon_species_stateful.with_state(29)); + let _empty_operation = EmptyOperation::from_service(EmptyOperationServiceA); + let empty_operation = EmptyOperation::from_service(EmptyOperationServiceB); + let get_pokemon_species = GetPokemonSpecies::from_handler(get_pokemon_species_stateful); // We can apply a layer to them let get_pokemon_species = get_pokemon_species.layer(MapResponseLayer::new(|resp| resp)); diff --git a/generated/src/operations/empty_operation.rs b/generated/src/operations/empty_operation.rs index 374cb46..02c8b8d 100644 --- a/generated/src/operations/empty_operation.rs +++ b/generated/src/operations/empty_operation.rs @@ -32,7 +32,7 @@ impl FromRequest for EmptyOperationInput { type Future = Ready>; - fn from_request(_request: http::Request) -> Self::Future { + fn from_request(_request: &mut http::Request) -> Self::Future { todo!() } } diff --git a/generated/src/operations/get_pokemon_species.rs b/generated/src/operations/get_pokemon_species.rs index 1fb0b18..6f171e8 100644 --- a/generated/src/operations/get_pokemon_species.rs +++ b/generated/src/operations/get_pokemon_species.rs @@ -34,7 +34,7 @@ impl FromRequest for GetPokemonSpeciesIn type Future = Ready>; - fn from_request(_request: http::Request) -> Self::Future { + fn from_request(_request: &mut http::Request) -> Self::Future { todo!() } } diff --git a/generated/src/services/pokemon_service.rs b/generated/src/services/pokemon_service.rs index 8106983..4ac555f 100644 --- a/generated/src/services/pokemon_service.rs +++ b/generated/src/services/pokemon_service.rs @@ -112,12 +112,12 @@ impl PokemonServiceBuilder { } impl PokemonServiceBuilder, Operation> { - pub fn build(self) -> PokemonService> + pub fn build(self) -> PokemonService> where // GetPokemonSpecies composition - UpgradeLayer: Layer, - L1: Layer>, - S1: Service<::Input>, + UpgradeLayer: Layer, + L1: Layer>, + S1: Service<(::Input, Exts1)>, L1::Service: Service, Response = http::Response>>, L1::Service: Clone + Send + 'static, >>::Future: Send + 'static, @@ -125,9 +125,9 @@ impl PokemonServiceBuilder, Operation> Into> + 'static, // EmptyOperation composition - UpgradeLayer: Layer, - L2: Layer>, - S2: Service<::Input>, + UpgradeLayer: Layer, + L2: Layer>, + S2: Service<(::Input, Exts2)>, L2::Service: Service, Response = http::Response>>, L2::Service: Clone + Send + 'static, >>::Future: Send + 'static, diff --git a/runtime/src/operation/flattened.rs b/runtime/src/operation/flattened.rs new file mode 100644 index 0000000..addea0b --- /dev/null +++ b/runtime/src/operation/flattened.rs @@ -0,0 +1,135 @@ +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<(Input, Arg0, Arg1, ...)>` to the canonical representation of +/// `Service<(Input, (Arg0, Arg1, ...))>` inline with [`IntoService`](super::IntoService). +pub trait Flattened: + Service> +where + Op: OperationShape, +{ + type Flattened; + + // Unflatten the request type. + fn unflatten(input: Op::Input, exts: Exts) -> Self::Flattened; +} + +// `Service` +impl Flattened for S +where + Op: OperationShape, + S: Service>, +{ + type Flattened = Op::Input; + + fn unflatten(input: Op::Input, _exts: ()) -> Self::Flattened { + input + } +} + +// `Service<(Op::Input, Arg0)>` +impl Flattened for S +where + Op: OperationShape, + S: Service< + (Op::Input, Arg0), + Response = Op::Output, + Error = OperationError, + >, +{ + type Flattened = (Op::Input, Arg0); + + fn unflatten(input: Op::Input, exts: (Arg0,)) -> Self::Flattened { + (input, exts.0) + } +} + +// `Service<(Op::Input, Arg0, Arg1)>` +impl Flattened for S +where + Op: OperationShape, + S: Service< + (Op::Input, Arg0, Arg1), + Response = Op::Output, + Error = OperationError, + >, +{ + type Flattened = (Op::Input, Arg0, Arg1); + + fn unflatten(input: Op::Input, exts: (Arg0, Arg1)) -> Self::Flattened { + (input, exts.0, exts.1) + } +} + +/// An extension trait of [`Flattened`]. +pub trait FlattenedExt: Flattened +where + Op: OperationShape, +{ + /// Convert the [`Flattened`] into a canonicalized [`Service`]. + fn into_unflatten(self) -> IntoUnflattened + where + Self: Sized, + { + IntoUnflattened { + inner: self, + _operation: PhantomData, + _poll_error: PhantomData, + } + } +} + +impl FlattenedExt for F +where + Op: OperationShape, + F: Flattened, +{ +} + +/// A [`Service`] canonicalizing the request type of a [`Flattened`]. +#[derive(Debug)] +pub struct IntoUnflattened { + inner: S, + _operation: PhantomData, + _poll_error: PhantomData, +} + +impl Clone for IntoUnflattened +where + S: Clone, +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + _operation: PhantomData, + _poll_error: PhantomData, + } + } +} + +impl Service<(Op::Input, Exts)> for IntoUnflattened +where + Op: OperationShape, + S: Flattened, +{ + 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::unflatten(input, exts); + self.inner.call(req) + } +} diff --git a/runtime/src/operation/handler.rs b/runtime/src/operation/handler.rs index 29652d1..dc2058e 100644 --- a/runtime/src/operation/handler.rs +++ b/runtime/src/operation/handler.rs @@ -11,20 +11,16 @@ use futures::{ }; use tower::Service; -/// The operation [`Service`] has two classes of failure modes - the failure models specified by -/// the Smithy model and failures to [`Service::poll_ready`]. -pub enum OperationError { - /// A [`Service::poll_ready`] failure occured. - Poll(PollError), - /// An error modelled by the Smithy model occured. - Smithy(SmithyError), -} +use super::{OperationError, OperationShape}; /// A utility trait used to provide an even interface for all handlers. -pub trait Handler { - type Future: Future>; +pub trait Handler +where + Op: OperationShape, +{ + type Future: Future>; - fn call(&mut self, req: Input) -> Self::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`. @@ -47,109 +43,108 @@ impl ToResult for Ok { } // fn(Input) -> Output -impl Handler for F +impl Handler for F where - F: FnMut(Input) -> Fut, + Op: OperationShape, + F: Fn(Op::Input) -> Fut, Fut: Future, - Fut::Output: ToResult, + Fut::Output: ToResult, { - type Future = Map Result>; + type Future = Map Result>; - fn call(&mut self, req: Input) -> Self::Future { - (self)(req).map(ToResult::into_result) + fn call(&mut self, input: Op::Input, _exts: ()) -> Self::Future { + (self)(input).map(ToResult::into_result) } } -/// Adjoins state to a `fn(Input, State) -> Output` to create a [`Handler`]. -#[derive(Clone)] -pub struct StatefulHandler { - f: F, - state: T, -} - -// fn(Input, State) -> Output -impl Handler for StatefulHandler +// fn(Input, Arg0) -> Output +impl Handler for F where - T: Clone, - F: Fn(Input, T) -> Fut, + Op: OperationShape, + F: Fn(Op::Input, Ext0) -> Fut, Fut: Future, - Fut::Output: ToResult, + Fut::Output: ToResult, { - type Future = Map Result>; + type Future = Map Result>; - fn call(&mut self, req: Input) -> Self::Future { - let Self { f, state } = self; - f(req, state.clone()).map(ToResult::into_result) + fn call(&mut self, input: Op::Input, exts: (Ext0,)) -> Self::Future { + (self)(input, exts.0).map(ToResult::into_result) } } -/// Provides the ability to [`AdjoinState::with_state`] on closures of the form -/// `(Input, State) -> Output` converting them to a [`StatefulHandler`] and therefore causing them -/// to implement [`Handler`]. -pub trait AdjoinState { - fn with_state(self, state: T) -> StatefulHandler - where - Self: Sized, - { - StatefulHandler { f: self, state } +// fn(Input, Arg0, Arg1) -> 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) } } -impl AdjoinState for F {} - /// An extension trait for [`Handler`]. -pub trait HandlerExt: Handler { +pub trait HandlerExt: Handler +where + Op: OperationShape, +{ /// Convert the [`Handler`] into a [`Service`]. - fn into_service(self) -> IntoService + fn into_service(self) -> IntoService where Self: Sized, { IntoService { handler: self, - _error: PhantomData, - _output: PhantomData, + _operation: PhantomData, } } } -impl HandlerExt for H where - H: Handler +impl HandlerExt for H +where + Op: OperationShape, + H: Handler, { } /// A [`Service`] provided for every [`Handler`]. -pub struct IntoService { +pub struct IntoService { handler: H, - _output: PhantomData, - _error: PhantomData, + _operation: PhantomData, } -impl Clone for IntoService +impl Clone for IntoService where H: Clone, { fn clone(&self) -> Self { Self { handler: self.handler.clone(), - _output: PhantomData, - _error: PhantomData, + _operation: PhantomData, } } } -impl Service for IntoService +impl Service<(Op::Input, Exts)> for IntoService where - H: Handler, + Op: OperationShape, + H: Handler, { - type Response = Output; - type Error = OperationError; - type Future = MapErr Self::Error>; + 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, req: Input) -> Self::Future { - self.handler.call(req).map_err(OperationError::Smithy) + fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { + self.handler + .call(input, exts) + .map_err(OperationError::Smithy) } } diff --git a/runtime/src/operation/http_conversions.rs b/runtime/src/operation/http_conversions.rs new file mode 100644 index 0000000..6321c3f --- /dev/null +++ b/runtime/src/operation/http_conversions.rs @@ -0,0 +1,107 @@ +use std::{convert::Infallible, future::Ready}; + +use futures::{ + future::{MapErr, MapOk, TryJoin}, + Future, TryFutureExt, +}; +use http_body::combinators::BoxBody; +use hyper::body::Bytes; + +/// A protocol and operation aware conversion from [`http`] types to Smithy types. +pub trait FromRequest: Sized { + /// Conversion failure. + type Error: IntoResponse; + type Future: Future>; + + fn from_request(request: &mut http::Request) -> Self::Future; +} + +impl FromRequest for () { + type Error = Infallible; + type Future = Ready>; + + fn from_request(_request: &mut http::Request) -> Self::Future { + std::future::ready(Ok(())) + } +} + +impl FromRequest for (Arg0,) +where + Arg0: FromRequest, +{ + type Error = Arg0::Error; + type Future = MapOk Self>; + + fn from_request(request: &mut http::Request) -> Self::Future { + Arg0::from_request(request).map_ok(|arg0| (arg0,)) + } +} + +/// An wrapper for state stored in the [`http::Extensions`] map. +pub struct Extension(pub T); + +impl FromRequest for Extension +where + T: Sync + Send + 'static, +{ + type Error = Infallible; + type Future = Ready>; + + fn from_request(request: &mut http::Request) -> Self::Future { + std::future::ready(Ok(Extension( + request + .extensions_mut() + .remove() + .expect("request does not contain extension"), + ))) + } +} + +/// Represents one of two errors. +/// +/// Implements [`IntoResponse`] if both inner also implement it. +pub enum Either { + Left(A), + Right(B), +} + +impl IntoResponse for Either +where + A: IntoResponse, + B: IntoResponse, +{ + fn into_response(self) -> http::Response> { + match self { + Either::Left(left) => left.into_response(), + Either::Right(right) => right.into_response(), + } + } +} + +impl FromRequest for (Arg0, Arg1) +where + Arg0: FromRequest, + Arg1: FromRequest, +{ + type Error = Either; + + type Future = TryJoin< + MapErr Self::Error>, + MapErr Self::Error>, + >; + + fn from_request(_request: &mut http::Request) -> Self::Future { + todo!() + } +} + +/// A protocol and operation aware conversion from Smithy types to [`http`] types. +pub trait IntoResponse: Sized { + fn into_response(self) -> http::Response>; +} + +impl IntoResponse for Infallible { + fn into_response(self) -> http::Response> { + match self {} + } +} diff --git a/runtime/src/operation/mod.rs b/runtime/src/operation/mod.rs index 1c63e9f..b239f47 100644 --- a/runtime/src/operation/mod.rs +++ b/runtime/src/operation/mod.rs @@ -1,4 +1,6 @@ +mod flattened; mod handler; +mod http_conversions; mod shape; mod upgrade; @@ -7,7 +9,9 @@ use tower::{ Layer, }; +pub use flattened::*; pub use handler::*; +pub use http_conversions::*; pub use shape::*; pub use upgrade::*; @@ -17,15 +21,18 @@ pub struct Operation { layer: L, } +type StackedUpgradeService = + , L> as Layer>::Service; + impl Operation { /// Takes the [`Operation`], which contains the inner [`Service`](tower::Service), 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) -> , L> as Layer>::Service + pub fn upgrade(self) -> StackedUpgradeService where - UpgradeLayer: Layer, - L: Layer< as Layer>::Service>, + UpgradeLayer: Layer, + L: Layer< as Layer>::Service>, { let Self { inner, layer } = self; let layer = Stack::new(UpgradeLayer::new(), layer); @@ -33,21 +40,26 @@ impl Operation { } } -impl Operation { +impl Operation> { /// Creates an [`Operation`] from a [`Service`](tower::Service). - pub fn from_service(inner: S) -> Self { + pub fn from_service(inner: S) -> Self + where + Op: OperationShape, + S: Flattened, + { Self { - inner, + inner: inner.into_unflatten(), layer: Identity::new(), } } } -impl Operation> { +impl Operation> { /// Creates an [`Operation`] from a [`Handler`]. - pub fn from_handler(handler: H) -> Self + pub fn from_handler(handler: H) -> Self where - H: Handler, + Op: OperationShape, + H: Handler, { Self { inner: handler.into_service(), @@ -57,8 +69,7 @@ impl Operation> { } impl Operation { - /// Applies a [`Layer`] to the operation after is has been upgraded to a [`Service`] accepting - /// and returning [`http`] types. + /// 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, @@ -69,3 +80,12 @@ impl Operation { /// A marker struct indicating an [`Operation`] has not been set in a builder. pub struct OperationNotSet; + +/// The operation [`Service`] has two classes of failure modes - the failure models specified by +/// the Smithy model and failures to [`Service::poll_ready`]. +pub enum OperationError { + /// A [`Service::poll_ready`] failure occurred. + Poll(PollError), + /// An error modelled by the Smithy model occurred. + Smithy(SmithyError), +} diff --git a/runtime/src/operation/shape.rs b/runtime/src/operation/shape.rs index d1a1dc6..32b0ed1 100644 --- a/runtime/src/operation/shape.rs +++ b/runtime/src/operation/shape.rs @@ -1,40 +1,33 @@ -use tower::Service; - -use super::{Handler, HandlerExt, IntoService, Operation, OperationError}; +use super::{Flattened, Handler, IntoService, IntoUnflattened, Operation}; /// Mirrors the Smithy Operation shape. pub trait OperationShape { const NAME: &'static str; + /// The operation input. type Input; + /// The operation output. type Output; + /// The operation error. 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::Output, ::Error, H>> + fn from_handler(handler: H) -> Operation> where - H: Handler< - ::Input, - ::Output, - ::Error, - >, + H: Handler, + Self: Sized, { - Operation::from_service(handler.into_service()) + Operation::from_handler(handler) } - /// Creates a new [`Operation`] for well-formed [`Service`]s. - fn from_service(svc: S) -> Operation + /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. + fn from_service(svc: S) -> Operation> where - S: Service< - ::Input, - Response = ::Output, - Error = OperationError::Error>, - >, + S: Flattened, + Self: Sized, { Operation::from_service(svc) } diff --git a/runtime/src/operation/upgrade.rs b/runtime/src/operation/upgrade.rs index 0661e34..a1eae54 100644 --- a/runtime/src/operation/upgrade.rs +++ b/runtime/src/operation/upgrade.rs @@ -1,5 +1,6 @@ +#![allow(clippy::type_complexity)] + use std::{ - convert::Infallible, future::Future, marker::PhantomData, pin::Pin, @@ -12,82 +13,68 @@ use hyper::body::Bytes; use pin_project_lite::pin_project; use tower::{Layer, Service}; -use super::{OperationError, OperationShape}; - -/// A protocol and operation aware conversion from [`http`] types to Smithy types. -pub trait FromRequest: Sized { - /// Conversion failure. - type Error: IntoResponse; - type Future: Future>; - - fn from_request(request: http::Request) -> Self::Future; -} +use crate::operation::FromRequest; -/// A protocol and operation aware conversion from Smithy types to [`http`] types. -pub trait IntoResponse: Sized { - fn into_response(self) -> http::Response>; -} - -impl IntoResponse for Infallible { - fn into_response(self) -> http::Response> { - match self {} - } -} +use super::{IntoResponse, 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 { +pub struct UpgradeLayer { _protocol: PhantomData, _operation: PhantomData, + _exts: PhantomData, _body: PhantomData, } -impl Default for UpgradeLayer { +impl Default for UpgradeLayer { fn default() -> Self { Self { _protocol: PhantomData, _operation: PhantomData, + _exts: PhantomData, _body: PhantomData, } } } -impl UpgradeLayer { +impl UpgradeLayer { /// Creates a new [`UpgradeLayer`]. pub fn new() -> Self { Self::default() } } -impl Layer for UpgradeLayer { - type Service = Upgrade; +impl Layer for UpgradeLayer { + type Service = Upgrade; fn layer(&self, inner: S) -> Self::Service { Upgrade { _protocol: PhantomData, _operation: PhantomData, _body: PhantomData, + _exts: PhantomData, inner, } } } /// A alias allowing for quick access to [`UpgradeLayer`]s target [`Service`]. -pub type UpgradedService = as Layer>::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 { +pub struct Upgrade { _protocol: PhantomData, _operation: PhantomData, + _exts: PhantomData, _body: PhantomData, inner: S, } -impl Clone for Upgrade +impl Clone for Upgrade where S: Clone, { @@ -96,6 +83,7 @@ where _protocol: PhantomData, _operation: PhantomData, _body: PhantomData, + _exts: PhantomData, inner: self.inner.clone(), } } @@ -103,15 +91,15 @@ where pin_project! { /// The [`Service::Future`] of [`Upgrade`]. - pub struct UpgradeFuture + pub struct UpgradeFuture where Operation: OperationShape, - Operation::Input: FromRequest, - S: Service, + (Operation::Input, Exts): FromRequest, + S: Service<(Operation::Input, Exts)>, { service: S, #[pin] - inner: Inner<>::Future, S::Future> + inner: Inner<<(Operation::Input, Exts) as FromRequest>::Future, S::Future> } } @@ -130,20 +118,22 @@ pin_project! { } } -impl Future for UpgradeFuture +impl Future for UpgradeFuture where - // Op is used to specify the operation shape + // `Op` is used to specify the operation shape Op: OperationShape, - // Smithy input must be convert from a HTTP request + // Smithy input must convert from a HTTP request Op::Input: FromRequest, - // Smithy output must be convert into a HTTP response + // 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: FromRequest, + // The signature of the inner service is correct - S: Service> - + Clone, + S: Service<(Op::Input, E), Response = Op::Output, Error = OperationError>, { type Output = Result>, PollError>; @@ -178,18 +168,27 @@ where } } -impl Service> for Upgrade +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, - S: Service> + + // Must be able to convert extensions + E: FromRequest, + + // 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; + type Future = UpgradeFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx).map_err(|err| match err { @@ -198,11 +197,11 @@ where }) } - fn call(&mut self, req: http::Request) -> Self::Future { + fn call(&mut self, mut req: http::Request) -> Self::Future { UpgradeFuture { service: self.inner.clone(), inner: Inner::FromRequest { - inner: >::from_request(req), + inner: <(Op::Input, E) as FromRequest>::from_request(&mut req), }, } }