-
Notifications
You must be signed in to change notification settings - Fork 196
/
Copy pathlambda_handler.rs
120 lines (100 loc) · 3.46 KB
/
lambda_handler.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use http::uri;
use lambda_http::{Request, RequestExt};
use std::{
fmt::Debug,
task::{Context, Poll},
};
use tower::Service;
type HyperRequest = http::Request<hyper::Body>;
/// A [`Service`] that takes a `lambda_http::Request` and converts
/// it to `http::Request<hyper::Body>`.
///
/// [`Service`]: tower::Service
#[derive(Debug, Clone)]
pub struct LambdaHandler<S> {
service: S,
}
impl<S> LambdaHandler<S> {
pub fn new(service: S) -> Self {
Self { service }
}
}
impl<S> Service<Request> for LambdaHandler<S>
where
S: Service<HyperRequest>,
{
type Error = S::Error;
type Response = S::Response;
type Future = S::Future;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, event: Request) -> Self::Future {
self.service.call(convert_event(event))
}
}
/// Converts a `lambda_http::Request` into a `http::Request<hyper::Body>`
/// Issue: <https://github.com/awslabs/smithy-rs/issues/1125>
///
/// While converting the event the [API Gateway Stage] portion of the URI
/// is removed from the uri that gets returned as a new `http::Request`.
///
/// [API Gateway Stage]: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-stages.html
fn convert_event(request: Request) -> HyperRequest {
let raw_path = request.raw_http_path();
let (mut parts, body) = request.into_parts();
let mut path = String::from(parts.uri.path());
if !raw_path.is_empty() && raw_path != path {
path = raw_path;
let uri_parts: uri::Parts = parts.uri.into();
let path_and_query = uri_parts
.path_and_query
.expect("request URI does not have `PathAndQuery`");
if let Some(query) = path_and_query.query() {
path.push('?');
path.push_str(query);
}
parts.uri = uri::Uri::builder()
.authority(uri_parts.authority.expect("request URI does not have authority set"))
.scheme(uri_parts.scheme.expect("request URI does not have scheme set"))
.path_and_query(path)
.build()
.expect("unable to construct new URI");
}
let body = match body {
lambda_http::Body::Empty => hyper::Body::empty(),
lambda_http::Body::Text(s) => hyper::Body::from(s),
lambda_http::Body::Binary(v) => hyper::Body::from(v),
};
http::Request::from_parts(parts, body)
}
#[cfg(test)]
mod tests {
use super::*;
use lambda_http::RequestExt;
#[test]
fn traits() {
use crate::test_helpers::*;
assert_send::<LambdaHandler<()>>();
assert_sync::<LambdaHandler<()>>();
}
#[test]
fn raw_http_path() {
// lambda_http::Request doesn't have a fn `builder`
let event = http::Request::builder()
.uri("https://id.execute-api.us-east-1.amazonaws.com/prod/resources/1")
.body(())
.expect("unable to build Request");
let (parts, _) = event.into_parts();
// the lambda event will have a raw path which is the path without stage name in it
let event =
lambda_http::Request::from_parts(parts, lambda_http::Body::Empty).with_raw_http_path("/resources/1");
let request = convert_event(event);
assert_eq!(request.uri().path(), "/resources/1")
}
}