use http::uri;
use lambda_http::{Request, RequestExt};
use std::{
fmt::Debug,
task::{Context, Poll},
};
use tower::Service;
type HyperRequest = http::Request<hyper::Body>;
#[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))
}
}
fn convert_event(request: Request) -> HyperRequest {
let raw_path: &str = request.extensions().raw_http_path();
let path: &str = request.uri().path();
let (parts, body) = if !raw_path.is_empty() && raw_path != path {
let mut path = raw_path.to_owned(); let (mut parts, body) = request.into_parts();
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");
(parts, body)
} else {
request.into_parts()
};
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() {
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();
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")
}
}