From 00e83c6adae0ab7a9fa92d4f982aa6d9e9be59c4 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Thu, 29 Sep 2022 20:52:07 +0000 Subject: [PATCH 01/23] Rust documentation improvements --- .../aws-smithy-http-server/src/plugin.rs | 25 ++++++++++++------- .../aws-smithy-http-server/src/routers/mod.rs | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/src/plugin.rs b/rust-runtime/aws-smithy-http-server/src/plugin.rs index 8ba1f5747a..b00fd3c947 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin.rs @@ -5,9 +5,11 @@ use crate::operation::Operation; -/// Provides a standard interface for applying [`Plugin`]s to a service builder. This is implemented automatically for all builders. -/// As [`Plugin`]s modify the way in which [`Operation`]s are [`upgraded`](crate::operation::Upgradable) we can use [`Pluggable`] as a foundation -/// to write extension traits for all builders. +/// Provides a standard interface for applying [`Plugin`]s to a service builder. This is implemented automatically for +/// all builders. +/// +/// As [`Plugin`]s modify the way in which [`Operation`]s are [`upgraded`](crate::operation::Upgradable) we can use +/// [`Pluggable`] as a foundation to write extension traits which are implemented for all service builders. /// /// # Example /// @@ -19,29 +21,34 @@ use crate::operation::Operation; /// self.apply(PrintPlugin) /// } /// } +/// /// impl PrintExt for Builder where Builder: Pluggable {} /// ``` pub trait Pluggable { type Output; - /// A service builder applies this `plugin`. + /// Applies a plugin to the service builder. fn apply(self, plugin: NewPlugin) -> Self::Output; } -/// Maps one [`Operation`] to another, -/// parameterised by the protocol `P` and operation shape `Op` to allow for plugin behaviour to be specialised accordingly. +/// A mapping from one [`Operation`] to another. Used to modify the behavior of +/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder, /// -/// This is passed to [`Pluggable::apply`] to modify the behaviour of the builder. -pub trait Plugin { +/// The generics `Protocol` and `Op` allow the behavior to be parameterized. +/// +/// Every service builder enjoys [`Pluggable`] and therefore can be provided with a [`Plugin`] using +/// [`Pluggable::apply`]. +pub trait Plugin { type Service; type Layer; - /// Map an [`Operation`] to another. + /// Maps an [`Operation`] to another. fn map(&self, input: Operation) -> Operation; } /// An [`Plugin`] that maps an `input` [`Operation`] to itself. pub struct IdentityPlugin; + impl Plugin for IdentityPlugin { type Service = S; type Layer = L; diff --git a/rust-runtime/aws-smithy-http-server/src/routers/mod.rs b/rust-runtime/aws-smithy-http-server/src/routers/mod.rs index abefe0ba32..19e65068af 100644 --- a/rust-runtime/aws-smithy-http-server/src/routers/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/routers/mod.rs @@ -48,7 +48,7 @@ pub trait Router { /// A [`Service`] using the a [`Router`] `R` to redirect messages to specific routes. /// -/// The `Protocol` parameter is used to determine +/// The `Protocol` parameter is used to determine the serialization of errors. pub struct RoutingService { router: R, _protocol: PhantomData, From 7d671b18324bda9a637a74666e09c8bd91923879 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Thu, 29 Sep 2022 20:53:31 +0000 Subject: [PATCH 02/23] Add "Anatomy of a Service" documentation --- design/src/docs/anatomy.md | 599 +++++++++++++++++++++ design/src/docs/imgs/router.png | Bin 0 -> 16986 bytes design/src/docs/imgs/upgradable-plugin.png | Bin 0 -> 21128 bytes design/src/docs/imgs/upgradable.png | Bin 0 -> 16694 bytes design/src/docs/imgs/upgrade-dfd.png | Bin 0 -> 12948 bytes 5 files changed, 599 insertions(+) create mode 100644 design/src/docs/anatomy.md create mode 100644 design/src/docs/imgs/router.png create mode 100644 design/src/docs/imgs/upgradable-plugin.png create mode 100644 design/src/docs/imgs/upgradable.png create mode 100644 design/src/docs/imgs/upgrade-dfd.png diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md new file mode 100644 index 0000000000..144577075d --- /dev/null +++ b/design/src/docs/anatomy.md @@ -0,0 +1,599 @@ +# The Anatomy of Smithy Rust + +This is an dissection of the various mechanisms at work inside Smithy Rust, intended for new contributors or users who are interested in the internal details. + +## Operations + +A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize an Operation as syntax for specifying a function type - constraining the domain and codomain. + + +We represent this in Rust using the `OperationShape` trait: + +```rust +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; +} +``` + +For each Smithy Operation shape, + +```smithy +/// Retrieve information about a Pokémon species. +@readonly +@http(uri: "/pokemon-species/{name}", method: "GET") +operation GetPokemonSpecies { + input: GetPokemonSpeciesInput, + output: GetPokemonSpeciesOutput, + errors: [ResourceNotFoundException], +} +``` + +the following implementation is generated + +```rust +/// Retrieve information about a Pokémon species. +pub struct GetPokemonSpecies; + +impl aws_smithy_http_server::operation::OperationShape for GetPokemonSpecies { + const NAME: &'static str = "com.aws.example#GetPokemonSpecies"; + + type Input = crate::input::GetPokemonSpeciesInput; + type Output = crate::output::GetPokemonSpeciesOutput; + type Error = crate::error::GetPokemonSpeciesError; +} +``` + +Note that `GetPokemonSpecies` marker structure is a zero-sized type (ZST), and therefore does not allocate - only existing in the type system as a way to hang operation specific data on. + +The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if it's request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if it's request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. + + +In contrast to the marker ZSTs above, the `Operation` structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely `OperationShapeExt`: + +```rust +/// 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) + } +} +``` + +Observe that there are two constructors provided: `from_handler` which takes a `H: Handler` and `from_service` which takes a `S: OperationService`. In both cases `Self` is passed as a parameter to the traits - this constrains `handler: H` and `svc: S` to the signature given by the implementation of `OperationShape` on `Self`. + +The `Handler` and `OperationService` both serve a similar purpose - they provide a common interface for converting to a the model service `S`. The `Handler` trait covers all closures taking `GetPokemonSpeciesInput` and asynchronously returning a `Result` - they are converted to a model service by a `IntoService`. The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput` - they are converted to a model service by a `Normalize` (this is a very small conversion which flattens request tuples). + + +The `from_handler` constructor is used in the following way: + +```rust +async fn get_pokemon_service(input: GetPokemonServiceInput) -> Result { + /* Handler logic */ +} + +let operation = GetPokemonService::from_handler(get_pokemon_service); +``` + +Alternatively, `from_service` constructor: + +```rust +struct Svc { + /* ... */ +} + +impl Service for Svc { + type Response = GetPokemonServiceOutput; + type Error = GetPokemonServiceError; + + /* ... */ +} + +let svc: Svc = /* ... */; +let operation = GetPokemonService::from_service(svc); +``` + +To summarize, the `S`, in `Operation`, is a **model service** constructed from a `Handler` or a `OperationService` subject to the constraints of a `OperationShape`. + +Now, what about the `L` in `Operation`? + +The `L` is a `tower::Layer`, or colloquially "middleware", that is applied to a **HTTP service**. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: + +```rust +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 typical use of this might be: + +```rust +let operation = GetPokemonSpecies::from_handler(hamdler).layer(RequestBodyLimitLayer::new(500)); +``` + +where [RequestBodyLimitLayer](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request to the `GetPokemonSpecies` operation. + +As mentioned, `L` is applied _after_ the `Operation` has been "upgraded" to a HTTP service. The procedure of upgrading a model service to a HTTP service is described in the [Upgrading a Model Service](#upgrading-a-model-service) section below. + +## Serialization and Deserialization + +A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the `FromRequest` and `IntoResponse` traits: + +```rust +/// 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: http::Request) -> Self::Future; +} + +/// A protocol aware function taking `self` to [`http::Response`]. +pub trait IntoResponse { + /// Performs a conversion into a [`http::Response`]. + fn into_response(self) -> http::Response; +} +``` + +Note that both traits are parameterized by `Protocol`. These are ZST marker structs, a few [publicly available](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) markers are: + +```rust +/// [AWS REST JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html). +pub struct AwsRestJson1; + +/// [AWS REST XML Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restxml-protocol.html). +pub struct AwsRestXml; + +/// [AWS JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-json-1_0-protocol.html). +pub struct AwsJson10; + +/// [AWS JSON 1.1 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-json-1_1-protocol.html). +pub struct AwsJson11; +``` + +## Upgrading a Model Service + +Notice that we can "upgrade" a model service to a HTTP service using `FromRequest` and `IntoResponse` described in the prior section: + +![Upgrade Data Flow Diagram](imgs/upgrade-dfd.png) + + +This formalized by the `Upgrade` HTTP service. The constraints on the `tower::Service` implementation are as follows: + +```rust +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

, + + // The signature of the inner service is correct + S: Service + Clone, +``` + +When we `GetPokemonService::from_handler` or `GetPokemonService::from_service` the `S` we noted earlier in [Operations](#operations) will meet these requirements. + +There is an associated `tower::Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. + +The upgrade procedure is finalized by the application of the `tower::Layer` `L`, referenced in `Operation`. In this way the entire upgrade procedure takes an `Operation` and returns a HTTP service. + +![Upgradable Diagram](imgs/upgradable.png) + +Note that the `S` and `L` are specified by logic written, in Rust, by the customer, whereas `Upgrade`/`UpgradeLayer` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. + +The procedure of taking a struct and transforming it into a HTTP service is formalized by the `Upgradable` trait: + +```rust +impl Upgradable for Operation +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 + Op::Error: IntoResponse

, + + // The signature of the inner service is correct + S: Service + Clone, + + // The modified Layer applies correctly to `Upgrade` + L: Layer>, + + // The signature of the output is correct + L::Service: Service, Response = http::Response>, +{ + type Service = L::Service; + + /// Takes the [`Operation`](Operation), then applies [`UpgradeLayer`] to `S`, then finally applies the `L`. + /// + /// The composition is made explicit in the method constraints and return type. + fn upgrade(self) -> Self::Service { + let Operation { inner, layer } = self; + let layer = Stack::new(UpgradeLayer::new(), layer); + layer.layer(inner) + } +} +``` + +Why do we need a trait for this? Why not simply write an `upgrade` method on `Operation`? The reason is that we might **not** want to supply an `Operation` to the service builder, instead we might want to supply something that overrides the typical upgrade procedure. + +Below we give an example of a ZST which can be provided to the builder, which also satisfies `Upgradable` and returns a `MissingFailure` `tower::Service`. This `MissingFailure` service simply returns a status code 500. + +```rust +/// A marker struct indicating an [`Operation`] has not been set in a builder. +/// +/// This _does_ implement [`Upgradable`] but produces a [`Service`] which always returns an internal failure message. +pub struct FailOnMissingOperation; + +impl Upgradable for FailOnMissingOperation +where + InternalFailureException: IntoResponse

, +{ + type Service = MissingFailure

; + + fn upgrade(self, _plugin: &Pl) -> Self::Service { + MissingFailure { _protocol: PhantomData } + } +} +``` + +We go into more detail on how the `Upgradable` trait is used in conjunction with builders in the [Builders](#builders) section below. + +## Routers + +Different protocols supported by Smithy enjoy different routing mechanisms, for example, [AWS JSON 1.0](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-json-1_0-protocol.html#protocol-behaviors) uses the `X-Amz-Target` header to select an operation, whereas [AWS REST XML](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restxml-protocol.html) uses the [HTTP label trait](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html#httplabel-trait). + +Despite their differences, all routing mechanisms satisfy a common interface. This is formalized using the `Router` trait: + +```rust +/// An interface for retrieving an inner [`Service`] given a [`http::Request`]. +pub trait Router { + type Service; + type Error; + + /// Matches a [`http::Request`] to a target [`Service`]. + fn match_route(&self, request: &http::Request) -> Result; +} +``` + +which provides the ability to lookup an inner HTTP service from a collection using a `&http::Request`. + +Types which implement the `Router` trait are converted to a HTTP service via the `RoutingService` struct: + +```rust +/// A [`Service`] using the a [`Router`] `R` to redirect messages to specific routes. +/// +/// The `Protocol` parameter is used to determine the serialization of errors. +pub struct RoutingService { + router: R, + _protocol: PhantomData, +} + +impl Service for RoutingService +where + R: Router, + R::Service: Service, + R::Error: IntoResponse

