aws_smithy_http_server/routing/
lambda_handler.rs1use http::uri;
7use lambda_http::{Request, RequestExt};
8use std::{
9    fmt::Debug,
10    task::{Context, Poll},
11};
12use tower::Service;
13
14type HyperRequest = http::Request<hyper::Body>;
15
16#[derive(Debug, Clone)]
25pub struct LambdaHandler<S> {
26    service: S,
27}
28
29impl<S> LambdaHandler<S> {
30    pub fn new(service: S) -> Self {
31        Self { service }
32    }
33}
34
35impl<S> Service<Request> for LambdaHandler<S>
36where
37    S: Service<HyperRequest>,
38{
39    type Error = S::Error;
40    type Response = S::Response;
41    type Future = S::Future;
42
43    #[inline]
44    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
45        self.service.poll_ready(cx)
46    }
47
48    fn call(&mut self, event: Request) -> Self::Future {
49        self.service.call(convert_event(event))
50    }
51}
52
53fn convert_event(request: Request) -> HyperRequest {
61    let raw_path: &str = request.extensions().raw_http_path();
62    let path: &str = request.uri().path();
63
64    let (parts, body) = if !raw_path.is_empty() && raw_path != path {
65        let mut path = raw_path.to_owned(); let (mut parts, body) = request.into_parts();
67
68        let uri_parts: uri::Parts = parts.uri.into();
69        let path_and_query = uri_parts
70            .path_and_query
71            .expect("request URI does not have `PathAndQuery`");
72
73        if let Some(query) = path_and_query.query() {
74            path.push('?');
75            path.push_str(query);
76        }
77
78        parts.uri = uri::Uri::builder()
79            .authority(uri_parts.authority.expect("request URI does not have authority set"))
80            .scheme(uri_parts.scheme.expect("request URI does not have scheme set"))
81            .path_and_query(path)
82            .build()
83            .expect("unable to construct new URI");
84
85        (parts, body)
86    } else {
87        request.into_parts()
88    };
89
90    let body = match body {
91        lambda_http::Body::Empty => hyper::Body::empty(),
92        lambda_http::Body::Text(s) => hyper::Body::from(s),
93        lambda_http::Body::Binary(v) => hyper::Body::from(v),
94    };
95
96    http::Request::from_parts(parts, body)
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use lambda_http::RequestExt;
103
104    #[test]
105    fn traits() {
106        use crate::test_helpers::*;
107
108        assert_send::<LambdaHandler<()>>();
109        assert_sync::<LambdaHandler<()>>();
110    }
111
112    #[test]
113    fn raw_http_path() {
114        let event = http::Request::builder()
116            .uri("https://id.execute-api.us-east-1.amazonaws.com/prod/resources/1")
117            .body(())
118            .expect("unable to build Request");
119        let (parts, _) = event.into_parts();
120
121        let event =
123            lambda_http::Request::from_parts(parts, lambda_http::Body::Empty).with_raw_http_path("/resources/1");
124        let request = convert_event(event);
125
126        assert_eq!(request.uri().path(), "/resources/1")
127    }
128}