Skip to content

Commit 3e37818

Browse files
authored
Expose the raw http path coming from the lambda event. (#453)
The API GW url includes th API GW stage information, so it's not easy to know what's the raw http path coming into the event. With this change, we expose the raw http path in a general way, so people that need to know the exact raw path have a common interface, regardless of where the event comes from. Signed-off-by: David Calavera <[email protected]>
1 parent 015921e commit 3e37818

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use lambda_http::{service_fn, Error, IntoResponse, Request, RequestExt};
2+
3+
#[tokio::main]
4+
async fn main() -> Result<(), Error> {
5+
lambda_http::run(service_fn(func)).await?;
6+
Ok(())
7+
}
8+
9+
async fn func(event: Request) -> Result<impl IntoResponse, Error> {
10+
let res = format!("The raw path for this request is: {}", event.raw_http_path()).into_response();
11+
12+
Ok(res)
13+
}

lambda-http/src/ext.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ pub(crate) struct PathParameters(pub(crate) QueryMap);
2020
/// These will always be empty for ALB requests
2121
pub(crate) struct StageVariables(pub(crate) QueryMap);
2222

23+
/// ALB/API gateway raw http path without any stage information
24+
pub(crate) struct RawHttpPath(pub(crate) String);
25+
2326
/// Request payload deserialization errors
2427
///
2528
/// Returned by [`RequestExt#payload()`](trait.RequestExt.html#tymethod.payload)
@@ -104,6 +107,12 @@ impl Error for PayloadError {
104107
/// }
105108
/// ```
106109
pub trait RequestExt {
110+
/// Return the raw http path for a request without any stage information.
111+
fn raw_http_path(&self) -> String;
112+
113+
/// Configures instance with the raw http path.
114+
fn with_raw_http_path(self, path: &str) -> Self;
115+
107116
/// Return pre-parsed http query string parameters, parameters
108117
/// provided after the `?` portion of a url,
109118
/// associated with the API gateway request.
@@ -177,6 +186,19 @@ pub trait RequestExt {
177186
}
178187

179188
impl RequestExt for http::Request<Body> {
189+
fn raw_http_path(&self) -> String {
190+
self.extensions()
191+
.get::<RawHttpPath>()
192+
.map(|ext| ext.0.clone())
193+
.unwrap_or_default()
194+
}
195+
196+
fn with_raw_http_path(self, path: &str) -> Self {
197+
let mut s = self;
198+
s.extensions_mut().insert(RawHttpPath(path.into()));
199+
s
200+
}
201+
180202
fn query_string_parameters(&self) -> QueryMap {
181203
self.extensions()
182204
.get::<QueryStringParameters>()
@@ -401,4 +423,10 @@ mod tests {
401423
let payload: Option<Payload> = request.payload().unwrap_or_default();
402424
assert_eq!(payload, None);
403425
}
426+
427+
#[test]
428+
fn requests_can_mock_raw_http_path_ext() {
429+
let request = Request::default().with_raw_http_path("/raw-path");
430+
assert_eq!("/raw-path", request.raw_http_path().as_str());
431+
}
404432
}

lambda-http/src/request.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Typically these are exposed via the `request_context`
44
//! request extension method provided by [lambda_http::RequestExt](../trait.RequestExt.html)
55
//!
6-
use crate::ext::{PathParameters, QueryStringParameters, StageVariables};
6+
use crate::ext::{PathParameters, QueryStringParameters, RawHttpPath, StageVariables};
77
use aws_lambda_events::alb::{AlbTargetGroupRequest, AlbTargetGroupRequestContext};
88
use aws_lambda_events::apigw::{
99
ApiGatewayProxyRequest, ApiGatewayProxyRequestContext, ApiGatewayV2httpRequest, ApiGatewayV2httpRequestContext,
@@ -61,6 +61,8 @@ pub enum RequestOrigin {
6161

6262
fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Body> {
6363
let http_method = ag.request_context.http.method.clone();
64+
let raw_path = ag.raw_path.unwrap_or_default();
65+
6466
let builder = http::Request::builder()
6567
.uri({
6668
let scheme = ag
@@ -75,7 +77,7 @@ fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Bod
7577
.or(ag.request_context.domain_name.as_deref())
7678
.unwrap_or_default();
7779

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

8183
if let Some(query) = ag.raw_query_string {
@@ -84,6 +86,7 @@ fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Bod
8486
}
8587
url
8688
})
89+
.extension(RawHttpPath(raw_path))
8790
.extension(QueryStringParameters(ag.query_string_parameters))
8891
.extension(PathParameters(QueryMap::from(ag.path_parameters)))
8992
.extension(StageVariables(QueryMap::from(ag.stage_variables)))
@@ -115,10 +118,12 @@ fn into_api_gateway_v2_request(ag: ApiGatewayV2httpRequest) -> http::Request<Bod
115118

116119
fn into_proxy_request(ag: ApiGatewayProxyRequest) -> http::Request<Body> {
117120
let http_method = ag.http_method;
121+
let raw_path = ag.path.unwrap_or_default();
122+
118123
let builder = http::Request::builder()
119124
.uri({
120125
let host = ag.headers.get(http::header::HOST).and_then(|s| s.to_str().ok());
121-
let path = apigw_path_with_stage(&ag.request_context.stage, &ag.path.unwrap_or_default());
126+
let path = apigw_path_with_stage(&ag.request_context.stage, &raw_path);
122127

123128
let mut url = match host {
124129
None => path,
@@ -141,6 +146,7 @@ fn into_proxy_request(ag: ApiGatewayProxyRequest) -> http::Request<Body> {
141146
}
142147
url
143148
})
149+
.extension(RawHttpPath(raw_path))
144150
// multi-valued query string parameters are always a super
145151
// set of singly valued query string parameters,
146152
// when present, multi-valued query string parameters are preferred
@@ -178,6 +184,8 @@ fn into_proxy_request(ag: ApiGatewayProxyRequest) -> http::Request<Body> {
178184

179185
fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {
180186
let http_method = alb.http_method;
187+
let raw_path = alb.path.unwrap_or_default();
188+
181189
let builder = http::Request::builder()
182190
.uri({
183191
let scheme = alb
@@ -191,7 +199,7 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {
191199
.and_then(|s| s.to_str().ok())
192200
.unwrap_or_default();
193201

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

203211
url
204212
})
213+
.extension(RawHttpPath(raw_path))
205214
// multi valued query string parameters are always a super
206215
// set of singly valued query string parameters,
207216
// when present, multi-valued query string parameters are preferred

0 commit comments

Comments
 (0)