+ Error, +{ + type Response = http::Response; + type Error = Box; + + async fn call(&mut self, req: http::Request) -> Result { + match self.router.match_route(&req) { + // Successfully routed, use the routes `Service::call`. + Ok(ok) => ok.oneshot(req).await, + // Failed to route, use the `R::Error`s `IntoResponse

`. + Err(error) => { + debug!(%error, "failed to route"); + Err(Box::new(error.into_response())) + } + } + } +} +``` + +The `RouterService` is the final piece necessary to form a functioning composition - they are used to aggregate together HTTP services into a single HTTP service which can be presented to the customer. + +![RouterService](imgs/router.png) + +## Builders + +The service builder is the primary public API, it can be generated for every [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html). At a high-level, the purpose of a service builder is to take a function for each Smithy Operation, whose signature is constrained by the Smithy model, and produce a single HTTP service. + +```rust +/// The service builder for [`PokemonService`]. +/// +/// Constructed via [`PokemonService::builder`]. +pub struct PokemonServiceBuilder< + Op1, + Op2, + Op3, + Op4, + Op5, + Op6, +> { + capture_pokemon_operation: Op1, + empty_operation: Op2, + get_pokemon_species: Op3, + get_server_statistics: Op4, + get_storage: Op5, + health_check_operation: Op6, +} +``` + +The builder has one generic variable for each [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) in the [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service). Each setter switches the type of the `Op{N}` type parameter: + +```rust + /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. + /// + /// This should be an [`Operation`](aws_smithy_http_server::operation::Operation) created from + /// [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) using either + /// [`OperationShape::from_handler`](aws_smithy_http_server::operation::OperationShapeExt::from_handler) or + /// [`OperationShape::from_service`](aws_smithy_http_server::operation::OperationShapeExt::from_service). + pub fn get_pokemon_species_operation( + self, + value: NewOp, + ) -> PokemonServiceBuilder< + Op1, + Op2, + NewOp, + Op4, + Op5, + Op6, + > { + PokemonServiceBuilder { + capture_pokemon_operation: self.capture_pokemon_operation, + empty_operation: self.empty_operation, + get_pokemon_species: value, + get_server_statistics: self.get_server_statistics, + get_storage: self.get_storage, + health_check_operation: self.health_check_operation, + } + } + + /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. + /// + /// This should be a closure satisfying the [`Handler`](aws_smithy_http_server::operation::Handler) trait. + /// See the [operation module documentation](aws_smithy_http_server::operation) for more information. + pub fn get_pokemon_species( + self, + value: H, + ) -> PokemonServiceBuilder< + Op1, + Op2, + Operation>, + Op4, + Op5, + Op6, + > + where + H: Handler, + { + self.get_pokemon_species_operation(GetPokemonSpecies::from_handler(value)) + } +``` + +To finalize the build and construct the complete service, `PokemonService`, each builder has a `build` method whose constraints list out all the requirements for composition: + +```rust + /// Constructs a [`PokemonService`] from the arguments provided to the builder. + pub fn build(self) -> PokemonService + where + Op1: Upgradable, + Op1::Service: tower::Service, + + Op2: Upgradable, + Op2::Service: tower::Service, + + Op3: Upgradable, + Op3::Service: tower::Service, + + Op4: Upgradable, + Op4::Service: tower::Service, + + Op5: Upgradable, + Op5::Service: tower::Service, + + Op6: Upgradable, + Op6::Service: tower::Service, +``` + +Notice the general form: `Op{N}` must upgrade to a HTTP service. + +We provide two builder constructors: + +```rust + /// Constructs a builder for [`PokemonService`]. + pub fn builder() -> PokemonServiceBuilder< + MissingOperation, + MissingOperation, + MissingOperation, + MissingOperation, + MissingOperation, + MissingOperation, + > { + PokemonServiceBuilder { + check_health: MissingOperation, + do_nothing: MissingOperation, + get_pokemon_species: MissingOperation, + get_server_statistics: MissingOperation, + capture_pokemon: MissingOperation, + get_storage: MissingOperation, + } + } + + /// Constructs an unchecked builder for [`PokemonService`]. + /// + /// This will not enforce that all operations are set, however if an unset operation is used at runtime + /// it will return status code 500 and log an error. + pub fn unchecked_builder() -> PokemonServiceBuilder< + FailOnMissingOperation, + FailOnMissingOperation, + FailOnMissingOperation, + FailOnMissingOperation, + FailOnMissingOperation, + FailOnMissingOperation, + > { + PokemonServiceBuilder { + check_health: FailOnMissingOperation, + do_nothing: FailOnMissingOperation, + get_pokemon_species: FailOnMissingOperation, + get_server_statistics: FailOnMissingOperation, + capture_pokemon: FailOnMissingOperation, + get_storage: FailOnMissingOperation, + } + } +``` + +The `builder` constructor provides a `PokemonServiceBuilder` where `build` cannot be called until all operations are set because `MissingOperation` purposefully doesn't implement `Upgradable`. In contrast, the `unchecked_builder` which sets all `Op{N}` to `FailOnMissingOperation` can be immediately built, however any unset operations are upgraded into a service which always returns status code 500, as noted in [Upgrading a Model Service](#upgrading-a-model-service). + +After all `Op{N}` are upgraded in `build` they are collected into their protocol specific `Router` implementation and then bundled up into a `RoutingService`. The `RoutingService` is then wrapped in a `PokemonService` newtype and presented to the user. + +```rust +/// The Pokémon Service allows you to retrieve information about Pokémon species. +#[derive(Clone)] +pub struct PokemonService { + router: RoutingService, AwsRestJson1>, +} +``` + +## Plugins + +Smithy Rust also provides a way to hook into the upgrade procedure in order to modify the service behavior. This is done via the `Plugin` trait: + +```rust +/// A mapping from one [`Operation`] to another. Used to modify the behavior of +/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder, +/// +/// The generics `Protocol` and `Op` allow the behavior to be parameterized. +/// +/// Every service builder enjoys [`Pluggable`] and therefore can be provided with a [`Plugin`] using +/// [`Pluggable::apply`]. +pub trait Plugin { + type Service; + type Layer; + + /// Maps an [`Operation`] to another. + fn map(&self, input: Operation) -> Operation; +} +``` + +The `Upgradable::upgrade` method on `Operation`, previously presented in [Upgrading a Model Service](#upgrading-a-model-service), is more accurately: + +```rust + /// Takes the [`Operation`](Operation), applies [`Plugin`], then applies [`UpgradeLayer`] to + /// the modified `S`, then finally applies the modified `L`. + /// + /// The composition is made explicit in the method constraints and return type. + fn upgrade(self, plugin: &Pl) -> Self::Service { + let Operation { inner, layer } = plugin.map(self); + let layer = Stack::new(UpgradeLayer::new(), layer); + layer.layer(inner) + } +``` + +![Upgradable Diagram](imgs/upgradable-plugin.png) + +An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](TODO). + +All builders implement the `Pluggable` trait, which allows them to apply plugins to service builders: + +```rust +/// Provides a standard interface for applying [`Plugin`]s to a service builder. This is implemented automatically for +/// all builders. +/// +/// As [`Plugin`]s modify the way in which [`Operation`]s are [`upgraded`](crate::operation::Upgradable) we can use +/// [`Pluggable`] as a foundation to write extension traits which are implemented for all service builders. +/// +/// # Example +/// +/// ``` +/// # struct PrintPlugin; +/// # use aws_smithy_http_server::plugin::Pluggable; +/// trait PrintExt: Pluggable { +/// fn print(self) -> Self::Output where Self: Sized { +/// self.apply(PrintPlugin) +/// } +/// } +/// +/// impl PrintExt for Builder where Builder: Pluggable {} +/// ``` +pub trait Pluggable { + type Output; + + /// Applies a plugin to the service builder. + fn apply(self, plugin: NewPlugin) -> Self::Output; +} +``` + +As seen in the `Pluggable` documentation, third-parties can use extension traits over `Pluggable` to extend the API of builders. + +## Accessing Unmodelled Data + +An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the `FromParts` trait: + +```rust +use http::request::Parts; + +/// 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; +} +``` + +This differs from `FromRequest` trait, introduced in [Serialization and Deserialization](#serialization-and-deserialization), as it's synchronous and has non-consuming access to [`Parts`](https://docs.rs/http/0.2.8/http/request/struct.Parts.html), rather than the entire [Request](https://docs.rs/http/0.2.8/http/request/struct.Request.html). + +```rust +pub struct Parts { + pub method: Method, + pub uri: Uri, + pub version: Version, + pub headers: HeaderMap, + pub extensions: Extensions, + /* private fields */ +} +``` + +This is commonly used to access types stored within [`Extensions`](https://docs.rs/http/0.2.8/http/struct.Extensions.html) which have been inserted by a middleware. diff --git a/design/src/docs/imgs/router.png b/design/src/docs/imgs/router.png new file mode 100644 index 0000000000000000000000000000000000000000..fdda140abf6e772814140a155a0d9b746ff85cde GIT binary patch literal 16986 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 jxFX^onX6IVMTh literal 0 HcmV?d00001 diff --git a/design/src/docs/imgs/upgradable-plugin.png b/design/src/docs/imgs/upgradable-plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..ecadb38a355de133339a33c0d06bc9094015edbb GIT binary patch literal 21128 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 literal 0 HcmV?d00001 diff --git a/design/src/docs/imgs/upgradable.png b/design/src/docs/imgs/upgradable.png new file mode 100644 index 0000000000000000000000000000000000000000..1777574de4ea8ec252a540dae7569b5a98ada09f GIT binary patch literal 16694 zcmY+s2Rzl^A3u&HBf3_yUAbn2dyOmenitpJ+56h#njtHD?~&OhBqLH(vXWU`Arys- zRAyGj|M-4>|Mhs}j`uz9^M0S#`*mL9`Fg%e)YDO?revWcA|j$jYp56y5fP7q&(F!p zz^j}x{xT8KWlfx_8O}4<8SCLh1cNL8_X#F0>gw%}gTYl`;^K~ec0pKg5eIKS9}zoG zyHIa0@Yxgm07iLwJ7S%&PLBVL6c-g27Zybdi%T1cB4KbPaRhh~l@>uth*|tM-p<9z z>;FuP@eaj$c-X`4*qY{f430mSHG9qm0xr*75ys3@*gS>Ax}de%?X;PJS1M z;i^Dzj#xVvKRX~6QSASbhLp0Hj|tktK?)}+sUG4KsN(Oe``_?j6MJoYoPUt2UXY!J zzMi+agO?A+%+3($qm0#e6vtYK`8XJg>iZbmsd$@6Ny5ZYhT`rX7w=|vD19SiH3>Zx zxS_EGCd9}Cr5osvRPzi)2a5aZs=8@7g4KFyDqA2N;oy^lsk65Mcvo}tG7Hf`>)Byd z3|u_)^ihsjv`UDIzNVRepsuDNQeR8gSVv3S+eJ!4%K_o4qavI z2trp}HP~NT5{~j$MQHfAikmx#VzfQn&DH$1F_J$1+Dbm2%E1~&o>*lAl(vzfU9g0> zx{-&mF)l<@(m_ehDMZ&>Nn9z=1L=;{)>Jie*0S(1FxS^L(o{hP_(*$VgVmKnynKTL z#Z6uP44fsELNI#17_^1Hz77y7(m4bT?#kau*ZtyM)yF_o$5cYuz{x^O9O-T1Y>!e= z6NT$*AdxCae^D1v1YB7MsjVt4r4%9=WbY`ct_K#SW?%wDtK)-3>-c&pJF1!*OQLZW zt|%PFKof@v(baZ9Du+tx=oyLPOkFLc{R|yKT_iO$HA4OLRdn3clq5{OB|IDh158vb z)I%N31Kq`?w7ii47g~UG#ER%1G$6oTDpXSni?Ik$ z3i0%FaddT)(gvqk=pci%mCOvq>`c)Ch){hk=Rn;MIL5%iCCJMhC4mYN3(~h!f;$2M z-~zySJt+r$Uw^O)Q(ql-2~{a^3HKnRfm1MC8He(ha53{X^Y;xxNh8trPGVXp6$dvr zl!LjBkGncHNM8z^CatEYDjFo^Z>X!G>gR9dAgvvsrlgDX!Ff5FAoY}-)RY2{>S#Mn za4AzQw-9~%KwN-iNN}JYIM>x&4JUzg5;YG{^3=l`csLlr4b6f>Tuk(^er|@L2qQ0@ zKv55K3qxl$gHSO;gtwHkq@T07gP$fmL_AnR0;g{$aiOLv0X`~$YIcTprhZQ5I*v%^ zKzkQE<3JrrAMhi%u!)qfCl+g`IV(G8Xv3wIz*Yy}{_oxXzpW3x{~xmnS7lbuDkdW0B0{Su83kMaE}?j>GkN?z zA6Z4rd~V>HgD2$HDj7h(COh*W3dQM*9MgDNAo^eXpLA4TrG;Ygu~)4O(Yq=YhyUcu zUVRK_UzJ;x+q8Cnd++UPh zi}IR$Vk&jC^JAE+w_YRj{&1QR)U4mE+5Q1UtZeh`xqOV`oAx@Br-Hk+ed>y6!vz{u zMOr;l64bW|T?GlM>$e6D5uUR~&ifj}|IAxm^;d;vpSzoqMTI(!;^8hI5Y#M)fkUGw zCZk6dm58V;=bX~K24gS*_k*sEfDLi-ou@D6#eWRt-bu(9gY@#cRyFr^I=rXxA6I2^ zs{e=2)}frsx@47}n-|+Xki+4D8_0dTf40jBcN${Y9f5x=0P+}qgDO`l$T1Ax9BITe$2~50*CS6KrIkN)s$8DRDs_FwW2)ZVVxcq3*+Rkk4HAgTC#q}fd+%t^11i;5 z(fq|y-hb?iG*XcPKaHRtDziP= zH4cx^gFl{RG;FSE(u)bQsb~`^T`t-ia(iRS(c_WCm$aiuE)&T3V56-eIiWX!guanP znbAcOaYf^}iJ1z-c;l}QWXGt`#!d~PG2iM1V&WO*rU!TSH4u!CBXSC}ijKr#4mr3T@Jn4VaJ@p$Y5EH5P zk$0BKsJ>$GSf^xUWwiLm7AkeVDJjd@tSqHL3uw?o=(o#bBc*b44eXYy^Xeq*8>3eFa zFIEwPfy62j2=yhZ-0N#c^}T~X)lS2C4b@}5mr&nwO|8Z7SM(#fb1U(MntK6W7s?(- zRLfIvd!k!={VG0z`ec93yZEkG`@4FpSCURbrUY%~J(=c<2^DRp8?36e3&ow^B{9ZD z-pJiUecS!0AobjBGK&zUn#4eHvXXBQPg0kv9=Q2ALI_cbWPRa znQwfO$^74%zRh{RmB_b=Xg~TEbIlS{u@uJ;)$&1Rc%)2WA%|a|JA+XWOGeV7QwhY> z08#A4uU64j3&r*-i1Z;NP7W0jI6?)r|-^vZpll;3^`t(mMk; zNB4ivO-O3yjh5|EZtjAcETm?YBo}Zz`&4gkI5J_Ib>vS#^P$;i4Rw&PrQRTdKF~xy ztlQ~Qx`NCrIT*%e>}S`7Hc=8&GL00%`8;<-(V6B=wo&G9LZ36(#PdT3%_^UmJS`G1 zYqHJj(+H(r_?V=3ezL3E;x<_qLw zNXRvdqU3c-w87d=xD~OluW$r&5ppBOV);3)>F*qUkLQei$s*;zuYCO~u4jmioC??9Exv zPs>&L*5bqYx23MhAC^y631KZB7+x{_pAwDDC+C^!Z?%NVmfSu)6nqKy1%FZF75@E#V*^jm{%t1f{;qq{APx-GOD$It_F~(cLsR1n z9f7}RoxgQO@m{U|@k{X=QA};&fCFnK9(zfZUHN}D$!7v1RY)cBvTH0sx*pZ>J_D*e za{PDWfkwld{n;jw$=Qz5`)46Yt_zRv_tB*+yzP0j=e5|W^!xrju@s&0CWJle3Yv=$ zqyf^!lz@gz*xq>1nRYOLuhEEyMbtL(=-?k`gD?K+$fM=#k`MX6 zzFnBS_QfQjk$`Ed{9h2ilC0|6hkN!H2H^qGI;4Q;h1b`gZj?4l`@d!k2@?Hj>_TT# zuSL8&kE+){)n;R->I?Ct5A-#bjt-a7F~F;5TLV=fjIBSoP-8k`64%dR=%3fJGlqP2 zN2QYEW*(`hiwX-fxHC(*<+zp>ptjfJ&?XI*0@cgw6OZ}c+=7kbD<-He>;WT*t2fU= zIy1a~lA)a=pWJ#Ni<=07V(@4351&9=D`vYkRo1hcJrIc80LDtnr8K0v{de13R>vJM zhwVi2f2Q^R$#%Se_3M{Mrw0pGn9L2|_o=Kdz{FBxm8z>h_kHi>93>{V;l>KuWuhKX z4*pX$@A(3Jh8OL`rVvmXL(%~(k7~K`6BC_PG|q?HPC(#bIa`-EG%?~fJ&9`xuIgx2 z;hdS$gIBlomVmad!YQs{`Vqd+ePEB|KOwR*(-u%MiQptudzKo^Thuha$^lEUQsmx% zK9o7NpCZqwB)kgpu1#TxBfpz8nSMj8M{ihK3Rq^=*1cC3Qe+^WHM1kDN=6I*NaQPp z+!tgGD-?G!XwPXaDn@OysU*mD{>Zy)5bBMm(!ZY3tM8?PhD*7w2VDq}ndIT|`k7eR zp^u9{kGK!mfuo(_|3-klDIJ);dCqPT4_lhs^VI z;+B=hw`bzO9ZP(aJ9x-|Rseef1A?)u4hD(wEKROYT6^6H_{8;vw{K5MddUOsy~#-j zDo_Z5b7dAidS>nIfCM&7c8!5XY|k&1+lILL=j+#n^Pgwt7YkO@oE&LcLkQvA6xdl8@?T$HO|$v-(K>%DmHV(V;AKmm%~1k^WE#ga6|_It-Yc(nKfwPT zZ02j&?@-tTf4>a9(}?4oT^(2FEKw@jOWoU{f$E*n5Q9L{LM8+FW1BObj`uGXB7|~N zYkG&ym5N+*zbUFiV#z*{x?HXzw&#{xN`d2(^aOeCpPf{WItYH$$w3%y<8V4)`#?x* z7KV6Ly`0BLm`8y-giuesl4^Z_|NP&A^I#T?`GwY=Afub8Awi14UU>=iPi^e%=X9et zN5r8WR9w4}+=4cC4O>)TF@BYH$5O1D4rf$>x-9k%Rg^=*hV*kJqeamb*g!|0G#5v#}&G2yj2g<|1O$Y*v9Aii(BU_Rhb#_u_CRnjt!ZOLSU( zWaI$h(0^4xAU^K*5#=C#y~XponP!K0*Do2o%VbfN*h&lvSD*hWA&vdcP!lQUbgTCo zGakJs)~S?`;Z7mgV}q8s+28dx(RS$X%EIfAmM|s){<=`OmYn}B+EJVPr_YhA5B;OV z5OfTrQMBf)_$`iTdM7q;ODfi@2LHRIt_3x_O}pMW*k1Tu8}0!z&az?tnPB&?&zzDO zZpRmFeMe~@LdTod1{2lDvGXCTC8*%rorjh6x!>3;?erSX?lze#Sd1e z7cwrmBwrWi>7qF239$XBa2_T$yx8kL-B2uR z((UUDtXQ5smej|L6{+@ds{Zz!z~%8i;^C4v?2m79xMXw4!FyLbm-Oln28N~M8Q+&t zujOo>ZXDg<{9Uqmoz%h!R);0qX24yf+|uO42o?gLXf7G_kh(Ctt57sqy$E8lLMc(; zCdL`KF@@pQMl(T&_V*@quk8vSS+g$7`LAe?*Tq1zd(0F?V}!6|Q7=DK04p-Xttihn zdyQgG2@N(Qj0yhh-L%+DB}LHY{8t7M%R{c&iUI%6&E zX(fsPU*ya3ahL0ph?r1HnRH-!$w>~ExkMWmQ{Bxp-f^v|&)sWRoBeu7dgr?fp2YwX zjl5C$q0Toj51%MkTnFLlB(TKqXxpuMGm)C<7cKQr-OdT)jV5yYR0I8RI`Vje|7&`K zgf|z&vE%(q-z7)R2{OX?{TEzK09J4zz0>~s(Jz8E4HU!uggl6sDIuF#0xITN6H8CL zQ&WSFVlR!Mv0MLCVWdymlR(sc8%I0SWEZphBXc96aAH^&CtvY^A|V2H*#Bh6!ET_H zg(Q3LbF;&x#5LIcTuXg}SoT?<&=mYTKU%XM{=0v+d7ZI9S?AWXqXnm5Q-3$5J37UF%a=t#nfnJ{d>eSlgVqMxOjuy;Jc`z?bY09erqD%%6IgcBXWi= zd7pEFt+d30R4D{Ydiz_-ljuq;29yJYN0ec(9do(Cpz>ux#E@Xfq=4mZ)cb2R#68s% zo7XxTTzrCR;Hnf+<(1C0A2n~qO;ht|-f#k13FArlm|eM1wiUbO5zAG8o|Fmq)zKF>LWcQX7??(s5PiLPr<++WBj z!g`uc1KuX2$k%WyvvAVC1=}z|*&H|9^eSLj?_OAiJ5F+@*8Q-mM-B(6cSLw2T zA6Zp@LF4w-In{CrK*=(KA z$rjjQ_qnzCI5J8zlw0v1S9s_8k;fyP=_fSIo8a`Ga^#uReDEVzOMOO-?1bqNR5|Zx zVQqJk-u>&@P!@?vKQ7Iam#4>nhh3kmh0p${B8h_5I6J68k)(YC-hcH2s=)f}Ugsrx zTEz`Fd3LFH z_s27{Qn0o)z(h)^88^sNdg3=w*5QARkp<lv+GEhe%+6U-z7&+mJFQw5ECu^cxbc&RiOd8TKvITJ?K05|b`Sc=2n8 zh_U!mJ)U`enrHQ$5OyUcGon|RqyU3X9GWWrr(LnPyddYtVdnp5Cu>EdBoD8=D)r5>~VF%F{zC0GNe3~8&l^%RX^8} z2v7qJENT*D%`61p9E9?*?ash6OKgnNzR}g*M-~8FW_a~DnQB(zxzl!wo#e7%i!+@XvZ42v3^)V4;JrWT@nUnA!aU&bxcVLlKX5*8^v z6FaKqP9jvmG7_G0hDHd6*Lq<-kSfSxo^EuK7760c}R|VDK z{;#StdNY3S@JlMVed4bzTFx|soTiGchVo$I3qT;~()u<@ytH9qQo^ed>c>`bwdZ`D_VNWW+uv1< z`I|mK`&N`}QKS+{+pT~k(4DKmq4@shn*-8v0N)zQ^!c*{ zKvNX_mNk_5K!cM3C)*0 z;k$uj@Oy{fJGm9g@tVur#O(TvB+7xC^TJj) zL-&6b?s+~8+Zl9T87aIXp{5hsbZ#9C${mSFdSOXT6zQ$Eo3H2eAqFPF`~0xRH~SV0 zc@LY_a#;7BblaPogM%LoSlZwzl*Uybjy?t1=ar{aucU8ZF<#{i+fo`w4NBPizdqR+ z9(|N9?>6wb`?l|U_MifQH72o_uvVGC_RpZoaRnC7-I$Yp5VEhSAFYi)dceR6GHRAT zrE>SQd*i58-FrVpO}(#v7x!LS{St?i*Tb-D)H!0%EP&A3Anc~p4*X}SoI0;;#%&>e z(l5ZD`c!g6gfFf3t@@Z|Iti`!H!<9^qKQpu03n#~_s_fcjs;?ps9wbO(zTvdm9Wfy z7XXpCd+BxBrLr@)-?PRM@xk{+wP4Dn){MD+KZeX>dfpXGUC)1~Xx5z&{jNUj9i!>X zheLy(5pC7KS^il=h&3g>dQ@S8`z|{+;h8Hy?cLp`8-!ucT!-m|9(HX^H{NzO(0TB1 z9$DyM5@mb%VEK}OUtQI+_a{fY4MtfR(M zBrCQ>GD(Qj5 zSVCNa+wlo^Q@@IY_O-HL{NOA!f(D|}QZXuirE|g&#;%baWAIv;2|skN?>p57S-fzk zuHn22jn${@J{b}LTjB3bJ#T$KFMaz9a^OLz2iTrFR1+_}Z9*DDm|n(VVlhb?f1zx(fc`tYk+-!w5ODKA#tcG44 ziQNl*2x>FGkot@SV;`mFpq6wsC68&@{dqYX5M*5DB1ZUgjNjROee+Bnp*T|bIp5`D zsjykg_wrL(XAF$$Ed9gWi9r_mDM06n0T@gLt@!c%JVuWz8<;>UENwtP83povk&8ok z(fxmO3w@3wD{<{PTNqbHT)G#rKUwg%zH>q}XQ$K{vbAp~G_weP`AdK70k-WSK*8G*% zBX!-4ZJ%_Wv2`CuOH<9pFtghuBGR$3CI62bqmfKa^-7GFU}i`E-6+i_c9eQb{gR?A zbuMOY*1b`V^qzK(Yi4Y%@<76>P1FciJ0+uFf=LED78QG#`})ky^;tdeqa~;!4(Pq* z=%WJVt~SrPWf|@!N+ywq&GI8JUCZZdnT1KX`x}$j3sUdU4kjYrh>e9oU6ZcGz~4pt z3nc=G@%{6^rz#^Y8O|p0BBiuPp{~lZd?U6QMz^rMerr8R-QWA}DIg1=8N8dWOZ}-o zwz}bQqQN4r`5C-dgy!KidJyR=9AU>mSS7*ns9ThF`DU2x1*U=QOZ-L7zcy6^5A5zv z?D_qW&WJT{(#ymsDmr#hY)VYVO1s9Hf4ffemd(vGff1Aa40B>&EB}DU;d0p$@M=GD z2CLtfL2tyCT_U>*u}9IzhRI@bN6Ew;cN1Y4ws8;VhX{9&@f8WKwEoh_HdF$E2nLkg z9IXcmQqxB>FkF(Mxfogk{@d-_GQ6+`_l3?kt{vpOmLoldH~qy}9msSLYXY={<=CV) z3X_y{G_ugOyD6JD9-b+v`PFnX%n<>CTecKwpIsfIPL}HgCTeGWMsAY?zV~x*H+=qyW zwfKSy&|~Z1u+_g6R#ea+_v{y_oi{vg`hCAouJ>P$l0-d;oxSh3uV~Znr%s$SyZ2J= zLbND0Q>dF4Cmf-r=GRD6ChT{+a>xYibzlm~m~?vX2u&BYuG4_e8|7tgoE{AQi9d$hkaSl!tLDBaM*Qr&D!YRLHYG3{}2sI`v9wg$Z? zF?_pHH96LCJBTjy<8ah9h6VmJ!#{~Rq$j1W+#WdvkJ|f_vyuBZ12x#;NC3@8hIx~f+*z`5t_;M8awBHR(fGdoysB9*hi}5j{t~# zDn}(W-ZD>7+!b$j|3)!8Pf}ZA)B1M>#|b4-iN$!wm|iIc_e#}X#V4qk*c92_Tem*m zri!>pDx6UcMd&14|0nTw4U(o_t=$dfYOiA3XNzr(6J(l#frT> za)ceVyJ+0^wNs@}p?@E0z<1AEkV;z~^BylHC8zi^obyGj`o2(jWiUi2kL0Vc?s)ax z!TCnV)&yS>?cTa7-Rh`XGy}wh90`A&{F0cwb7#j}?7;6v@ARk)g-FbeTAJvPyaQrt zW(EFgTLa_Qid;0F+cXbea`xQ%5`XY-!=@mr8^7Oc5vJ4~_#RQ$huH4AO3=Q#~Lsz(^ZYQs^%*xP1mY#Jg%mY88cE;P^ zqC-5;3>x&Z|<+YW1e}3O=May50EIOR3g-2JB@Cdy?HM4T+0EpHIedqEMLDhqlc-l=$_p@U+04 zCu5QhmoSU$9iqeb-CaQgQI}e7ToFH6H8uy-j^{T)E=63uoN6$8;&IiEm2xRu4` z>XGnwwq-^*iC&s7sF5o&m32&vPMJ#+&%Buiu03`p=AUKJht0R;2>Wo@Y1|VP`CEh! zPxO7Z+mz)xMa!Txe~M_-V%Bg&l8K)C_6oFsVL|E-3u~p7jz{^8C^145qU8>bcIqQ< z68idEN%_2AZvATCGOH%(e2pPmwofRY|8})}Yr6!$`By}+YWrl~J8=5mJGY+2g}q9v zM068$R(FCdiT1`#b`3a*=my;qm!dTvY;Q5QW<=PhZCBp^&i$3mjm_zENcWC%7{#AV z(S+YW^)q-ypf>NQdx%mRp!Z1k@+}rJjJOpMcUHtSU1uS4jBzLRgKgJ;y9nF0=M~Tx zQ1xa#a@!5>#;6Px0=~n%r22*)0Y_R-Qw$+#dxeQro#^AyVlm9QGsCk-lzm871w)^6 zcXH+00|&wV0#FK$j4l=2MZ<|Q0+L>mfBqv$d%cX8n0MJAzBUSHX04n=nc=27WFq*k zO?Tqn0l??~KjS@Uxu|1KJT#%lxuPl`4YSCJdhCtmX=RO!2O3pd0` zvKF3p4J@aIub?sn+%4&Ao#H4W40}z#<0BjQIw}f1-2zIzqWr>Ls`++*7XiMX0fj?H zl&=<=%r8OyjAE$ZUz+dNuk%Ub!P+j5q_plb6x? zd-4l0yNA)Ad;w#I6Za-MflED9+HA77X}Z-9rl+Mr#SrM@N~^k zXp_f`xWL2uzhV;`?P`C$H1kC~YpM?jMtx zY{LIe8jy1j*h9d)*)=om{;S8)VE}C)Y_VPL0|+14yzYBnR=f62lgCVV)yn}_hM-@f zoSDR(;YLb6;5uIUz>fa!O%*XhY|(tJQsSt_Ncj|7UWfthwrt3bf>eV_Z$Zc|s za>#`qGTSs#(~_9cNMHRLIOhqq$Q6OzzS#tjg$X>GzQyaSjE!g*sJRmYDXGl${CcUS z3P9zz2wi0SGw&8Q zctJCV|NDldkZHp$37n8|9ZnRx^P<7(RZ=6dO_XLUZ5P`Pz&_D$6#)FIO>P-D;}oa% zuDMuGeB6{T|HXV(#ChW5!Rvj910{it`Jh#GS9X6i>CgBCs5S3T@db^)B%b6Dc zR*LULVAlGHiEs;G5kBm6`*A+*CT6MtXnCFe$xo$N?~Ezo2k%2XXIflz{Q)y^IPn38 zy;5A@zm8$qU@?8G*IYUK+)OlvuZkRZza>L5?^b_40z@qj5W7;@2=QJf@?QWY#h>mE zRc`lBcH1GeoplkIflOEXk!OE@PSWp+HD9&MD))EyWc=G`)?8`cfvfegZ3CA)12ybt z#7nL3OSSN`fOput$A%NW0+H4$K%FRip!RO{ixY zBcJkHa9dEse(zed@bKtsn;z&U9(`fk9bLKBG@k7ocldX8#?n2bh}R0EcX)EWVnEPs zs#gU)u`*ZTAOMJkY2+t>`4YF*4=6J~4!{2Dj1-FI$_6aHV-~*51^PZ+{&+yUSaQ^;)7R7+4TU$#t#*l8nM`yNO(A#K6bp`&+46{cq1Rxh1r~cGg|0Uy6#tG znR*H&&$^?|bjyqjIm5;+(!NX=b2R6zsoQWpt2ZAS$Zc-5iY1^g_uOP0OwZ8L&fM=$ zWfj666B{%dgUb0U@{E>2viEUi^`wld$tK!&;U_hA^xSI#dy013y)<`jc?{E^FFr*2 zKg_O9tuZGVmgdrzxm{XQ&D>$C&4edkrY2QOV-w(2<;I?BK$K)I*gF+cPZunzBmTha zrVT_NYh-fAy;e817b+^kX?3hQnzHMnCngO)`2z9-mpbV)s+R-Xi{}w`U6I6DZc&9( zxvu%y%bC=b!xYsKJCj$aS#tJJ^wfHQF~MU0=0Yqd++Dot9i+L@S&Vlt(?2UNW1y+? zJi<6>>~lN{V?RY8KkFTj<@O1wWE3BnEzzp8LEk8}(8ET*l-Dg>Ecpq!VnM+le)&J@ zaYO{3KLoQ?+!A;CGLsG63l*e@{;dFG0#!TZ|xwz8*7CaSwbEhb`@5A3&Nj%LX^$ zjyph!#h(p@hN~y+QT<0T${Ij_dJIqJ#LmBN=h;{G5^)4M~b+p(;(6r&< zVWoIDy}{!ssk}55RWw{5zjyCKHf+SK64nAi@#(Fmjn-4s?m~(4$2}a(7I@nm2mfDW1WuoKWX=B5$v+5A&@UeV{e z-xWPKAlU*0re-Ik}hed8Gr^5WPTxMs__n%;OyApvVG67f5MnK_r)r}1{YS~?7 zbHOE^CfA#Z@BisJ+lmeqysMvUF3gp%mxuS3I6Le;KK)#G z`)&sg+iaU7Xe_ucA&BLrF0rlw5^A933MuKRRB7^+G!qX^V$E+4_eE^gfxZ?nXLbO@nn%rK-4Cv|z+3LRHNy9qS zJP=j24JTHVd=NuBS`NI$H}}tgcRD^M;Qs! zpH2SB(RRr*^Eq;vZU6fsjaA+P@k|Qc=H^dt&v5@Nd>`$DWEchHWtpqMZ$T%CFLs{< z2_UJ~868I*o(wd<=Md7*1?7N;7fXyDEuoab#^698-+hW2kG{9fj<3OVq zfN(>7CrNs^#MxS6g37rS5Oqp+8d>}M6P?6IwcnV@zZY!_C~`fH^fbmp-c!nTe((${ z);~8tTBx9DFO{_S_;rXJPv{YG-FXxGmoB?DSyN%z&i?vAu?b1qMOO@-rzBv?{DYk_ zW&+tK6LrBFS0l190VR^B5I$AP3+~&B#T<%;@2d<)_@2IUeohnRS4}SM)}@VxS09eW zDY$#`f4%S_rg}v60ibi*p#ELo2!BGHi?|eJ&Udl0Y4UPk(BDG{vh$uw9NYr6)eL*e zh)}8iS1yQ|vWR1Jky*=16FD^=TWtKy5zY7ejp$<=+_q>Dd*$F^CSXwylPTEukw0N{dMyzSBevn$r_GgXT&9`y{4Aq8y&HS;VdTHQ@)+9^NI^*GYMpsfP zS`x^1%(VeRFkM;M{DIJN>?B7u!W zE_6@SV^LB&=F15nw>^M>z=e-@l#6uK4YX)E|DJu0iUv(t+4JQb=u-a=$eo~cm7_Z( z-8;2_9Z0X>WO5)6=(u81%6V-&24b1{LZ&*=Ee67%x*tIz^Y(%et4_4=wJYipjQm|* z+$5+Z?f!hy$U=Zy7J8Iia`24?K2WR7z|Sh4n$_9K5fDOCR3Fn7|9*3FO9w;`q5q2> zRLF=e0-K2+K?B#ux`#T%9?gT6A$v+#D!2CfO%pHCC?)0d>D;>jyo^YE7=8RGk~Yg}gd;{i^5Jz-tCcJ=ymtyg*rvQrHMo$HNgF7gm^@ zgjhQgLR|bj)v{;Az1m#zGUo0q~|5=&5-(zz!( z*TcqU9ZZAYi?sqhK4-DG@c-f|Kd$jo{t|T&u8@p2C(sAOJUhF$KB0Q)ZxWLpPIeMF z#4^X_2XtW*_2%&P+mz;@Y3kGQ4tZ(xE!{i65#0|FMf?d_&J3kl4f{BQS4e3X$-W&!1}hddW5|_&bSw^-UQUC&<(1 zUAfb>_I~*b6@k}gl@4;CduN0{pGhml zTD82MJa^5cRjpA5qF_~Ux8+iNg1!8wKbH;jE$TPo@%VKXJNEyXqg0~hcX7J8tdutt z%RvKC;-1{)=HP2DKn0%Cc|E!~%NgRG{y+L-rn)U?TQle)B7x0bC;2NBRgUC>c6fes;JE^?Pdrp2(N)kR7)o0rJU;6sspB8< z1MZ07sHY=GTWCx15**E}<{_+`g7?er1LN$SdojBglMNAxT{~WzY zd?Dn&=HJ`}O@xz?T)LF7&p&JE`s8?IuC&*;n#b@9eE9GNm3L9p#U*Im2aTMBbX}&4 zDArDyh!88g5VFdi7?C;}e(P?V$LGA_rBG1!jnLB(7=LjA4^-j`wE(%&VDB<)l!3T$ z;=&ak-`oNK1Dlak{0DZ9#2t7_lnUuiuqxBx5h(0)8oj9jeUt0w-=4KA!$xIK5C5)Q zWQ7`}Am#9fKqY`}`!)AMcWA$&J49jd1x|Q5JT#;nz)HCaAU<9gDVEygvJcWX82kIC zoy`V40qc-y19k||O}P{=b0vJbDZI%yc+L?;c%0Qy55Wdl! zlDEjuE(53rOrAuu=s57A+lCt=PP%n7lvFeE4FFl3v=W*bqZVo10RmXn+@hWV*uPXW fR_YGO^J}X{wi+@lV!y!uhd_i@)lq3c*+&0gC)1ym literal 0 HcmV?d00001 diff --git a/design/src/docs/imgs/upgrade-dfd.png b/design/src/docs/imgs/upgrade-dfd.png new file mode 100644 index 0000000000000000000000000000000000000000..d704bb510265e40cebe3d7bfbf189307623d4457 GIT binary patch literal 12948 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 Y`smy1@Y9nP5ByV8(o!sww|Mb?04INOGynhq literal 0 HcmV?d00001 From 3042a9d4eb78874252d02c5928bd2f1c1bebcf66 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Thu, 29 Sep 2022 22:22:14 +0000 Subject: [PATCH 03/23] Tweaks --- design/src/docs/anatomy.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 144577075d..3153f295c6 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -1,6 +1,8 @@ -# The Anatomy of Smithy Rust +# The Anatomy of a Service -This is an dissection of the various mechanisms at work inside Smithy Rust, intended for new contributors or users who are interested in the internal details. +What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services modulo business logic. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a set of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) to which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. + +This is an dissection of the various mechanisms at work inside Smithy Rust in order to provide such a builder. This is intended for new contributors or users who are interested in the internal details. ## Operations @@ -330,7 +332,7 @@ where } ``` -The `RouterService` is the final piece necessary to form a functioning composition - they are used to aggregate together HTTP services into a single HTTP service which can be presented to the customer. +The `RouterService` is the final piece necessary to form a functioning composition - it is used to aggregate together the HTTP services, created via the upgrade procedure, into a single HTTP service which can be presented to the customer. ![RouterService](imgs/router.png) @@ -532,7 +534,7 @@ The `Upgradable::upgrade` method on `Operation`, previously presented in [ ![Upgradable Diagram](imgs/upgradable-plugin.png) -An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](TODO). +An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs). All builders implement the `Pluggable` trait, which allows them to apply plugins to service builders: From 84f9dfcf29fe6a6e79609d6e921dbb1385c22081 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 10:04:27 +0000 Subject: [PATCH 04/23] Tweaks --- design/src/docs/anatomy.md | 112 +++++++++++++++++++++++++++++++------ 1 file changed, 95 insertions(+), 17 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 3153f295c6..73c872ddec 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -2,13 +2,31 @@ What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services modulo business logic. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a set of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) to which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. -This is an dissection of the various mechanisms at work inside Smithy Rust in order to provide such a builder. This is intended for new contributors or users who are interested in the internal details. +This survey is disinterested in the actual code generator Kotlin implementation and instead focused on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in the internal details. + +During the survey we will use the [`pokemon.smithy`](https://github.com/awslabs/smithy-rs/blob/main/codegen-core/common-test-models/pokemon.smithy) model as a reference: + +```smithy +/// The Pokémon Service allows you to retrieve information about Pokémon species. +@title("Pokémon Service") +@restJson1 +service PokemonService { + version: "2021-12-01", + resources: [PokemonSpecies, Storage], + operations: [ + GetServerStatistics, + DoNothing, + CapturePokemon, + CheckHealth + ], +} +``` ## Operations A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize an Operation as syntax for specifying a function type - constraining the domain and codomain. - + We represent this in Rust using the `OperationShape` trait: ```rust @@ -117,11 +135,9 @@ let svc: Svc = /* ... */; let operation = GetPokemonService::from_service(svc); ``` -To summarize, the `S`, in `Operation`, is a **model service** constructed from a `Handler` or a `OperationService` subject to the constraints of a `OperationShape`. - -Now, what about the `L` in `Operation`? +To summarize, the `S`, in `Operation`, is a **model service** constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. -The `L` is a `tower::Layer`, or colloquially "middleware", that is applied to a **HTTP service**. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: +Now, what about the `L` in `Operation`? The `L` is a `tower::Layer`, or colloquially "middleware", that is applied to a **HTTP service**. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: ```rust impl Operation { @@ -167,7 +183,7 @@ pub trait IntoResponse { } ``` -Note that both traits are parameterized by `Protocol`. These are ZST marker structs, a few [publicly available](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) markers are: +Note that both traits are parameterized by `Protocol`. [Protocols](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) exist as ZST marker structs: ```rust /// [AWS REST JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html). @@ -344,14 +360,7 @@ The service builder is the primary public API, it can be generated for every [Sm /// The service builder for [`PokemonService`]. /// /// Constructed via [`PokemonService::builder`]. -pub struct PokemonServiceBuilder< - Op1, - Op2, - Op3, - Op4, - Op5, - Op6, -> { +pub struct PokemonServiceBuilder { capture_pokemon_operation: Op1, empty_operation: Op2, get_pokemon_species: Op3, @@ -487,7 +496,38 @@ We provide two builder constructors: The `builder` constructor provides a `PokemonServiceBuilder` where `build` cannot be called until all operations are set because `MissingOperation` purposefully doesn't implement `Upgradable`. In contrast, the `unchecked_builder` which sets all `Op{N}` to `FailOnMissingOperation` can be immediately built, however any unset operations are upgraded into a service which always returns status code 500, as noted in [Upgrading a Model Service](#upgrading-a-model-service). -After all `Op{N}` are upgraded in `build` they are collected into their protocol specific `Router` implementation and then bundled up into a `RoutingService`. The `RoutingService` is then wrapped in a `PokemonService` newtype and presented to the user. +After all `Op{N}` are upgraded in `build` they are collected into their protocol specific `Router` implementation, type erased via a `Route` (which basically amounts to `Box`ing), and then bundled up into a `RoutingService`. The `RoutingService` is then wrapped in a `PokemonService` newtype and presented to the user. + +```rust + /// Constructs a [`PokemonService`] from the arguments provided to the builder. + pub fn build(self) -> PokemonService + where + Op1: Upgradable, + Op1::Service: tower::Service, + + Op2: Upgradable, + Op2::Service: tower::Service, + + /* ... */ +{ + let router = RestRouter::from_iter([ + ( + /* `CheckHealth` (Op1) routing information */, + Route::new(self.check_health.upgrade()) + ), + ( + /* `DoNothing` (Op2) routing information */, + Route::new(self.do_nothing.upgrade()) + ), + /* ... */ + ]); + PokemonService { + router: RoutingService::new(router) + } +} +``` + +where ```rust /// The Pokémon Service allows you to retrieve information about Pokémon species. @@ -566,7 +606,45 @@ pub trait Pluggable { } ``` -As seen in the `Pluggable` documentation, third-parties can use extension traits over `Pluggable` to extend the API of builders. +As seen in the `Pluggable` documentation, third-parties can use extension traits over `Pluggable` to extend the API of builders. In addition to all the `Op{N}` the service builder also holds a `Pl`: + +```rust +/// The service builder for [`PokemonService`]. +/// +/// Constructed via [`PokemonService::builder`]. +pub struct PokemonServiceBuilder { + capture_pokemon_operation: Op1, + empty_operation: Op2, + get_pokemon_species: Op3, + get_server_statistics: Op4, + get_storage: Op5, + health_check_operation: Op6, + + plugin: Pl +} +``` + +which allows the following `Pluggable` implementation to be generated: + +```rust +impl aws_smithy_http_server::plugin::Pluggable + for PokemonServiceBuilder +{ + type Output = + PokemonServiceBuilder>; + fn apply(self, plugin: NewPl) -> Self::Output { + PokemonServiceBuilder { + capture_pokemon_operation: self.capture_pokemon_operation, + empty_operation: self.empty_operation, + /* ... */, + + plugin: PluginStack::new(self.plugin, plugin), + } + } +} +``` + +Here `PluginStack` works in a similar way to [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) - allowing users to append a new plugin rather than replacing the currently set one. ## Accessing Unmodelled Data From 4e60105fd76034923930ecabfc532da79b924f2c Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 11:49:44 +0000 Subject: [PATCH 05/23] Fix Rust documentation --- rust-runtime/aws-smithy-http-server/src/routers/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-runtime/aws-smithy-http-server/src/routers/mod.rs b/rust-runtime/aws-smithy-http-server/src/routers/mod.rs index 19e65068af..ecffe36e0c 100644 --- a/rust-runtime/aws-smithy-http-server/src/routers/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/routers/mod.rs @@ -46,7 +46,7 @@ pub trait Router { fn match_route(&self, request: &http::Request) -> Result; } -/// A [`Service`] using the a [`Router`] `R` to redirect messages to specific routes. +/// A [`Service`] using the [`Router`] `R` to redirect messages to specific routes. /// /// The `Protocol` parameter is used to determine the serialization of errors. pub struct RoutingService { From 6b2bae8ace76358675fac621e59f2ba93e6eedb0 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 11:49:51 +0000 Subject: [PATCH 06/23] Fix spelling mistakes --- design/src/docs/anatomy.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 73c872ddec..6d5dc70425 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -74,7 +74,7 @@ impl aws_smithy_http_server::operation::OperationShape for GetPokemonSpecies { Note that `GetPokemonSpecies` marker structure is a zero-sized type (ZST), and therefore does not allocate - only existing in the type system as a way to hang operation specific data on. -The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if it's request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if it's request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. +The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if its request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if its request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. In contrast to the marker ZSTs above, the `Operation` structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely `OperationShapeExt`: @@ -104,7 +104,7 @@ pub trait OperationShapeExt: OperationShape { Observe that there are two constructors provided: `from_handler` which takes a `H: Handler` and `from_service` which takes a `S: OperationService`. In both cases `Self` is passed as a parameter to the traits - this constrains `handler: H` and `svc: S` to the signature given by the implementation of `OperationShape` on `Self`. -The `Handler` and `OperationService` both serve a similar purpose - they provide a common interface for converting to a the model service `S`. The `Handler` trait covers all closures taking `GetPokemonSpeciesInput` and asynchronously returning a `Result` - they are converted to a model service by a `IntoService`. The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput` - they are converted to a model service by a `Normalize` (this is a very small conversion which flattens request tuples). +The `Handler` and `OperationService` both serve a similar purpose - they provide a common interface for converting to a model service `S`. The `Handler` trait covers all closures taking `GetPokemonSpeciesInput` and asynchronously returning a `Result` - they are converted to a model service by a `IntoService`. The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput` - they are converted to a model service by a `Normalize` (this is a very small conversion which flattens request tuples). The `from_handler` constructor is used in the following way: @@ -154,7 +154,7 @@ impl Operation { A typical use of this might be: ```rust -let operation = GetPokemonSpecies::from_handler(hamdler).layer(RequestBodyLimitLayer::new(500)); +let operation = GetPokemonSpecies::from_handler(handler).layer(RequestBodyLimitLayer::new(500)); ``` where [RequestBodyLimitLayer](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request to the `GetPokemonSpecies` operation. @@ -317,7 +317,7 @@ which provides the ability to lookup an inner HTTP service from a collection usi Types which implement the `Router` trait are converted to a HTTP service via the `RoutingService` struct: ```rust -/// A [`Service`] using the a [`Router`] `R` to redirect messages to specific routes. +/// A [`Service`] using a [`Router`] `R` to redirect messages to specific routes. /// /// The `Protocol` parameter is used to determine the serialization of errors. pub struct RoutingService { @@ -538,6 +538,7 @@ pub struct PokemonService { ``` ## Plugins + Smithy Rust also provides a way to hook into the upgrade procedure in order to modify the service behavior. This is done via the `Plugin` trait: From aef6e0c7aaad9bf03e618bbc25720958b2f7b516 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 12:14:31 +0000 Subject: [PATCH 07/23] Add more detail --- design/src/docs/anatomy.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 6d5dc70425..1edab1160b 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -221,10 +221,19 @@ where OpError: IntoResponse

, // The signature of the inner service is correct - S: Service + Clone, + S: Service, + + async fn call(&mut self, request: http::Request) -> http::Response { + let model_request = match ::from_request(request).await { + Ok(ok) => ok, + Err(err) => return err.into_response() + }; + let model_response = self.model_service.call(model_request).await; + model_response.into_response() + } ``` -When we `GetPokemonService::from_handler` or `GetPokemonService::from_service` the `S` we noted earlier in [Operations](#operations) will meet these requirements. +When we `GetPokemonService::from_handler` or `GetPokemonService::from_service` the `S` we noted earlier in [Operations](#operations) will meet the constraints above. There is an associated `tower::Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. @@ -496,7 +505,13 @@ We provide two builder constructors: The `builder` constructor provides a `PokemonServiceBuilder` where `build` cannot be called until all operations are set because `MissingOperation` purposefully doesn't implement `Upgradable`. In contrast, the `unchecked_builder` which sets all `Op{N}` to `FailOnMissingOperation` can be immediately built, however any unset operations are upgraded into a service which always returns status code 500, as noted in [Upgrading a Model Service](#upgrading-a-model-service). -After all `Op{N}` are upgraded in `build` they are collected into their protocol specific `Router` implementation, type erased via a `Route` (which basically amounts to `Box`ing), and then bundled up into a `RoutingService`. The `RoutingService` is then wrapped in a `PokemonService` newtype and presented to the user. +The build method then proceeds as follows: + +1. Upgrade all `Op{N}` to a HTTP service via their `Upgradable::upgrade` method. +2. Type erase them via `Route` (basically amounts to `Box`ing them). +3. Pair each of them with their routing information and collect them all into a `Router`. +4. Transform the `Router` implementation into a HTTP service via `RouterService`. +5. Wrap the `RouterService` in a newtype given by the service name, `PokemonService`. ```rust /// Constructs a [`PokemonService`] from the arguments provided to the builder. From 284a0e695e1df4a86b1e346e5fc1b7fdcd33123b Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 12:24:22 +0000 Subject: [PATCH 08/23] Link to GH --- design/src/docs/anatomy.md | 46 +++++++++++++++----------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 1edab1160b..06dbcfe071 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -25,9 +25,8 @@ service PokemonService { ## Operations A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize an Operation as syntax for specifying a function type - constraining the domain and codomain. - -We represent this in Rust using the `OperationShape` trait: +We represent this in Rust using the [`OperationShape`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L8-L22) trait: ```rust pub trait OperationShape { @@ -63,12 +62,12 @@ the following implementation is generated /// Retrieve information about a Pokémon species. pub struct GetPokemonSpecies; -impl aws_smithy_http_server::operation::OperationShape for GetPokemonSpecies { +impl OperationShape for GetPokemonSpecies { const NAME: &'static str = "com.aws.example#GetPokemonSpecies"; - type Input = crate::input::GetPokemonSpeciesInput; - type Output = crate::output::GetPokemonSpeciesOutput; - type Error = crate::error::GetPokemonSpeciesError; + type Input = GetPokemonSpeciesInput; + type Output = GetPokemonSpeciesOutput; + type Error = GetPokemonSpeciesError; } ``` @@ -76,8 +75,7 @@ Note that `GetPokemonSpecies` marker structure is a zero-sized type (ZST), and t The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if its request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if its request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. - -In contrast to the marker ZSTs above, the `Operation` structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely `OperationShapeExt`: +In contrast to the marker ZSTs above, the [`Operation`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L192-L198) structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L24-L45): ```rust /// An extension trait over [`OperationShape`]. @@ -104,7 +102,7 @@ pub trait OperationShapeExt: OperationShape { Observe that there are two constructors provided: `from_handler` which takes a `H: Handler` and `from_service` which takes a `S: OperationService`. In both cases `Self` is passed as a parameter to the traits - this constrains `handler: H` and `svc: S` to the signature given by the implementation of `OperationShape` on `Self`. -The `Handler` and `OperationService` both serve a similar purpose - they provide a common interface for converting to a model service `S`. The `Handler` trait covers all closures taking `GetPokemonSpeciesInput` and asynchronously returning a `Result` - they are converted to a model service by a `IntoService`. The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput` - they are converted to a model service by a `Normalize` (this is a very small conversion which flattens request tuples). +The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/handler.rs#L21-L29) and [`OperationService`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs#L15-L29) both serve a similar purpose - they provide a common interface for converting to a model service `S`. The `Handler` trait covers all closures taking `GetPokemonSpeciesInput` and asynchronously returning a `Result` - they are converted to a model service by a `IntoService`. The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput` - they are converted to a model service by a `Normalize` (this is a very small conversion which flattens request tuples). The `from_handler` constructor is used in the following way: @@ -157,13 +155,13 @@ A typical use of this might be: let operation = GetPokemonSpecies::from_handler(handler).layer(RequestBodyLimitLayer::new(500)); ``` -where [RequestBodyLimitLayer](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request to the `GetPokemonSpecies` operation. +where [`RequestBodyLimitLayer`](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request to the `GetPokemonSpecies` operation. As mentioned, `L` is applied _after_ the `Operation` has been "upgraded" to a HTTP service. The procedure of upgrading a model service to a HTTP service is described in the [Upgrading a Model Service](#upgrading-a-model-service) section below. ## Serialization and Deserialization -A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the `FromRequest` and `IntoResponse` traits: +A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the [`FromRequest`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L156-L164) and [`IntoResponse`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/response.rs#L40-L44) traits: ```rust /// Provides a protocol aware extraction from a [`Request`]. This consumes the @@ -183,7 +181,7 @@ pub trait IntoResponse { } ``` -Note that both traits are parameterized by `Protocol`. [Protocols](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) exist as ZST marker structs: +Note that both traits are parameterized by `Protocol`. These [protocols](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) exist as ZST marker structs: ```rust /// [AWS REST JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html). @@ -205,8 +203,7 @@ Notice that we can "upgrade" a model service to a HTTP service using `FromReques ![Upgrade Data Flow Diagram](imgs/upgrade-dfd.png) - -This formalized by the `Upgrade` HTTP service. The constraints on the `tower::Service` implementation are as follows: +This formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L76-L84) HTTP service. The `tower::Service` implementation is as approximately: ```rust impl Service for Upgrade @@ -243,7 +240,7 @@ The upgrade procedure is finalized by the application of the `tower::Layer` `L`, Note that the `S` and `L` are specified by logic written, in Rust, by the customer, whereas `Upgrade`/`UpgradeLayer` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. -The procedure of taking a struct and transforming it into a HTTP service is formalized by the `Upgradable` trait: +The procedure of taking a struct and transforming it into a HTTP service is formalized by the [`Upgradable`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L222-L229) trait: ```rust impl Upgradable for Operation @@ -443,14 +440,7 @@ To finalize the build and construct the complete service, `PokemonService`, each Op2: Upgradable, Op2::Service: tower::Service, - Op3: Upgradable, - Op3::Service: tower::Service, - - Op4: Upgradable, - Op4::Service: tower::Service, - - Op5: Upgradable, - Op5::Service: tower::Service, + /* ... */ Op6: Upgradable, Op6::Service: tower::Service, @@ -508,7 +498,7 @@ The `builder` constructor provides a `PokemonServiceBuilder` where `build` canno The build method then proceeds as follows: 1. Upgrade all `Op{N}` to a HTTP service via their `Upgradable::upgrade` method. -2. Type erase them via `Route` (basically amounts to `Box`ing them). +2. Type erase them via [`Route`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/routing/route.rs#L49-L52) (basically amounts to `Box`ing them). 3. Pair each of them with their routing information and collect them all into a `Router`. 4. Transform the `Router` implementation into a HTTP service via `RouterService`. 5. Wrap the `RouterService` in a newtype given by the service name, `PokemonService`. @@ -553,9 +543,9 @@ pub struct PokemonService { ``` ## Plugins - + -Smithy Rust also provides a way to hook into the upgrade procedure in order to modify the service behavior. This is done via the `Plugin` trait: +Smithy Rust also provides a way to hook into the upgrade procedure in order to modify the service behavior. This is done via the [`Plugin`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L31-L41) trait: ```rust /// A mapping from one [`Operation`] to another. Used to modify the behavior of @@ -592,7 +582,7 @@ The `Upgradable::upgrade` method on `Operation`, previously presented in [ An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs). -All builders implement the `Pluggable` trait, which allows them to apply plugins to service builders: +All builders implement the [`Pluggable`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L8-L29) trait, which allows them to apply plugins to service builders: ```rust /// Provides a standard interface for applying [`Plugin`]s to a service builder. This is implemented automatically for @@ -664,7 +654,7 @@ Here `PluginStack` works in a similar way to [`tower::layer::util::Stack`](https ## Accessing Unmodelled Data -An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the `FromParts` trait: +An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the [`FromParts`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L114-L121) trait: ```rust use http::request::Parts; From 2828c85176845b45405a4348d99f6d53f83ccc51 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 12:28:07 +0000 Subject: [PATCH 09/23] Remove Math terms --- design/src/docs/anatomy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 06dbcfe071..3217f6d636 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -1,6 +1,6 @@ # The Anatomy of a Service -What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services modulo business logic. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a set of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) to which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. +What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services while leaving the business logic undefined. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a collection of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) to which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. This survey is disinterested in the actual code generator Kotlin implementation and instead focused on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in the internal details. @@ -24,7 +24,7 @@ service PokemonService { ## Operations -A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize an Operation as syntax for specifying a function type - constraining the domain and codomain. +A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize a Operation as syntax for specifying a function type. We represent this in Rust using the [`OperationShape`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L8-L22) trait: From 40003ac02ee39ae778d0eedd5db9eb1edba9245e Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 12:30:19 +0000 Subject: [PATCH 10/23] Tweaks --- design/src/docs/anatomy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 3217f6d636..a3ce6a98da 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -1,6 +1,6 @@ # The Anatomy of a Service -What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services while leaving the business logic undefined. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a collection of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) to which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. +What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services while leaving the business logic undefined. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a collection of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. This survey is disinterested in the actual code generator Kotlin implementation and instead focused on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in the internal details. @@ -24,7 +24,7 @@ service PokemonService { ## Operations -A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize a Operation as syntax for specifying a function type. +A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize a Smithy Operation as syntax for specifying a function type. We represent this in Rust using the [`OperationShape`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L8-L22) trait: From 5237c4542f8e904189f47a45395f0085a9b42bb2 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 12:44:27 +0000 Subject: [PATCH 11/23] Tweaks --- design/src/docs/anatomy.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index a3ce6a98da..8c261d2efb 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -2,7 +2,7 @@ What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services while leaving the business logic undefined. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a collection of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. -This survey is disinterested in the actual code generator Kotlin implementation and instead focused on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in the internal details. +This survey is disinterested in the actual code generator Kotlin implementation and instead focuses on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in the internal details. During the survey we will use the [`pokemon.smithy`](https://github.com/awslabs/smithy-rs/blob/main/codegen-core/common-test-models/pokemon.smithy) model as a reference: @@ -133,9 +133,9 @@ let svc: Svc = /* ... */; let operation = GetPokemonService::from_service(svc); ``` -To summarize, the `S`, in `Operation`, is a **model service** constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. +To summarize, the `S`, in `Operation`, is a _model service_ constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. -Now, what about the `L` in `Operation`? The `L` is a `tower::Layer`, or colloquially "middleware", that is applied to a **HTTP service**. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: +Now, what about the `L` in `Operation`? The `L` is a `tower::Layer`, or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: ```rust impl Operation { @@ -149,6 +149,8 @@ impl Operation { } ``` +where [`Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) is used to chains layers together. + A typical use of this might be: ```rust @@ -203,7 +205,7 @@ Notice that we can "upgrade" a model service to a HTTP service using `FromReques ![Upgrade Data Flow Diagram](imgs/upgrade-dfd.png) -This formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L76-L84) HTTP service. The `tower::Service` implementation is as approximately: +This formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L76-L84) HTTP service. The `tower::Service` implementation is approximately: ```rust impl Service for Upgrade @@ -230,7 +232,7 @@ where } ``` -When we `GetPokemonService::from_handler` or `GetPokemonService::from_service` the `S` we noted earlier in [Operations](#operations) will meet the constraints above. +When we `GetPokemonService::from_handler` or `GetPokemonService::from_service`, the model service produced, `S`, will meet the constraints above. There is an associated `tower::Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. @@ -277,7 +279,7 @@ where } ``` -Why do we need a trait for this? Why not simply write an `upgrade` method on `Operation`? The reason is that we might **not** want to supply an `Operation` to the service builder, instead we might want to supply something that overrides the typical upgrade procedure. +Why do we need a trait for this? Why not simply write an `upgrade` method on `Operation`? The reason is that we might _not_ want to supply an `Operation` to the service builder, instead we might want to supply something that overrides the typical upgrade procedure. Below we give an example of a ZST which can be provided to the builder, which also satisfies `Upgradable` and returns a `MissingFailure` `tower::Service`. This `MissingFailure` service simply returns a status code 500. From 51a30fd314245529de4d2c97ae3ed2925102cb01 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 30 Sep 2022 13:41:13 +0000 Subject: [PATCH 12/23] Tweaks --- design/src/docs/anatomy.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 8c261d2efb..76a1c5d39a 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -1,8 +1,8 @@ # The Anatomy of a Service -What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services while leaving the business logic undefined. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a collection of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) which encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. +What is [Smithy](https://awslabs.github.io/smithy/2.0/index.html)? At a high-level, it's a grammar for specifying services while leaving the business logic undefined. A [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) specifies a collection of function signatures in the form of [Operations](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation), their purpose is to encapsulate business logic. A Smithy implementation should, for each Smithy Service, provide a builder, which accepts functions conforming to said signatures, and returns a service subject to the semantics specified by the model. -This survey is disinterested in the actual code generator Kotlin implementation and instead focuses on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in the internal details. +This survey is disinterested in the actual Kotlin implementation of the code generator, and instead focuses on the structure of the generated Rust code and how it relates to the Smithy model. The intended audience is new contributors and users interested in internal details. During the survey we will use the [`pokemon.smithy`](https://github.com/awslabs/smithy-rs/blob/main/codegen-core/common-test-models/pokemon.smithy) model as a reference: @@ -635,11 +635,9 @@ pub struct PokemonServiceBuilder { which allows the following `Pluggable` implementation to be generated: ```rust -impl aws_smithy_http_server::plugin::Pluggable - for PokemonServiceBuilder +impl Pluggable for PokemonServiceBuilder { - type Output = - PokemonServiceBuilder>; + type Output = PokemonServiceBuilder>; fn apply(self, plugin: NewPl) -> Self::Output { PokemonServiceBuilder { capture_pokemon_operation: self.capture_pokemon_operation, From 8ff688e219a6dd41fc73cf65646116d0e657f52f Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 3 Oct 2022 10:55:00 +0000 Subject: [PATCH 13/23] Add motivation to Plugins section --- design/src/docs/anatomy.md | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 76a1c5d39a..c296e37a71 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -547,7 +547,44 @@ pub struct PokemonService { ## Plugins -Smithy Rust also provides a way to hook into the upgrade procedure in order to modify the service behavior. This is done via the [`Plugin`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L31-L41) trait: +There are a variety of places in which the customer can apply middleware. During the build: + +- For a specific operation, for example `GetPokemonSpecies`, the model service can be wrapped by a `Layer` before passing it to `GetPokemonSpecies::from_service` constructor. +- The `Operation::layer` method can be used to apply a `Layer` to a specific operation _after_ it's been upgraded. + +After the build is finalized: + +- The entire `PokemonService` HTTP service can be wrapped by a `Layer`. +- Every `Route` in the `Router` can be wrapped by a `Layer` using `PokemonService::layer`. + +Although this provides a reasonably "complete" API, it can be cumbersome in some use cases. Suppose a customer wants to log the operation name when a request is routed to said operation. Writing a `Layer`, `NameLogger`, to log a operation name is simple, however with the current API the customer is forced to do the following + +```rust +let get_pokemon_species = GetPokemonSpecies::from_handler(/* handler */).layer(NameLogger::new("GetPokemonSpecies")); +let get_storage = GetStorage::from_handler(/* handler */).layer(NameLogger::new("GetStorage")); +let do_nothing = DoNothing::from_handler(/* handler */).layer(NameLogger::new("DoNothing")); +/* Repeat for every route... */ +``` + +Note that `PokemonService::layer` cannot be used here because it applies a _single_ layer uniformly across all `Route`s stored in the `Router`. + +```rust +impl PokemonService { + /// Applies a layer uniformly to all routes. + pub fn layer(self, layer: &L) -> PokemonService + where + L: tower::Layer, + { + PokemonService { + router: self.router.map(|s| s.layer(layer)), + } + } +} +``` + +The plugin system solves the general problem of modifying `Operation` prior to the upgrade procedure in a way parameterized by the protocol and operation marker structures. This parameterization removes the excessive boiler plate above. + +The central trait is [`Plugin`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L31-L41): ```rust /// A mapping from one [`Operation`] to another. Used to modify the behavior of From e338488cc7d5ca18cdadc4b87548d9e9cc623750 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 3 Oct 2022 14:50:37 +0000 Subject: [PATCH 14/23] Tweaks to plugin.rs --- .../examples/pokemon-service/src/plugin.rs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs index 6c76df5613..cb05ef2e4e 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs @@ -3,7 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http_server::plugin::Plugin; +use aws_smithy_http_server::{ + operation::{Operation, OperationShape}, + plugin::{Pluggable, Plugin}, +}; +use tower::{layer::util::Stack, Layer, Service}; + +use std::task::{Context, Poll}; /// A [`Service`](tower::Service) that adds a print log. #[derive(Clone, Debug)] @@ -12,15 +18,15 @@ pub struct PrintService { name: &'static str, } -impl tower::Service for PrintService +impl Service for PrintService where - S: tower::Service, + S: Service, { type Response = S::Response; type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } @@ -35,7 +41,7 @@ where pub struct PrintLayer { name: &'static str, } -impl tower::Layer for PrintLayer { +impl Layer for PrintLayer { type Service = PrintService; fn layer(&self, service: S) -> Self::Service { @@ -46,26 +52,24 @@ impl tower::Layer for PrintLayer { } } -/// A [`Plugin`]() for a service builder to add a [`PrintLayer`] over operations. +/// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. #[derive(Debug)] pub struct PrintPlugin; + impl Plugin for PrintPlugin where - Op: aws_smithy_http_server::operation::OperationShape, + Op: OperationShape, { type Service = S; - type Layer = tower::layer::util::Stack; + type Layer = Stack; - fn map( - &self, - input: aws_smithy_http_server::operation::Operation, - ) -> aws_smithy_http_server::operation::Operation { + fn map(&self, input: Operation) -> Operation { input.layer(PrintLayer { name: Op::NAME }) } } /// An extension to service builders to add the `print()` function. -pub trait PrintExt: aws_smithy_http_server::plugin::Pluggable { +pub trait PrintExt: Pluggable { /// Causes all operations to print the operation name when called. /// /// This works by applying the [`PrintPlugin`]. @@ -77,4 +81,4 @@ pub trait PrintExt: aws_smithy_http_server::plugin::Pluggable { } } -impl PrintExt for Builder where Builder: aws_smithy_http_server::plugin::Pluggable {} +impl PrintExt for Builder where Builder: Pluggable {} From 214518c320816af4829d9ce9cfe7c2a6c83730cb Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 3 Oct 2022 15:14:17 +0000 Subject: [PATCH 15/23] Address feedback --- .../generators/ServerServiceGeneratorV2.kt | 2 +- design/src/docs/anatomy.md | 65 ++++++++++++++----- .../src/operation/mod.rs | 4 +- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt index 9486b47488..493e0032c4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt @@ -113,7 +113,7 @@ class ServerServiceGeneratorV2( """ /// Sets the [`$structName`](crate::operation_shape::$structName) operation. /// - /// This should be a closure satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. + /// This should be async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. /// See the [operation module documentation](#{SmithyHttpServer}::operation) for more information. pub fn $fieldName(self, value: H) -> $builderName<#{HandlerSetterGenerics:W}> where diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index c296e37a71..38e1299542 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -22,6 +22,27 @@ service PokemonService { } ``` +Smithy Rust will use this model to produce the following API: + +```rust +// A handler for the `GetPokemonSpecies` operation (the `PokemonSpecies` resource). +async fn get_pokemon_species(input: GetPokemonSpeciesInput) -> Result { + /* implementation */ +} + +// Apply a `tower::Layer` to a handler. +let get_pokemon_species_op = GetPokemonSpecies::from_handler(get_pokemon_species).layer(/* some `tower::Layer` */); + +// Use the service builder to create `PokemonService`. +let pokemon_service = PokemonService::builder() + // Pass the handler directly to the service builder... + .get_pokemon_species(get_pokemon_species) + // ...or pass the layered handler. + .get_pokemon_species_operation(get_pokemon_species_op) + /* other operation setters */ + .build(); +``` + ## Operations A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize a Smithy Operation as syntax for specifying a function type. @@ -75,7 +96,19 @@ Note that `GetPokemonSpecies` marker structure is a zero-sized type (ZST), and t The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if its request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if its request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. -In contrast to the marker ZSTs above, the [`Operation`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L192-L198) structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L24-L45): +In contrast to the marker ZSTs above, the [`Operation`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L192-L198) structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. + +```rust +/// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. +/// +/// The `L` is held and applied lazily during [`Upgradable::upgrade`]. +pub struct Operation { + inner: S, + layer: L, +} +``` + +The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L24-L45): ```rust /// An extension trait over [`OperationShape`]. @@ -135,7 +168,7 @@ let operation = GetPokemonService::from_service(svc); To summarize, the `S`, in `Operation`, is a _model service_ constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. -Now, what about the `L` in `Operation`? The `L` is a `tower::Layer`, or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: +Now, what about the `L` in `Operation`? The `L` is a [`tower::Layer`](https://docs.rs/tower/latest/tower/layer/trait.Layer.html), or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: ```rust impl Operation { @@ -149,7 +182,7 @@ impl Operation { } ``` -where [`Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) is used to chains layers together. +where [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) is used to chains layers together. A typical use of this might be: @@ -157,7 +190,7 @@ A typical use of this might be: let operation = GetPokemonSpecies::from_handler(handler).layer(RequestBodyLimitLayer::new(500)); ``` -where [`RequestBodyLimitLayer`](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request to the `GetPokemonSpecies` operation. +where [`RequestBodyLimitLayer`](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request body to the `GetPokemonSpecies` operation. As mentioned, `L` is applied _after_ the `Operation` has been "upgraded" to a HTTP service. The procedure of upgrading a model service to a HTTP service is described in the [Upgrading a Model Service](#upgrading-a-model-service) section below. @@ -201,11 +234,11 @@ pub struct AwsJson11; ## Upgrading a Model Service -Notice that we can "upgrade" a model service to a HTTP service using `FromRequest` and `IntoResponse` described in the prior section: +We can "upgrade" a model service to a HTTP service using `FromRequest` and `IntoResponse` described in the prior section: ![Upgrade Data Flow Diagram](imgs/upgrade-dfd.png) -This formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L76-L84) HTTP service. The `tower::Service` implementation is approximately: +This is formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L76-L84) HTTP service. The `tower::Service` implementation is approximately: ```rust impl Service for Upgrade @@ -234,9 +267,9 @@ where When we `GetPokemonService::from_handler` or `GetPokemonService::from_service`, the model service produced, `S`, will meet the constraints above. -There is an associated `tower::Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. +There is an associated `Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. -The upgrade procedure is finalized by the application of the `tower::Layer` `L`, referenced in `Operation`. In this way the entire upgrade procedure takes an `Operation` and returns a HTTP service. +The upgrade procedure is finalized by the application of the `Layer` `L`, referenced in `Operation`. In this way the entire upgrade procedure takes an `Operation` and returns a HTTP service. ![Upgradable Diagram](imgs/upgradable.png) @@ -320,7 +353,7 @@ pub trait Router { } ``` -which provides the ability to lookup an inner HTTP service from a collection using a `&http::Request`. +which provides the ability to determine an inner HTTP service from a collection using a `&http::Request`. Types which implement the `Router` trait are converted to a HTTP service via the `RoutingService` struct: @@ -340,7 +373,7 @@ where R::Error: IntoResponse

+ Error, { type Response = http::Response; - type Error = Box; + type Error = /* implementation detail */; async fn call(&mut self, req: http::Request) -> Result { match self.router.match_route(&req) { @@ -378,7 +411,7 @@ pub struct PokemonServiceBuilder { } ``` -The builder has one generic variable for each [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) in the [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service). Each setter switches the type of the `Op{N}` type parameter: +The builder has one generic type parameter for each [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) in the [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service). Each setter switches the type of the `Op{N}` type parameter: ```rust /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. @@ -410,7 +443,7 @@ The builder has one generic variable for each [Smithy Operation](https://awslabs /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. /// - /// This should be a closure satisfying the [`Handler`](aws_smithy_http_server::operation::Handler) trait. + /// This should be an async function satisfying the [`Handler`](aws_smithy_http_server::operation::Handler) trait. /// See the [operation module documentation](aws_smithy_http_server::operation) for more information. pub fn get_pokemon_species( self, @@ -430,7 +463,7 @@ The builder has one generic variable for each [Smithy Operation](https://awslabs } ``` -To finalize the build and construct the complete service, `PokemonService`, each builder has a `build` method whose constraints list out all the requirements for composition: +To finalize the build and construct the complete service, `PokemonService`, each builder has a `build` method whose bounds list out all the requirements for composition: ```rust /// Constructs a [`PokemonService`] from the arguments provided to the builder. @@ -573,7 +606,7 @@ impl PokemonService { /// Applies a layer uniformly to all routes. pub fn layer(self, layer: &L) -> PokemonService where - L: tower::Layer, + L: Layer, { PokemonService { router: self.router.map(|s| s.layer(layer)), @@ -621,7 +654,7 @@ The `Upgradable::upgrade` method on `Operation`, previously presented in [ An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs). -All builders implement the [`Pluggable`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L8-L29) trait, which allows them to apply plugins to service builders: +The service builder implements the [`Pluggable`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L8-L29) trait, which allows them to apply plugins to service builders: ```rust /// Provides a standard interface for applying [`Plugin`]s to a service builder. This is implemented automatically for @@ -687,7 +720,7 @@ impl Pluggable for PokemonServiceBuilder< } ``` -Here `PluginStack` works in a similar way to [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) - allowing users to append a new plugin rather than replacing the currently set one. +Here `PluginStack` works in a similar way to [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) - allowing users to stack a new plugin rather than replacing the currently set one. ## Accessing Unmodelled Data 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 5fec3a2f15..ddfab157d6 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -49,7 +49,7 @@ //! //! ## [`Handler`] //! -//! The [`Handler`] trait is implemented by all closures which accept [`OperationShape::Input`] as their first +//! The [`Handler`] trait is implemented by all async functions 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 @@ -191,7 +191,7 @@ pub use upgrade::*; /// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. /// -/// The `L` is held and applied lazily during [`Operation::upgrade`]. +/// The `L` is held and applied lazily during [`Upgradable::upgrade`]. pub struct Operation { inner: S, layer: L, From 35f4dcc55052bb425e2839e8e49ae42a139f9959 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 3 Oct 2022 15:47:08 +0000 Subject: [PATCH 16/23] Address comments --- design/src/docs/anatomy.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 38e1299542..4fad9d1971 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -7,6 +7,23 @@ This survey is disinterested in the actual Kotlin implementation of the code gen During the survey we will use the [`pokemon.smithy`](https://github.com/awslabs/smithy-rs/blob/main/codegen-core/common-test-models/pokemon.smithy) model as a reference: ```smithy +/// A Pokémon species forms the basis for at least one Pokémon. +@title("Pokémon Species") +resource PokemonSpecies { + identifiers: { + name: String + }, + read: GetPokemonSpecies, +} + +/// A users current Pokémon storage. +resource Storage { + identifiers: { + user: String + }, + read: GetStorage, +} + /// The Pokémon Service allows you to retrieve information about Pokémon species. @title("Pokémon Service") @restJson1 @@ -88,6 +105,8 @@ impl OperationShape for GetPokemonSpecies { type Input = GetPokemonSpeciesInput; type Output = GetPokemonSpeciesOutput; + // NOTE: `GetPokemonSpeciesError` is an enum generated from the `errors: [ResourceNotFoundException]` from the + // model above. type Error = GetPokemonSpeciesError; } ``` From cb0db87883daf70f4c5475fcc1b83004e7236260 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 3 Oct 2022 19:33:38 +0000 Subject: [PATCH 17/23] Address feedback --- design/src/docs/anatomy.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 4fad9d1971..4dcd2694cf 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -105,13 +105,13 @@ impl OperationShape for GetPokemonSpecies { type Input = GetPokemonSpeciesInput; type Output = GetPokemonSpeciesOutput; - // NOTE: `GetPokemonSpeciesError` is an enum generated from the `errors: [ResourceNotFoundException]` from the - // model above. type Error = GetPokemonSpeciesError; } ``` -Note that `GetPokemonSpecies` marker structure is a zero-sized type (ZST), and therefore does not allocate - only existing in the type system as a way to hang operation specific data on. +where `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput` are both generated from the Smithy structures and `GetPokemonSpeciesError` is an enum generated from the `errors: [ResourceNotFoundException]`. + +Note that the `GetPokemonSpecies` marker structure is a zero-sized type (ZST), and therefore does not allocate - only existing in the type system as a way to hang operation specific data on. The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if its request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if its request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. @@ -154,8 +154,10 @@ pub trait OperationShapeExt: OperationShape { Observe that there are two constructors provided: `from_handler` which takes a `H: Handler` and `from_service` which takes a `S: OperationService`. In both cases `Self` is passed as a parameter to the traits - this constrains `handler: H` and `svc: S` to the signature given by the implementation of `OperationShape` on `Self`. -The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/handler.rs#L21-L29) and [`OperationService`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs#L15-L29) both serve a similar purpose - they provide a common interface for converting to a model service `S`. The `Handler` trait covers all closures taking `GetPokemonSpeciesInput` and asynchronously returning a `Result` - they are converted to a model service by a `IntoService`. The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput` - they are converted to a model service by a `Normalize` (this is a very small conversion which flattens request tuples). - +The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/handler.rs#L21-L29) and [`OperationService`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs#L15-L29) both serve a similar purpose - they provide a common interface for converting to a model service `S`. + +- The `Handler` trait covers all async functions taking `GetPokemonSpeciesInput` and asynchronously returning a `Result`. +- The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput`. The `from_handler` constructor is used in the following way: @@ -185,7 +187,7 @@ let svc: Svc = /* ... */; let operation = GetPokemonService::from_service(svc); ``` -To summarize, the `S`, in `Operation`, is a _model service_ constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. +To summarize, the `S`, in `Operation`, is a _model service_ constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. More detailed information on these conversions is provided in the [Handler and OperationService section](https://github.com/awslabs/smithy-rs/blob/39c0096c33417d44f125a042c112b3c16918098a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L50-L100) Rust docs. Now, what about the `L` in `Operation`? The `L` is a [`tower::Layer`](https://docs.rs/tower/latest/tower/layer/trait.Layer.html), or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: From 5dbc8bac44519ff1f407a384d69559a887251591 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 3 Oct 2022 19:47:45 +0000 Subject: [PATCH 18/23] Fix missing an --- .../server/smithy/generators/ServerServiceGeneratorV2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt index 493e0032c4..b8a7980010 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt @@ -113,7 +113,7 @@ class ServerServiceGeneratorV2( """ /// Sets the [`$structName`](crate::operation_shape::$structName) operation. /// - /// This should be async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. + /// This should be an async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. /// See the [operation module documentation](#{SmithyHttpServer}::operation) for more information. pub fn $fieldName(self, value: H) -> $builderName<#{HandlerSetterGenerics:W}> where From e40aff985957469a0de6e171050318384fc24c81 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Tue, 4 Oct 2022 18:42:38 +0000 Subject: [PATCH 19/23] Use mermaid --- design/src/docs/anatomy.md | 75 +++++++++++++++++++-- design/src/docs/imgs/router.png | Bin 16986 -> 0 bytes design/src/docs/imgs/upgradable-plugin.png | Bin 21128 -> 0 bytes design/src/docs/imgs/upgradable.png | Bin 16694 -> 0 bytes design/src/docs/imgs/upgrade-dfd.png | Bin 12948 -> 0 bytes 5 files changed, 71 insertions(+), 4 deletions(-) delete mode 100644 design/src/docs/imgs/router.png delete mode 100644 design/src/docs/imgs/upgradable-plugin.png delete mode 100644 design/src/docs/imgs/upgradable.png delete mode 100644 design/src/docs/imgs/upgrade-dfd.png diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 4dcd2694cf..58dcc2c51d 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -257,7 +257,19 @@ pub struct AwsJson11; We can "upgrade" a model service to a HTTP service using `FromRequest` and `IntoResponse` described in the prior section: -![Upgrade Data Flow Diagram](imgs/upgrade-dfd.png) +```mermaid +stateDiagram-v2 + direction LR + HttpService: HTTP Service + [*] --> from_request: HTTP Request + state HttpService { + direction LR + ModelService: Model Service + from_request --> ModelService: Model Input + ModelService --> into_response: Model Output + } + into_response --> [*]: HTTP Response +``` This is formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L76-L84) HTTP service. The `tower::Service` implementation is approximately: @@ -292,7 +304,17 @@ There is an associated `Layer`, `UpgradeLayer` which constructs `Upgra The upgrade procedure is finalized by the application of the `Layer` `L`, referenced in `Operation`. In this way the entire upgrade procedure takes an `Operation` and returns a HTTP service. -![Upgradable Diagram](imgs/upgradable.png) +```mermaid +stateDiagram-v2 + direction LR + [*] --> S: HTTP Request + state L { + state Upgrade { + S + } + } + S --> [*]: HTTP Response +``` Note that the `S` and `L` are specified by logic written, in Rust, by the customer, whereas `Upgrade`/`UpgradeLayer` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. @@ -412,7 +434,21 @@ where The `RouterService` is the final piece necessary to form a functioning composition - it is used to aggregate together the HTTP services, created via the upgrade procedure, into a single HTTP service which can be presented to the customer. -![RouterService](imgs/router.png) +```mermaid +stateDiagram +state in <> + direction LR + [*] --> in + state RouterService { + direction LR + in --> ServiceA + in --> ServiceB + in --> ServiceC + } + ServiceA --> [*] + ServiceB --> [*] + ServiceC --> [*] +``` ## Builders @@ -671,7 +707,38 @@ The `Upgradable::upgrade` method on `Operation`, previously presented in [ } ``` -![Upgradable Diagram](imgs/upgradable-plugin.png) +```mermaid +stateDiagram-v2 + direction TB + Op1: Operation#60;S1, L1#62; + state Op1 { + direction LR + [*] --> S1 : HTTP Request + S1 --> [*]: HTTP Response + state L1 { + Upgrade1 : Upgrade + state Upgrade1 { + S1 + } + } + + } + + Op2: Operation#60;S2, L2#62; + state Op2 { + direction LR + [*] --> S2: HTTP Request + S2 --> [*]: HTTP Response + state L2 { + Upgrade2 : Upgrade + state Upgrade2 { + S2 + } + } + } + + Op1 --> Op2 : Plugin#colon;#colon;map +``` An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs). diff --git a/design/src/docs/imgs/router.png b/design/src/docs/imgs/router.png deleted file mode 100644 index fdda140abf6e772814140a155a0d9b746ff85cde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16986 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 jxFX^onX6IVMTh diff --git a/design/src/docs/imgs/upgradable-plugin.png b/design/src/docs/imgs/upgradable-plugin.png deleted file mode 100644 index ecadb38a355de133339a33c0d06bc9094015edbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21128 zcmZs@1z6K>8#XM60wPjMh#(~)AdFFy(W6Vcl7q(eeM5Rgvk zl#ch_|NrRoeDC`m$DFYJ_S+rTeZ_g5Ct;6N<%qA{zIN%-C1M46gvO;yxC`Ltj|5l2 z^C4#d!=+1C%G_l1+#JzXwhoq;?m?yh{plVbuZ=Ux?H&|ykB`p+Y3ga~{J`89>GHtT z(bUV?3H9qipU$rNHkBPHvVC;D7KkcvjN{PkP`V zg!chN;NL5d9@yLS@;%_?0B^`yn%g^ryJ0T@4+wZk))r}L<%|X|{&Us8L-4RfqHLX= z{v9!d?*ab<;eX$-bThU7_f<7+eRHI<5Cm!~Y)!3^reIlkZU3_x2D(rkO^5>wVJ7TkrY<8XsAFdR@8v=|Hmc?VC{LM3 zybyUs6)Al)DVUuipZz0KEiEloh?TP+nCULjtH zr|lyheWainpOb@zva+70sl1keqNA{exrQXtQbAsW9|eQ)+oBci)#afu7XevoTkU&% zQcAk&(i#S)3W{o;3a&6~Nm(~*J^=%1d3&U`q?44axreE~G(uBWQd!5-)mB^IO<0Z3 z)6UY#PS;MxN>frtTS`D4j0UQxZli4mQI>b(<+pm|sids0g@Af_DOk$rpk(AQaw?{_ zPP_mvv3A$8S8_p0xkzC=6@)NI3r8~z zJ&c+<+T4bZPsWQE#pfi4(vU$|pi$}?yhuGeNvNi?v;|sAz){*k-vD8ysrSgtSrVmV z?I16YQd4zwv~)0YLGek-dMfFoEHqVg)%g(~YR)zqXhkbc1$ikoPt`}R?rOr?{3wXC ziVf^b>&TC><}=lF;?s9k!Kf+d!{lw;ENw7QFJO66>MrIMo^GlZb^?4TIeS}YM{T6K zxt5g{pETOt-CPnZoU)ufQqNMy00ngtz?eEA^qplDQM?c-EgdT*Z2A?9D7bCAO z=Ov>r$M;A>USCzk%2g6(;UcJK<76fzjgqqPbaXdInIcrJz1)PMC@)Df1#KM%DK%#U zMHLlUYaNV^jHQ8{mHHzWEn7)VCsUX+Kd+XffQJsmQPoaKQbowYQ_4wO0Ss5wO;!_u zf|@Gmsp{IQ3rRve_2gtU)nr{z5MF*TSPf;20l$vDr<1a+E}xzprOyl>xuGkb60RvR)bkO@#zXmAqA~Hfw4(B zt0`J2d+KYW1e~;?Xmc3S046Dn=EV+y7o+AZsO|yA3Ncd^(1!^N>N$I$<-C;Cu=Deh zLdt4Oqb!{4g=P6=4Iq|!nodfNI*Ll*dZetTpn#+*Qosu9Q;=4kT5fvEnwAhR6*D0| zsE)Rqhn1bYj^!g=49Z;rVg~NUXRF`8$J~z z#=%ie848oJK+EX~2}1alg@mP0SQi782H)|=tNi0)!0-QY&rq2$C@H4|HG3+mK2@$v&P>rIq@{}M_pVPn+3`1g~OqTh!7e#7NubfQ+W zthh`Je;zSgc%Pgm?1l>stsBnv4R?P!HzwZoJ8U{VT1sDx7C)CiPCRWauG>*8cNpbE zwbNL?8LvkDOy{_$b(v{qb)KwS98lueN@05C!4iS_x;47UzT;Nf8k4l&}f;Z68X>EFsE~F(Qi>tb0ThW>gZ+kTmL9} zqJTS5t-Cz%e*MhCmLi7z#~|aMAv54IvXyxKw%eTvxy$OiGF)hHI3H@Zv}Bu;y6CNR zusM~^@-?;Xl2EFE{p;$Dn$?$LmS0}~u76-!6M(?H@ltZXg+oUA?<2q*Z@Abvo#ivt zgU2@4D7nxn(`_wu{4M!uJ~Jie9XB+~t$MS(64=$5Y_y|neObEAUl)Q(~O0N7MRF!v%@9qh;?oG_vGv#z<{tDji16SXGiNSk=-T`ou4Me(K

BE9$2F?_k&ol+(o~u5DMNmJD3G@p+IsrT3IxkL)vlwf|A%*oHYf z{!LMHM7}^cQE!(*5c>3=hr}C#>vx2|`s{s^8y1Nmbsi}(eUrDT^dv_m*{ba*LpsFa z`Z04feD;g)?2``&Or5vH0o9+c>>R+maaYXY_+Zod`1h(cz2BkfV7@L7>UcTF+Lkp} zAT2VJ_qLc%mF4$xYqdC0@BP3T7WFjVC%M7@`SPa(fngHJ*s#wC8`hVdd-*(itqgto zoh$=PvYF$>Y_bb4{v9NEmbi~6tgzd~md;PXrdlBdS8suseLD&~VfboT>E!5ju;I8f z-$fDr^B74IhBPk8d-P|Eama*YKfjfH?r{6_L-&<8s;LjJT>DnNlxClXS)>`PupOjW z`RH?m+?@^QNxJKQ`fc26wJ_Utu02~K05>4_B9x4|XKl2+msn`rq{efT{*9MqHWT?m zV#fQ;{~6p(JW6f@i~8!w+X8%O&*wIwJ7DF;h_kyKPxjXZWlW!6CGMH_7IQ|qY_dama{N$i-xUfCfkzDko zHt9*N#}$LEm$GE8=WFHtN&D+R@re%zf5)G|Kj15~eU5fA9CjCblh>v4oTnPHWx^=~ zY@Q=93^kIk{^PiC$iQ+5K0M#hco?X(P zQ9bp>RZ4guppq;okSyqkG=8HRLSA*c^QHIM6(Z#ZpQFdGZ;mdIFT}bIRQ@%ZGHM+7eCk8@mkj-8 zXK8iS1SnNVWKhTBOgNFaja|*3!APv&g)%Np-w7)CMaUR$xjoy~(D6noepUak5t9a3 z;B}U1yp_Ny2Tt9U3`dE;8EO@=CQ%I~DN za#11vBv|K4_#bx}7aAv?LX!u2 zC2tM2Y>gU$v*8@@Ki!stdha_JhLrb<{TvQZ>rE0+NrPj2Y8$7npmvt#qQ#wnQ=LC5 zWa$=_%ftPz-O0WM7J+q`*}%b==GTnYdKM?bF=f94`;&$GuNlkXCEVA(zp>t=w8=%T z$2;yesPft!9H&B0F$Y2S6|LP->-0p^mSBv5dl*v9HIMiII-&Yy7GX5vem)=a#2m-? zce^=@_4f`3m%HrI|)n&^=`3Zzlzf`;;O#%??HxljZc*Br9?0MJ! z&iyNy%~0N}>cu2`FsrqS`+2{vXGK0HG$djn(4W*D!`L;JlSLC+pY4GC>}| zUpe7nNSeQFpE~J#Fu~$_4lFTA=|Oetj}sX~bR4S+*5_jbCY>YM{=+b&?x9=h-}#Ra z#1uu7h7}rCUM*W85#5VXVG~Bq%hPVA4Xrm`oE0yFIFoDK*r1|MgJABR*EROF>=W6n z`;gD3PA~BA3HzTkl2jJgAqFw-YNHwxq2=v3R#8g7<1|roCD$(C!jLaqBEY7EF1)ZYyT{r3PfKRrlnaZK|+Kl-eddYw1a^~R{AOEARKv%q9y zw7jEicEnrnitY1_%Or4<`&u7N#y;9p-MSIB^Pyy!>F?5)kO`x=-gYGm6~kPnB~0?f zta_+-oAB>qlNAoFdldD}ewQfqT9I+G5cb#1ayL$VQhj#15{q6UFxj!oxPR}%OU{rZ z#Icqn=y*GHB~BrTc}Xsso-Xt_u#_C#3-V%TBj4Xc5?cPVtzm*~i(hyeQdq{ z*Mfc#Fto$ITlS^ETnoKhf{7jBBAMSvyw^%QdVm{q@Y|h#&21(#@QawfQ&N1-|_J zoJ7oq;0a@`LS4j&89XPD|IjjY2ZYR&l0%7j6rUAb6)!S@J!IR^x!$rRQ0)BByfMaF zZ)0nwWgSNw4DQQTt&e0ToJslk<=+X65P-;hckvm~177cgjYz|xFw=D40CP3ozbJO^ zl#fSUv`5m?!uMN0n)kzZBXSKZi;hk<8+!-U#2vGh6U`J~-M752oo7*I-S5O67ss#k zn#;C+o!g*-*L~$XMAU1qxx2pK=lj#A z;5(hnuDO02z5QccPQ~y5G!ts=+tg z|Kglx(l`??jW~YxKMQ|H%x5IU?|69;cnH_rhtGUKU|CE-^@VkvG2dFirYgYUe1v)J zedt9?1K_MdpM4BX0S&8(>SYC$aXU`TUyuCp&XWY}dv57IHl^g!Gr352 z3&d89qKSL?#SLYR((!Y*LRzX^=NyhGSd~jSE}Jhju?A*=1>V2;6+NQBmTEC2OR5;3 zrV!up&eKipj~~D>CfwVY`1yhX8P_pJS_`tQ$gq%cTiVG@8mQG9HL)X>+-eK)nLveR z;TM$Lc0o{j^GSLWbTP=Rozi^w^*Q4SRhqU|n5yX1U>WS_PmrBDip=Kh0burDgYy`= zu|?@s68aRD3bKq8yD;{~xhRqA^sDn8AeU+7F9^MIDftP8g4Etz{Uy{|6LKK9G0ZUQDiH#;}_s= zl~XeDmG(dS9;12%6lU&Qv>t)7OQr3_E%Q+Uk3lxv#2&6v{xoS}Iym}KCj9lZJI|jb z3^*ibn8fs_iCTSriUTPmNM;W!-G_crG$1|(po4NOlT5+Zy^qMw!;GP12)m!bm~zbSgr?+?8-*o9wjbl4e3u8 zpZfkmy(PGiRN8m5{$R|TH1iod8#lu;p~8>QkQA@d?bwn;Jz9PJ!`6pSnAoC_dN*B< zY!jPx&Cd9L`TY$;4dhivi^;v9m~~Lb6kOlrbN#XnSJ|mQm@H3J>3sMcxSO79GuYfw zA)aEXS5~DJGINvQ5GVKzmRMrQGLKr{`R$iT-I0At%_!k{Zm9s=D=!E3V($e#x@k5O z=F8e^3V@-(y_H!NHsve*JO?KW3C3&w=B)*qVnom8X=#t>8My!_8P zkX7xE**i9<2%0|j7`603$#f4nm)I4Ig+49a4!mvr?1II{htThCZZI2psO7_EhDamzHdM#%(DUmH6Ebu+o@|J4 zezjyGWx)l*{!aUCNM=sSZQM=UfuMB}uhc-Uj!S58e(=Hk9 zBp7)u<88kC;mL%$x=h)nN6xCG79UrcBJB@CmBSz&HbD}+$D~X+SPe=`N6oG_)v?yT zRFUK`3N{0>FWCmjcg*}Sh4{+ffug&V8K8pC#_TR;G<~mfHBVMbad_x=+u7ZuYjJGe zN(NRO4~j1)lInC((z&vb8+3L^fC9K#I7K)Y{|u8L2kRMjz-!~9P$x$09gYoFC<&C; z`zhInl4Tw)Xw^4dqUx&gcqj5Bft_a0BcoIHx@IH@P?J&&4Ys?W=v|w93)0S?l{@bh zUzO!}A-QwHcJ`l2KkK~QxBRM9QRtV9vCB0(P|U99X8X||%&=(fRExY`ABoMCgHaB_ z&bl@gKb($`F|6NM&Hxz$eQolJDpO3vrOTD=39UXX{0%HtjlmzaOS@}!zDkpf)_Y9U zxD(Ok?IB=NcVbm~jv{EF$^b|03Al{!a0*LwXgFG=T|H`FjWoH34`=g#xV;R*8!v#! z%EFltFgADOZ5wLGt?4E!fC7IK^)s;3*DIr|J^E(se>U`pP~VKq>bG*CDr!rG_lYbam#GT0iWyQIl4IkkkWGGAD6An$bRa|4vg$(+V)4qc10W%jRl} zt*Bbet`%oDER}+;DR%Gdt|`!{&pjd_k?E6CZbuWj22Vz5;olqCN`V0U?L^Ky+TA;f$*VCIL%b{Ljf3o!sJaX7fKOek{e)_?^?QS+22W2wUm%PlDFt?d{JalbL! z9GbeuFotfNKoeHH5R}Pj*}!|HWZS{)(JCLVpQWGA-`yc)QBuYguph3#m*_$;(Bti$ zwmtV++sHkSq_dykaKvSnjo_Up_TL}1_H99oTuVf3QHKnXE?6il*s>f~0brsR>#mEj zo?QAqn)$PB0)gx7ue;uB5U9!LODCyQ{udKg?55Ie!I!syX?!Z358W|Wr-r~nJNg^m zidu|eK(g;stI`ogSIFSmm(b<0rpjWny&imDxB+jZBI7*L`L4x6?dp-z+NACUtH(v~ zdv^lG8|^b&RN@gTQGrD%-ri-W#spOB!jR2V02OlydjV)ji|4?7lhIn&7Q%wSz>@ zxd3Qr^o>s#Qk($4Kv^XTXElaPhfdT#Y+`0*s&!+Ex55eAm6`q=AozNe#GHZ!QzPUw z{!4BGr3(HKQg_6MWFGu!(gJ(P-MoqVFI&#^#LYs}kA4lK7vGLG|Am zd7YEjrVllSO*ER?&>ejwi}DLYs>0X;1JFqQVHSxSpck7x&airmcmPdK-)5V6&3OBT zoy3=KSu|gxx|oIrb`wVE3eQ#6&WWX0g>7Fhk*d7|lN7M#jtN4%79foAPJO%%Aj0h{ zml7S2i#@gg`MIaQK{RveR-OV>A~p6(e&!NSsmPPF3_?E>?jHM&4 z#RQ`_u{TlOr+L@aop^5*A(I8$f$Mr97Nsk{z)s%ycPBI4{T5&DYla2|6s{cd;D}S% zbfKn|;@J*0a}=OWBEr+XgS%8)H<0?Gzl)Wg){G0_G%L!Fb~FhXB~39B84YV&wgo>m z&QJ!RR!HyVekgz%k{cp^KH;}*C$4w$iFlgU*y?xvJ9dpEo#`+i!3neTJDkDq&wmJ@ zi(O!jNlWSele=ZwCc=a+nf5`~ZYkG#tizo@gN)U=;dDnS4B6jxwLs$k-O0C=PUojb zR-G}7R-j%=(|x7)TaF&nO8@Zz1rCAFb{mz|;nvK4g=X=3&1w*o81k1-3c_Ak0L`*HvXySUkS@hRu=4#$(4ir);@S46PIvYPnWJf_d1gNR2x zZCWjswHI%5`l*mwzwu|0PRrll8z11TAa(K5yjD0+lbEGMLC3MG`)mts9MNJ26kb(jhm}UeoqBZ=Hj4Jc@1d?dy4(y`dvpZ&OHh&~QVX z1CQDRzzgRjA!jF0$ys4rQ2p87UYlghvTwKzKfM9rV0aJ-P#8zyRGrLm&4MOQQ(AkF9;YS->!n5P(D~a@6=?uG(SUQmsCw~s|NTC~v{t#3 zSksnrMXN_&&km-Xzw%%kYImOAyveMSPBO#S`Y2>*qiXI(ZRa*Jqugais)ar*v_%&S zmxD-brr9hk3m<3=qfi0BsgLyuC^2cqPdq2w6Y8lH#`STGz;Q^MN*>RN7TwnyxVwP$ z!yEMnM!Ub>Mbmro>?9yva3n7hOF=<5>IGWcqwOB=tr|~v!m&MB(a8jFgnbhKyL$;A ziZCUT< zHVZ+3O?yR?{|wthc5eX%;QY~|F3-=;Q0UuEk2?06$RyDVJrgKAF6*|KMq>f( z8x}gfaN*XIae#_h9qlYw0bnN^7{1O2k~_k@nkt^GDt?rlI%`$-SRfML-mH{VR{6el zs=H2hx7zu_TeT#0A+BbkZOSYkS^1^btBhiBIWz^@k|y%(pz-44w&gdYyS<|3uEew` zJ%WSPAN@nEG+nzn+Y5)=S2A4ZJIQFDcj-wRJyzxFN#HR?JV~EC*9;EO(E zOTGmHAUbEv_eB;Gn3ktLT-*PZ2@)^mj!0T6MRirhLXRR3NTY>59-7fmVq z4r3YE^0t(jd;$Iz3)`gGBJ#`uw#0ws+D%^b_J~rLC}#aRezLY0?L9qW7(G3F<$7S& zkm(h>xGd{0R`+#_9NI)_$+FeboisP53H9pGrM*F6zY2}&Yi39m-bIV?0pdiZe_%lw zrrvEPi0ZbP>-lc_1vPxU;PW=`*5-F2vVL6Le!Mt3&O0k91EgoCQOB2ppZrkPi>g1I z4PPw#@&F22e5DJT;wysy0#lQL)xHL?#%_co^>KVl8R*RldOWEAk!G6AU+?vH@P3HQ zjeeGXg4#V2(CZhf``U<3-coFiPo4Wleb#zc6Up5q{Sjp-EIu8bLxqn+Um_3cmA(-qs26veiFigb4;98a7%znhHz1h0 z(?L^SWHTn8Ciz2*3hF!)ID69L8*1Jj51PXxmA4}}PouV{+i9IE zc5ySJ12Y-miNypb^HVOz(>|u(UXA&5-02qR(eDZG@$SI^SmwtAcLePB%6N5oYIx{# zS@yu0652p&M@f=t{xFXSS5?XR(cj&t?1T9=?{)fscXV{hOQ2B0l&e^bWdBmpWRM zX|B8B9Wz@T(Wfomfgwi@GeJ}AXVb#PY5Ik^?uXN~fG~S3!pix`xn`MFB!=;8;s?dXQ`a<;7LJ8NI*mY^|zkY4;q7kq|xd4-`Kr&?y`v zq<4Jyx{66B+sLas6+f7+!NncE!1cmr4$xw(WJInf)>bQ~u07Oo zENLcEcqVJ1NW5!IwYtSd``%m);QU)t1>36hp*s_`Fb|;R1lKQXQWl4ja2>UV$3sMT01nxo6egt(A)VY~4#(MO z(`)aFBckLgL6MnL+x~p!6sSH!Z3{@!(%8_ywZJ69=G7K32@Mu^OmAtFwHgtDSJul3Vn?flnt zh#T~98*DOKKO96v^(pgesSoIqj{FFsCLy~_boqLmh^tH#RVYq`LQ>bA{>yP!83{r; z*?fW|L-9$~`#$1z&dtb+g_V+9vvD?_9L`0rZ&I_T`Y*W?&L&H~MuTV{RT(Gf!u^~z zUnt_xKqs@y^Jlw8ye9ri8xwxt=zGmDsmpsN4<4=b>9FZG@4{-p%>v^4y~88}Usn@&rV4<1gpa{A*|xoP8kK)NZpkOab3>X3eUS)e zw|?rn6TtVDfpqj&C{>Mciv55eApr>^${oAQp-5IZ zoCe*reGIr?f(#6L)gtZ+4TNzNEqd$>dvxI;?4E+pcI3L|o1}2m91Lt}naN~~;EyD^ zTgVpb7Cz5~ZeQ&nY?N1vhYItK?pzJ(h32aCamZlo!awhoapAp6rqJx0O$xei1}~>z zb^OQAnXQEdf-r^-!(Ri<_8zU_TTA+iV00ejO<~f{mS{T^kV>uFK`=aL_nqEAUMp8s zG+|~6ue^2Iv^8iT6f#)*ST;PPFePuL&0+1voCM?Fu_jG}!q;14n7b316;j*loe!Hol!ruB#e9g>Ge)2KW9Bd~EV9 ziu>`Z`>D|Fpz46e=(o2~;{F|6zsQI5w01mi&bQsG33p{Ynv@ngi_Lw5gpbwHqXc0) za{XCN-Rb_;3w`#&MwQzk!IwIS!c1gu&K+4joAJC(e_gS4X3LAWn%&cg1W7#GsF|ZG z_$fJnpcp=BzoB$n61cFTD-cY4?{tW696sgt^@{I^YUY=E!jHu63ci@e$s$It6LvJs zJxzEX5Ji0R`dXJXnZ^qw-kl%9wQmPk7_0|cTTu6nz!iYcAKGq6x%dIG7n3vJ`_rL5V2A%5b&y>j>(57Y* zr!Svec?aDRWQd{RC!0VoZQtgt;**d49pqKQF0sYDuVC64{0&U0)GSJO!WKU>{ZMcx zibA(nB4qlWTl==eBY(lmWJPn-mjx!>*oG%Zm;0yt-=@T}s~vfFn{KsH>wS5}nQ&7x zlr8cK0jC@#V`!>87rFXP1(@lXsucp3hHqrLn@77zUFwMK%Vj(Oi9^`^?!?nkx!GYR z-*f(k)5h(fpc;zBm=w`TNIQ&j#;Ki(G9@>NobMCc%3(9L#7PR~N>N04UsWW#wG*S+9VrPp%G4aao?0S{S=t zZ&JBKlZLBPxg4e_WfFQKyEa!i_k8JwG}rq@I;%$(8j@Motmk}|Y}>cvf^Z=|70-`n zVQQ1F_a0K>bO72#?7<47%gU?K9j03{iQGb5Q=a?OEd32H>o!hnq1EJG%~>H|t<<}q zJ>0wX`lVRNWCaL`D`cq`P3ZQqL}cDH)@A;Qd6#b!rThZ4a`0f5LQg* z6GdF%SBE#8fAe{vZj08N7%l$}OOxzXSG|cd(-e5kTEJYj-E?IEN8N~*MJn)$h$@_i zYmm*$%1u))es{}4yM?)BRmCVkW~FOCS=&k`cIr2w*OBrw)wK_UAzXM+C=9mP)vM)6szS4r zeLF;PiV?9VNr9DkDt+G$QnBBh@ayDrKWF`lvH0p6_0hl}Qq^Y({O`9v#Xq8y(vc5n zUv(35HiPDc`=!HhTi>HbjCV-WdS1DlRuaB@--t=Rdy@z$uTnrw#?(yT%5YDGVJ)d1 ze+{zToYEtn23S=0-}BY6#Sp}0A87FzXR{S!`x5n^MCNK_v~Bmg>)+B$^YPnWtwNPa z3}GQ)wtpbt=aUZyI9^6ZAP9`!{J`{23@i8>Uy#p73YK_{4o)fzt1U2k9G_(1bfL2>tMcXQc)Pz>|YUL%T74mrd zQBCQUfw%KE2Rj&3XFO}b+`wP3-OZ&>;`hbPH)CdcyTrq!oN@N@Sg7Jj0_iQb-76uu zFW*fN5xvMsiMdraN+!oGl6EQARNuwJiKc{T;BI|HL$rjlVC)|VOCKD~_6^nl~^B{GVOW6tMnjXL5*`qTbDw+KRD zD1EQ;H&}d&B6(D!3g(i31;{f=@IMgT^}@MFG&>@3Wdns*7}YQUpIr;^ zsGRbv{kh1p_Pe<8yk=%A4Y0!73L}jcIn<0^BQWDN{DMMYX0&}ZN-O`&EbiZ#Nw3!R zXwz>}h;}tJ<<4V~)XT?J!`nU3e*P?GWsHaG=Q`85 za6(%b`-xi5N?N&58yXTKsY7M>7+tTZ_{ka+OZwRbd}dix^{V}dO*h9r=(M>$q0CTR zW40NuDg|7>Bpe=zO!jHI`01fREQ(+79~57w(7^KQb?WIbc7H5gC+ZHY2hcg`ceBe~ zV@?_YRRPlIsplLTN+;~{wi{XiH6cKnyR4sW%>Uf`jvpEy4!8V z4aa#$)6Yq+LmEmy0VuQ&tHU5|W(tqEl)*_b3Tjw6kARRV0D0H0(cZ%QNUKyGEdg0@ zvI(9x2+JgO)4ENv+$hb?{ZD^o2NU;ke%R32U3&qb1vIKUEVQx(z4?NzHIv(Q zQ-E8sMoW%N=ostV-`)Abll4l<|oFVA3-zF4Yb^gt_`&{-J4#3lr%ZMx1-d; z^*Vnx2A%w_bhb(?_i8rksjiV#v6^j;;zV%|7>4g)#d?<#>AM`mro@di4EwA@` z04$ttLH>ui_(XiZ{bl*js}wo7BM5DdX*?iLvEgsPtBcDlNNZ1EZ(8Q-Cv^FGBbN+p z_Ik}4(SxebUfnQLh|VW~*#iLiYgJ%xYhI+^L=NaIVxhQf!1?u?m<>e2BtL;3$|3+E zJ$=>qI%ArGF2|NQbli5Uz!GZ#pkG9!i_^qZ;Hs-I@{3!Xx9{AEvJ}zT`UdP7{c^ZXd)=*c39kmA zQZH_Dn90N{tzvI|<6-Z*u z@E2wJ#2Qyz9~eCiid8kmrha}enbw?45Q_+2onuiy2hcFx0wN2$%!Oc?pSp>LklYny zOLfz@WS?{2@(M0!!YsJ5@^{Iy zSgpZ6-gmv=Wv~TPTXo>m{VGE$rDknCfF{r*L-oY1j(zEZ9^iPi;kTl*j*1N4hpRDs zUQH3dI32uBE2!*-U?2rU#LJ|G=LWV@sqyBK-X-`4@&z=ej<6IPC+Q9QR%@VzdRGk} z{PEap`MqFvN=YJm44iK>Azr4N_TN943iV|Y>VI(Yx|c!ezB>Hz#yDqDRWm*Mc|s$A zXkGL70b9Xwd8G6m2WU{v ze9#9Rk?AJ0-KTgM;S-)%`@-#er}g>Z$@fhs_pia6XvqOAB~BYE;=W=*+8v=^%F9N-Dy?!w!OQc6qfF|AxTRR5b!k%YT z<@O2qf#SE3A9v(3V_tC`G6ZII0n);N`bA^XZR6%4WBvM1Ky3m6c3D4Khbia0ne~DB z&Me>+xoWFsQBl~vfO30ojK>1$JM{wWrl4KbsRPrUYD~%*_}P3AuNd;{ZM4{Nr}h=M z(gVCGx>Jj}CF(%*tJZu{$CbUba>c#ZPur=SF~{0T?Np7Od5^^jweod(uLC8bR-u8w z%Fk1v4iUr)Zm~5pm3$Z&Uhxu(cuUc4LWzQ7ndOpzgoQ0*1km$|O#z|4?$&DnJcnJ$ z=HDb!@-yOki`7g)E3>lmHE%7rzRyqoD_;nexQSQS-7HnG2c&0abVh#3R8sxn56h_5 zK?6h(OYr5s5ul2`hMO8$Y+>G{ue2sMiR4Qcrj@9#^5?f~9ne&LEP7)V;KT^cp)7li zFEz8L8kys_44(X#oO}}(|Hd6&i;j^-|Hl0LWY@Y`rRrM!3?G?w6Ut$`>IL}YrJD^W zeGLM7pBxfztp=8t98svjY}Mx+OCvbg_zyu45CVK-_aO&rz_RZLIn`VslBN4e#!%Z| zVxA<^S1jSDD7xUzHv=xw63MQHXVe|xN+WzT_9-YScG3*Wz=D#gn8R3Dzzb6M>d z@iS4QPe25hOxU&wUzoR&dlYwnFfvxD5wt)B8*35Ov@dIP{z@8d;)Qz4N-*Aj_fgN% zv?;%zQM9!bziDJzapW3DS?c#t$q%}A?hFmypaawpj^rsT`uyq&_0i-YAEx{HaaOL; z-?6KcQNz(C!~zVb&5>8*;>NJgDSoTmL6JS^EAjZqRv-n>sa~d7Ny2Aqbj< zQ}ca8*vC3ayv~6eG}oy9y-x`qsd}pd1qEFp#+^ak>tLzVL-mV$vK`NuVDM25=$(yd zLcYXv%o#!l9{ys33S~v*r2Ew@e|slalb{f99h}#|`Iqy44VM5ZMGG>X*}R4nr>?7_ zcyEn8hV`uzL1%f40%{pcHd60h$)TPZNUXW@1*_{U|E8S8-!;Tdp!N{7PJ%7}U&{2w z>5}->rjrr#XqhKxdRY_U+Sv&o81c5Giup3Le(*~rjJyduj2^~nFoA%FXZO3Nnx$SK z0uvm%cjYfzk(Ycr<~6nz<6`ih)_$ZUW5$K9wfycrVA3aJTfY>{U~D#14fWU%gP9J% zXR2k0n_}`NxvR?WZhohx0x-W$hFO{8*!x3c{CNIO*Z}uQjazAshR4P@Y(+4Jq!x5f zvD$)&nuxsz^N;BHLZ{DO$;dtpA}>7z%!25(XUcxzb=&jeJjM+^ti`7Xt5}NqXoGJp z%|kaE1em^Dpn@^^AB`yiGmges?0a*-Z79l9bHHZi%0SNd_*L9++pGK9eD7o<`E-pi z?|By3G%`RBA$NJ~W4?ZkTP{y;63`L$21_ef)7H)e)2L9LoSIA^xQguVVqm4>0QT|%WP`!+Q zqXII0`Reu1ngwjkt{%ju#LBN2)zdg0AD8{5tWpzl8P$y}R-A)~^F&)5kP!BHY07HQ zj)5K6kc(Ah?A81VB4Il!pcfA0)5>opD6cpb`yPIXjiVJ}*D_G?{0b+C;PhL$J zh^VkM=>Bg$TTS@ERp+DS*0uLfvf@GT8&lryNND^!Rl z9zQ=hVyWJ}fJbmwn+E81Ob2EDchfJKGv5vAN7&D`M?$e!K8=Wn19q$E@MmM#YJq|w zErD(r7l>$Wp;Obn`(vRCjsMX`y?byBdiH$2$9oXKl8COk4&*%FmZkoVADlD^DtHnY z07~1pSs9zaZB2VQf()ok6r>6JYerObH9OOv+sB^clXw|L5RasQ-w5M>b4Y zE;JY#_ui~mr&|C0OgR(2QuOtI^i^!r+62-|eW~lV8FUZbwr&!dr8EDWp!b@$eQ%~k z9kLa}y#yNCyL(YscHo`}{%IAYSULfej(fpHe`WpzqBw#_+>fiiDyIm4(>4s{SVX&v z8Qpf>od4FRXiL6Xv-&{#FzQE6GFJbHr4R*+UU4b_+=<24hEAohg?hd)WU+kSoxeK_ zEc91KNDnx7>jsa00LsT6P|7(m>? zcHpaaDOMnW6|2yI*q4vgFyd0*;84Bw_ir+g>fp}j$yP8HOpLrwFtV#J4J=37wiYp8 ztsl|)Wm#Xxl@A=OhwHr6uA|*={c@|wCoZv{zD6?s(szrM{bmpTDnQbQ?!S6e0`35i z_l?CpBpY^G10wEE`Y^yRozH$lQ6v+C3cZoGi-5xC?Kpb`N|d0ced58+Lr@RgmtP?`oB};+AxsPZ~gZ8TbOi7JO%B z&Asy){SK(T`#v(TUA}%+1tjl6(TJKKivO2PHTG!&oBS+>ZLQT&UitrYaW4K$?|&SR zIOZrDa!%zo=9Wv7PMnEaOR`#{+tjHPEq-dQMPnm!a_pyEySQz#9L|wbZe_6)Dk76q zZiUQT=Hz}W%6Wg!Qe@r=)g>z@K`e*~}F+rB-=p}_4n zuWIMWbS~AiZez&bgLZVqO+`O@?C0C_T=*lFvbyUm@BaIaoWMu!9-UGK@ z?OtZe8G%-=a1edjcUyhXPm8TX{yxx}Vdo(Yme!PhB=hYVE``#s&09kroURw{f!WrV zAvlCP(hExBR7aWr=-odwZb39cwr2dJG;#Ct@^NE6fLAs-j}lVso^GYKC3cqn7GQC- zQmB8TA6SKvjWkYBx7zS`-xM68MIiJod$oRT`BFfGwM4yll4^v2s`rDHYdzY2EVA`6 zi`G?_g!>CyTX7Dq)>yP#dodY~9LZUh6-IK!+)aesrT4+mHW`*nyY3iDu^Kl1<`W`o zg|MLtnan$c%khM3xtT%(VW+zmZw_(3_BiB;wiMZRvs;jmh<^brFxBzg;sS1fb1fa} zQaT*M*5D6Qjj$Kc(?H+bdyo^<`ZiZyhV7_xm+%vo{tM+e6!|fLO`{Q2VH9Z5(C6BL zKcxe4Dvy<%9qo#FW;||fKlk#&Sb_+t1 zQ?p>>8_h3x1_yz6(DP%Ztp8m#3;>UadE2H>Q+r)rXb0_^|1*zw63Dgz6_<%Lw2s|N z3fJxx5~~4l^RvUjul85IF%Qii+hY0vym}#9#_DhhZHkxloa!JtW8t{C^6l!-y7tQr z)#gxx{o)#?rlFxZ<*LXYu-IA--=ZC_x4A1OIFT#K&G4I09EMDYB*SF9aEw$@h&`o} z1?{rD(!^&uVJI1$VO!?k%_~LKaW|F?PwX{^h190PHTqf`PSSt5%NY8o`;uFFF+ykA z3bLZn|AK+BL~D8GK2G~%uUw}`Tmt3yiS9&sfH9? z6F8S7={!QP% zGXkh}9avFH3xu%^CgBGD=FW{PT|l&$gugu`Q@^DOz~*{kIPSkvmw0rP`p^P4&9oi2 z>BIdHLiI4kE58Iz`-_SM76(j7i?b7W5n1e z_v;&|sNGfP72o%FGBf@!F$85P=b6WvmY%R(6(-S-fY6afKy!~f>kYa&?@WzsN?TtG zi>s={;c(!tc>BU3vLvMBeNR<%csoODmyuEZjqKc0vfsPwwyX{m?l-i2wDiXZQdI-oUJV<}zMGN#JfJj)+XtSzF{BLl~m8o>g z-MUpYcVz zA3F+@S(&Ag9(O>qcIKX<7d1n_uV~ivbN>Y?dn17YO;gv04SWa7onQ~mG^2d^*@+6D z_2fu~nDp2!_#mIWKD*;jma&LvLIpuPRxB~!pTB~+08s?@(jc-zQw&&WQSfc3#sky+ zx)$++N_(i^y)~?drE-3j?7aR*IdQ4>=qLh&tz}F^vV7-fM_ndtnlcG3Vb8fWQJ_Ny zrr%p1=>pqo@&v!+UNMmC2-QHlm=)6DleQLrL#0hSZY3T}z9*>ZwN|N)p)144!CzI+ zhA2qMIFKFdoqD)4VwKV-ESsxgQ|8<0rD!)Rxvmow$I{VF7Fufm>bNiu{$o0@%O{4b z*6>9JQHs$=^VoY5VA0l5lY|jNHJwo@!AqbaecRh&A956sTp07!1hR(QP+6crt$HrQ zzjB?$GR!kD@los+l#=3YxhVv~{T9OGmJdUjgF~6pw1ojg2^UR?hIk8xUK!SizZ^dNk zNpmDF2UCmMLH)BY5~~mfr1N95rShs}Ft>JC?_A-hE(x2GjRKC?c!hXfhm&X&>3Ygv zicvfh7v9gWO;gK0gD>3z_k!|7yga&lAFv6=1ry)AiX_jFKT>!s>e9HeQm8AsoKr~P zv3)`A+Egryy9Pv7drg{pvthkPk=rX{*+1D;g9#LbuWC?G5>E6X(aGv&8-H6IV_r=R zQ)uas25c+2&l|6qx(tr&7t4-%HKhlJmEzvZ=)6z^5h3;u=6J7%A1y!F0miPO1SAG&? zO6mRx$*$QO(Z2_6X3CkkK66oO^hI7^<$W-1Q1%1!=unt#$vL>!(SqHVFQtpJofh7} zJ~A*b!Tn70wu9vljOt6y%-$TAHDwO*7*x<&h zjMik6hQf(ufQ;p^V4IE~BI*x-8oE+!!mfW|jNN@JZ|m1HUyPFL73f4&f_ZEbZe()J z1u0pBX;%aAhXsZD>?dkHfCW7>`02iH|Dxi}PW2{2<4iLoOfDgBqdMLq9XtOI+a0Z# diff --git a/design/src/docs/imgs/upgradable.png b/design/src/docs/imgs/upgradable.png deleted file mode 100644 index 1777574de4ea8ec252a540dae7569b5a98ada09f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16694 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 diff --git a/design/src/docs/imgs/upgrade-dfd.png b/design/src/docs/imgs/upgrade-dfd.png deleted file mode 100644 index d704bb510265e40cebe3d7bfbf189307623d4457..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12948 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 Y`smy1@Y9nP5ByV8(o!sww|Mb?04INOGynhq From daf701ac42fe4a19882deb5cab213aecb3609228 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Wed, 5 Oct 2022 11:37:48 +0000 Subject: [PATCH 20/23] Improve Rust doc links --- .../server/smithy/generators/ServerServiceGeneratorV2.kt | 2 +- design/src/docs/anatomy.md | 4 ++-- rust-runtime/aws-smithy-http-server/src/plugin.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt index b8a7980010..d63ed4cd73 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt @@ -312,7 +312,7 @@ class ServerServiceGeneratorV2( #{SmithyHttpServer}::routing::IntoMakeService::new(self) } - /// Applies a layer uniformly to all routes. + /// Applies a [`Layer`](#{Tower}::Layer) uniformly to all routes. pub fn layer(self, layer: &L) -> $serviceName where L: #{Tower}::Layer diff --git a/design/src/docs/anatomy.md b/design/src/docs/anatomy.md index 58dcc2c51d..46f2f6afc4 100644 --- a/design/src/docs/anatomy.md +++ b/design/src/docs/anatomy.md @@ -660,7 +660,7 @@ Note that `PokemonService::layer` cannot be used here because it applies a _sing ```rust impl PokemonService { - /// Applies a layer uniformly to all routes. + /// Applies a [`Layer`](tower::Layer) uniformly to all routes. pub fn layer(self, layer: &L) -> PokemonService where L: Layer, @@ -767,7 +767,7 @@ The service builder implements the [`Pluggable`](https://github.com/awslabs/smit pub trait Pluggable { type Output; - /// Applies a plugin to the service builder. + /// Applies a [`Plugin`] to the service builder. fn apply(self, plugin: NewPlugin) -> Self::Output; } ``` diff --git a/rust-runtime/aws-smithy-http-server/src/plugin.rs b/rust-runtime/aws-smithy-http-server/src/plugin.rs index b00fd3c947..206a0d7fea 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin.rs @@ -27,7 +27,7 @@ use crate::operation::Operation; pub trait Pluggable { type Output; - /// Applies a plugin to the service builder. + /// Applies a [`Plugin`] to the service builder. fn apply(self, plugin: NewPlugin) -> Self::Output; } From 91855b8f95d4c231cb55da9e3130234f329fc355 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Wed, 5 Oct 2022 18:11:23 +0000 Subject: [PATCH 21/23] Add commented items to book --- design/src/SUMMARY.md | 1 + design/src/server/overview.md | 1 + 2 files changed, 2 insertions(+) diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index f104234efc..5a25cb39da 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -18,6 +18,7 @@ - [Generating Common Service Code](./server/code_generation.md) - [Generating the Pokémon Service](./server/pokemon_service.md) - [Instrumentation](./server/instrumentation.md) + - [RFCs](./rfcs/overview.md) - [RFC-0001: Sharing configuration between multiple clients](./rfcs/rfc0001_shared_config.md) diff --git a/design/src/server/overview.md b/design/src/server/overview.md index 2b023112c5..8957eb45d0 100644 --- a/design/src/server/overview.md +++ b/design/src/server/overview.md @@ -5,3 +5,4 @@ Smithy Rust provides the ability to generate a server whose operations are provi - [Generating Common Service Code](./code_generation.md) - [Generating the Pokémon Service](./pokemon_service.md) - [Instrumentation](./instrumentation.md) + From 79e50f9d14d17ddad19f4e7b2ec1a49a4daa94f8 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Mon, 10 Oct 2022 12:36:23 +0000 Subject: [PATCH 22/23] Fix spelling --- design/src/server/anatomy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 46f2f6afc4..f025e14e14 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -647,7 +647,7 @@ After the build is finalized: - The entire `PokemonService` HTTP service can be wrapped by a `Layer`. - Every `Route` in the `Router` can be wrapped by a `Layer` using `PokemonService::layer`. -Although this provides a reasonably "complete" API, it can be cumbersome in some use cases. Suppose a customer wants to log the operation name when a request is routed to said operation. Writing a `Layer`, `NameLogger`, to log a operation name is simple, however with the current API the customer is forced to do the following +Although this provides a reasonably "complete" API, it can be cumbersome in some use cases. Suppose a customer wants to log the operation name when a request is routed to said operation. Writing a `Layer`, `NameLogger`, to log an operation name is simple, however with the current API the customer is forced to do the following ```rust let get_pokemon_species = GetPokemonSpecies::from_handler(/* handler */).layer(NameLogger::new("GetPokemonSpecies")); From 06b604da42ca8d087b1a4bb2f83f3118f77d2926 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Tue, 11 Oct 2022 18:07:25 +0000 Subject: [PATCH 23/23] Add FromParts impl example --- design/src/server/anatomy.md | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index f025e14e14..7c14ecd15e 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -840,4 +840,41 @@ pub struct Parts { } ``` -This is commonly used to access types stored within [`Extensions`](https://docs.rs/http/0.2.8/http/struct.Extensions.html) which have been inserted by a middleware. +This is commonly used to access types stored within [`Extensions`](https://docs.rs/http/0.2.8/http/struct.Extensions.html) which have been inserted by a middleware. An `Extension` struct implements `FromParts` to support this use case: + +```rust +/// Generic extension type stored in and extracted from [request extensions]. +/// +/// This is commonly used to share state across handlers. +/// +/// If the extension is missing it will reject the request with a `500 Internal +/// Server Error` response. +/// +/// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html +#[derive(Debug, Clone)] +pub struct Extension(pub T); + +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) + } +} + +/// 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 { + let mut response = http::Response::new(empty()); + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + response + } +} +```