aws_smithy_http_server/request/
mod.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6// This code was copied and then modified from Tokio's Axum.
7
8/* Copyright (c) 2022 Tower Contributors
9 *
10 * Permission is hereby granted, free of charge, to any
11 * person obtaining a copy of this software and associated
12 * documentation files (the "Software"), to deal in the
13 * Software without restriction, including without
14 * limitation the rights to use, copy, modify, merge,
15 * publish, distribute, sublicense, and/or sell copies of
16 * the Software, and to permit persons to whom the Software
17 * is furnished to do so, subject to the following
18 * conditions:
19 *
20 * The above copyright notice and this permission notice
21 * shall be included in all copies or substantial portions
22 * of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
25 * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
26 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
27 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
28 * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
29 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
31 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 * DEALINGS IN THE SOFTWARE.
33 */
34
35//! Types and traits for extracting data from requests.
36//!
37//! See [Accessing Un-modelled data](https://github.com/smithy-lang/smithy-rs/blob/main/design/src/server/from_parts.md)
38//! a comprehensive overview.
39//!
40//! The following implementations exist:
41//! * Tuples up to size 8, extracting each component.
42//! * `Option<T>`: `Some(T)` if extracting `T` is successful, `None` otherwise.
43//! * `Result<T, T::Rejection>`: `Ok(T)` if extracting `T` is successful, `Err(T::Rejection)` otherwise.
44//!
45//! when `T: FromParts`.
46//!
47
48use std::{
49    convert::Infallible,
50    future::{ready, Future, Ready},
51};
52
53use futures_util::{
54    future::{try_join, MapErr, MapOk, TryJoin},
55    TryFutureExt,
56};
57use http::{request::Parts, Request, StatusCode};
58
59use crate::{
60    body::{empty, BoxBody},
61    rejection::any_rejections,
62    response::IntoResponse,
63};
64
65pub mod connect_info;
66pub mod extension;
67#[cfg(feature = "aws-lambda")]
68#[cfg_attr(docsrs, doc(cfg(feature = "aws-lambda")))]
69pub mod lambda;
70#[cfg(feature = "request-id")]
71#[cfg_attr(docsrs, doc(cfg(feature = "request-id")))]
72pub mod request_id;
73
74fn internal_server_error() -> http::Response<BoxBody> {
75    let mut response = http::Response::new(empty());
76    *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
77    response
78}
79
80/// Provides a protocol aware extraction from a [`Request`]. This borrows the [`Parts`], in contrast to
81/// [`FromRequest`] which consumes the entire [`http::Request`] including the body.
82pub trait FromParts<Protocol>: Sized {
83    /// The type of the extraction failures.
84    type Rejection: IntoResponse<Protocol>;
85
86    /// Extracts `self` from a [`Parts`] synchronously.
87    fn from_parts(parts: &mut Parts) -> Result<Self, Self::Rejection>;
88}
89
90impl<P> FromParts<P> for () {
91    type Rejection = Infallible;
92
93    fn from_parts(_parts: &mut Parts) -> Result<Self, Self::Rejection> {
94        Ok(())
95    }
96}
97
98impl<P, T> FromParts<P> for (T,)
99where
100    T: FromParts<P>,
101{
102    type Rejection = T::Rejection;
103
104    fn from_parts(parts: &mut Parts) -> Result<Self, Self::Rejection> {
105        Ok((T::from_parts(parts)?,))
106    }
107}
108
109macro_rules! impl_from_parts {
110    ($error_name:ident, $($var:ident),+) => (
111        impl<P, $($var,)*> FromParts<P> for ($($var),*)
112        where
113            $($var: FromParts<P>,)*
114        {
115            type Rejection = any_rejections::$error_name<$($var::Rejection),*>;
116
117            fn from_parts(parts: &mut Parts) -> Result<Self, Self::Rejection> {
118                let tuple = (
119                    $($var::from_parts(parts).map_err(any_rejections::$error_name::$var)?,)*
120                );
121                Ok(tuple)
122            }
123        }
124    )
125}
126
127impl_from_parts!(Two, A, B);
128impl_from_parts!(Three, A, B, C);
129impl_from_parts!(Four, A, B, C, D);
130impl_from_parts!(Five, A, B, C, D, E);
131impl_from_parts!(Six, A, B, C, D, E, F);
132impl_from_parts!(Seven, A, B, C, D, E, F, G);
133impl_from_parts!(Eight, A, B, C, D, E, F, G, H);
134
135/// Provides a protocol aware extraction from a [`Request`]. This consumes the
136/// [`Request`], including the body, in contrast to [`FromParts`] which borrows the [`Parts`].
137///
138/// This should not be implemented by hand. Code generation should implement this for your operations input. To extract
139/// items from a HTTP request [`FromParts`] should be used.
140pub trait FromRequest<Protocol, B>: Sized {
141    /// The type of the extraction failures.
142    type Rejection: IntoResponse<Protocol>;
143    /// The type of the extraction [`Future`].
144    type Future: Future<Output = Result<Self, Self::Rejection>>;
145
146    /// Extracts `self` from a [`Request`] asynchronously.
147    fn from_request(request: Request<B>) -> Self::Future;
148}
149
150impl<P, B, T1> FromRequest<P, B> for (T1,)
151where
152    T1: FromRequest<P, B>,
153{
154    type Rejection = T1::Rejection;
155    type Future = MapOk<T1::Future, fn(T1) -> (T1,)>;
156
157    fn from_request(request: Request<B>) -> Self::Future {
158        T1::from_request(request).map_ok(|t1| (t1,))
159    }
160}
161
162impl<P, B, T1, T2> FromRequest<P, B> for (T1, T2)
163where
164    T1: FromRequest<P, B>,
165    T2: FromParts<P>,
166    T1::Rejection: std::fmt::Display,
167    T2::Rejection: std::fmt::Display,
168{
169    type Rejection = any_rejections::Two<T1::Rejection, T2::Rejection>;
170    type Future = TryJoin<MapErr<T1::Future, fn(T1::Rejection) -> Self::Rejection>, Ready<Result<T2, Self::Rejection>>>;
171
172    fn from_request(request: Request<B>) -> Self::Future {
173        let (mut parts, body) = request.into_parts();
174        let t2_result: Result<T2, any_rejections::Two<T1::Rejection, T2::Rejection>> = T2::from_parts(&mut parts)
175            .map_err(|e| {
176                // The error is likely caused by a failure to construct a parameter from the
177                // `Request` required by the user handler. This typically occurs when the
178                // user handler expects a specific type, such as `Extension<State>`, but
179                // either the `ExtensionLayer` has not been added, or it adds a different
180                // type to the extension bag, such as `Extension<Arc<State>>`.
181                tracing::error!(
182                    error = %e,
183                    "additional parameter for the handler function could not be constructed");
184                any_rejections::Two::B(e)
185            });
186        try_join(
187            T1::from_request(Request::from_parts(parts, body)).map_err(|e| {
188                // `T1`, the first parameter of a handler function, represents the input parameter
189                // defined in the Smithy model. An error at this stage suggests that `T1` could not
190                // be constructed from the `Request`.
191                tracing::debug!(error = %e, "failed to deserialize request into operation's input");
192                any_rejections::Two::A(e)
193            }),
194            ready(t2_result),
195        )
196    }
197}
198
199impl<P, T> FromParts<P> for Option<T>
200where
201    T: FromParts<P>,
202{
203    type Rejection = Infallible;
204
205    fn from_parts(parts: &mut Parts) -> Result<Self, Self::Rejection> {
206        Ok(T::from_parts(parts).ok())
207    }
208}
209
210impl<P, T> FromParts<P> for Result<T, T::Rejection>
211where
212    T: FromParts<P>,
213{
214    type Rejection = Infallible;
215
216    fn from_parts(parts: &mut Parts) -> Result<Self, Self::Rejection> {
217        Ok(T::from_parts(parts))
218    }
219}