aws_smithy_http_server/routing/
route.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) 2021 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
35use crate::body::{Body, BoxBody};
36use http::{Request, Response};
37use std::{
38    convert::Infallible,
39    fmt,
40    future::Future,
41    pin::Pin,
42    task::{Context, Poll},
43};
44use tower::{
45    util::{BoxCloneService, Oneshot},
46    Service, ServiceExt,
47};
48
49/// A HTTP [`Service`] representing a single route.
50///
51/// The construction of [`Route`] from a named HTTP [`Service`] `S`, erases the type of `S`.
52pub struct Route<B = Body> {
53    service: BoxCloneService<Request<B>, Response<BoxBody>, Infallible>,
54}
55
56impl<B> Route<B> {
57    /// Constructs a new [`Route`] from a well-formed HTTP service which is cloneable.
58    pub fn new<T>(svc: T) -> Self
59    where
60        T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone + Send + 'static,
61        T::Future: Send + 'static,
62    {
63        Self {
64            service: BoxCloneService::new(svc),
65        }
66    }
67}
68
69impl<ReqBody> Clone for Route<ReqBody> {
70    fn clone(&self) -> Self {
71        Self {
72            service: self.service.clone(),
73        }
74    }
75}
76
77impl<ReqBody> fmt::Debug for Route<ReqBody> {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        f.debug_struct("Route").finish()
80    }
81}
82
83impl<B> Service<Request<B>> for Route<B> {
84    type Response = Response<BoxBody>;
85    type Error = Infallible;
86    type Future = RouteFuture<B>;
87
88    #[inline]
89    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
90        Poll::Ready(Ok(()))
91    }
92
93    #[inline]
94    fn call(&mut self, req: Request<B>) -> Self::Future {
95        RouteFuture::new(self.service.clone().oneshot(req))
96    }
97}
98
99pin_project_lite::pin_project! {
100    /// Response future for [`Route`].
101    pub struct RouteFuture<B> {
102        #[pin]
103        future: Oneshot<BoxCloneService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>,
104    }
105}
106
107impl<B> RouteFuture<B> {
108    pub(crate) fn new(future: Oneshot<BoxCloneService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>) -> Self {
109        RouteFuture { future }
110    }
111}
112
113impl<B> Future for RouteFuture<B> {
114    type Output = Result<Response<BoxBody>, Infallible>;
115
116    #[inline]
117    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
118        self.project().future.poll(cx)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn traits() {
128        use crate::test_helpers::*;
129
130        assert_send::<Route<()>>();
131    }
132}