Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lambda-http/examples/hello-raw-http-path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use lambda_http::{service_fn, Error, IntoResponse, Request, RequestExt};

#[tokio::main]
async fn main() -> Result<(), Error> {
lambda_http::run(service_fn(func)).await?;
Ok(())
}

async fn func(event: Request) -> Result<impl IntoResponse, Error> {
let res = format!("The raw path for this request is: {}", event.raw_http_path()).into_response();

Ok(res)
}
28 changes: 28 additions & 0 deletions lambda-http/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub(crate) struct PathParameters(pub(crate) QueryMap);
/// These will always be empty for ALB requests
pub(crate) struct StageVariables(pub(crate) QueryMap);

/// ALB/API gateway raw http path without any stage information
pub(crate) struct RawHttpPath(pub(crate) String);

/// Request payload deserialization errors
///
/// Returned by [`RequestExt#payload()`](trait.RequestExt.html#tymethod.payload)
Expand Down Expand Up @@ -104,6 +107,12 @@ impl Error for PayloadError {
/// }
/// ```
pub trait RequestExt {
/// Return the raw http path for a request without any stage information.
fn raw_http_path(&self) -> String;

/// Configures instance with the raw http path.
fn with_raw_http_path(self, path: &str) -> Self;

/// Return pre-parsed http query string parameters, parameters
/// provided after the `?` portion of a url,
/// associated with the API gateway request.
Expand Down Expand Up @@ -177,6 +186,19 @@ pub trait RequestExt {
}

impl RequestExt for http::Request<Body> {
fn raw_http_path(&self) -> String {
self.extensions()
.get::<RawHttpPath>()
.map(|ext| ext.0.clone())
.unwrap_or_default()
}

fn with_raw_http_path(self, path: &str) -> Self {
let mut s = self;
s.extensions_mut().insert(RawHttpPath(path.into()));
s
}

fn query_string_parameters(&self) -> QueryMap {
self.extensions()
.get::<QueryStringParameters>()
Expand Down Expand Up @@ -401,4 +423,10 @@ mod tests {
let payload: Option<Payload> = request.payload().unwrap_or_default();
assert_eq!(payload, None);
}

#[test]
fn requests_can_mock_raw_http_path_ext() {
let request = Request::default().with_raw_http_path("/raw-path");
assert_eq!("/raw-path", request.raw_http_path().as_str());
}
}
17 changes: 13 additions & 4 deletions lambda-http/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Typically these are exposed via the `request_context`
//! request extension method provided by [lambda_http::RequestExt](../trait.RequestExt.html)
//!
use crate::ext::{PathParameters, QueryStringParameters, StageVariables};
use crate::ext::{PathParameters, QueryStringParameters, RawHttpPath, StageVariables};
use aws_lambda_events::alb::{AlbTargetGroupRequest, AlbTargetGroupRequestContext};
use aws_lambda_events::apigw::{
ApiGatewayProxyRequest, ApiGatewayProxyRequestContext, ApiGatewayV2httpRequest, ApiGatewayV2httpRequestContext,
Expand Down Expand Up @@ -61,6 +61,8 @@ pub enum RequestOrigin {

fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Body> {
let http_method = ag.request_context.http.method.clone();
let raw_path = ag.raw_path.unwrap_or_default();

let builder = http::Request::builder()
.uri({
let scheme = ag
Expand All @@ -75,7 +77,7 @@ fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Bod
.or(ag.request_context.domain_name.as_deref())
.unwrap_or_default();

let path = apigw_path_with_stage(&ag.request_context.stage, ag.raw_path.as_deref().unwrap_or_default());
let path = apigw_path_with_stage(&ag.request_context.stage, &raw_path);
let mut url = format!("{}://{}{}", scheme, host, path);

if let Some(query) = ag.raw_query_string {
Expand All @@ -84,6 +86,7 @@ fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Bod
}
url
})
.extension(RawHttpPath(raw_path))
.extension(QueryStringParameters(ag.query_string_parameters))
.extension(PathParameters(QueryMap::from(ag.path_parameters)))
.extension(StageVariables(QueryMap::from(ag.stage_variables)))
Expand Down Expand Up @@ -115,10 +118,12 @@ fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Bod

fn into_proxy_request(ag: ApiGatewayProxyRequest) -> http::Request<Body> {
let http_method = ag.http_method;
let raw_path = ag.path.unwrap_or_default();

let builder = http::Request::builder()
.uri({
let host = ag.headers.get(http::header::HOST).and_then(|s| s.to_str().ok());
let path = apigw_path_with_stage(&ag.request_context.stage, &ag.path.unwrap_or_default());
let path = apigw_path_with_stage(&ag.request_context.stage, &raw_path);

let mut url = match host {
None => path,
Expand All @@ -141,6 +146,7 @@ fn into_proxy_request(ag: ApiGatewayProxyRequest) -> http::Request<Body> {
}
url
})
.extension(RawHttpPath(raw_path))
// multi-valued query string parameters are always a super
// set of singly valued query string parameters,
// when present, multi-valued query string parameters are preferred
Expand Down Expand Up @@ -178,6 +184,8 @@ fn into_proxy_request(ag: ApiGatewayProxyRequest) -> http::Request<Body> {

fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {
let http_method = alb.http_method;
let raw_path = alb.path.unwrap_or_default();

let builder = http::Request::builder()
.uri({
let scheme = alb
Expand All @@ -191,7 +199,7 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {
.and_then(|s| s.to_str().ok())
.unwrap_or_default();

let mut url = format!("{}://{}{}", scheme, host, alb.path.unwrap_or_default());
let mut url = format!("{}://{}{}", scheme, host, &raw_path);
if !alb.multi_value_query_string_parameters.is_empty() {
url.push('?');
url.push_str(&alb.multi_value_query_string_parameters.to_query_string());
Expand All @@ -202,6 +210,7 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {

url
})
.extension(RawHttpPath(raw_path))
// multi valued query string parameters are always a super
// set of singly valued query string parameters,
// when present, multi-valued query string parameters are preferred
Expand Down