rocket_lamb 0.6.0

A crate to allow running a Rocket webserver as an AWS Lambda Function with API Gateway
Documentation
use http::header::HOST;
use lambda_http::request::RequestContext;
use lambda_http::{Request, RequestExt as _};

pub(crate) trait RequestExt {
    fn full_path(&self) -> String;

    fn base_path(&self) -> String;

    fn api_path(&self) -> &str;
}

impl RequestExt for Request {
    fn full_path(&self) -> String {
        if self.request_context().is_alb() || !is_default_api_gateway_url(self) {
            self.uri().path().to_owned()
        } else {
            let mut path = self.base_path();
            path.push_str(self.uri().path());
            path
        }
    }

    fn base_path(&self) -> String {
        match self.request_context() {
            RequestContext::ApiGateway {
                stage,
                resource_path,
                ..
            } => {
                if is_default_api_gateway_url(self) {
                    format!("/{}", stage)
                } else {
                    let resource_path = populate_resource_path(self, resource_path);
                    let full_path = self.uri().path();
                    let resource_path_index =
                        full_path.rfind(&resource_path).unwrap_or_else(|| {
                            panic!(
                                "Could not find segment '{}' in path '{}'.",
                                resource_path, full_path
                            )
                        });
                    full_path[..resource_path_index].to_owned()
                }
            }
            RequestContext::Alb { .. } => String::new(),
        }
    }

    fn api_path(&self) -> &str {
        if self.request_context().is_alb() || is_default_api_gateway_url(self) {
            self.uri().path()
        } else {
            &self.uri().path()[self.base_path().len()..]
        }
    }
}

fn is_default_api_gateway_url(req: &Request) -> bool {
    req.headers()
        .get(HOST)
        .and_then(|h| h.to_str().ok())
        .map(|h| h.ends_with(".amazonaws.com") && h.contains(".execute-api."))
        .unwrap_or(false)
}

fn populate_resource_path(req: &Request, resource_path: String) -> String {
    let path_parameters = req.path_parameters();
    resource_path
        .split('/')
        .map(|segment| {
            if segment.starts_with('{') {
                let end = if segment.ends_with("+}") { 2 } else { 1 };
                let param = &segment[1..segment.len() - end];
                path_parameters
                    .get(param)
                    .unwrap_or_else(|| panic!("Could not find path parameter '{}'.", param))
            } else {
                segment
            }
        })
        .collect::<Vec<&str>>()
        .join("/")
}