Skip to content

Commit

Permalink
Decode query paramters for ALB event source (#545)
Browse files Browse the repository at this point in the history
* decode ALB query strings
  • Loading branch information
bnusunny committed Oct 11, 2022
1 parent 45211b0 commit 2201109
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 5 deletions.
1 change: 1 addition & 0 deletions lambda-http/Cargo.toml
Expand Up @@ -36,6 +36,7 @@ query_map = { version = "0.5", features = ["url-query"] }
mime = "0.3.16"
encoding_rs = "0.8.31"
url = "2.2.2"
percent-encoding = "2.2.0"

[dependencies.aws_lambda_events]
version = "^0.6.3"
Expand Down
68 changes: 64 additions & 4 deletions lambda-http/src/request.rs
Expand Up @@ -20,6 +20,7 @@ use serde::Deserialize;
use serde_json::error::Error as JsonError;
use std::future::Future;
use std::pin::Pin;
use std::str::FromStr;
use std::{io::Read, mem};
use url::Url;

Expand Down Expand Up @@ -201,22 +202,25 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {
let host = alb.headers.get(http::header::HOST).and_then(|s| s.to_str().ok());
let raw_path = alb.path.unwrap_or_default();

let query_string_parameters = decode_query_map(alb.query_string_parameters);
let multi_value_query_string_parameters = decode_query_map(alb.multi_value_query_string_parameters);

let builder = http::Request::builder()
.uri(build_request_uri(
&raw_path,
&alb.headers,
host,
Some((&alb.multi_value_query_string_parameters, &alb.query_string_parameters)),
Some((&multi_value_query_string_parameters, &query_string_parameters)),
))
.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
.extension(QueryStringParameters(
if alb.multi_value_query_string_parameters.is_empty() {
alb.query_string_parameters
if multi_value_query_string_parameters.is_empty() {
query_string_parameters
} else {
alb.multi_value_query_string_parameters
multi_value_query_string_parameters
},
))
.extension(RequestContext::Alb(alb.request_context));
Expand All @@ -243,6 +247,12 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request<Body> {
req
}

fn decode_query_map(query_map: QueryMap) -> QueryMap {
let query_string = query_map.to_query_string();
let decoded = percent_encoding::percent_decode(query_string.as_bytes()).decode_utf8_lossy();
QueryMap::from_str(&decoded).unwrap_or_default()
}

#[cfg(feature = "apigw_websockets")]
fn into_websocket_request(ag: ApiGatewayWebsocketProxyRequest) -> http::Request<Body> {
let http_method = ag.http_method;
Expand Down Expand Up @@ -548,6 +558,34 @@ mod tests {
);
}

#[test]
fn deserializes_alb_request_encoded_query_parameters_events() {
// from the docs
// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers
let input = include_str!("../tests/data/alb_request_encoded_query_parameters.json");
let result = from_str(input);
assert!(
result.is_ok(),
"event was not parsed as expected {:?} given {}",
result,
input
);
let req = result.expect("failed to parse request");
assert_eq!(req.method(), "GET");
assert_eq!(
req.uri(),
"https://lambda-846800462-us-east-2.elb.amazonaws.com/?myKey=%3FshowAll%3Dtrue"
);

// Ensure this is an ALB request
let req_context = req.request_context();
assert!(
matches!(req_context, RequestContext::Alb(_)),
"expected Alb context, got {:?}",
req_context
);
}

#[test]
fn deserializes_apigw_multi_value_request_events() {
// from docs
Expand Down Expand Up @@ -593,6 +631,28 @@ mod tests {
);
}

#[test]
fn deserializes_alb_multi_value_request_encoded_query_parameters_events() {
// from docs
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
let input = include_str!("../tests/data/alb_multi_value_request_encoded_query_parameters.json");
let result = from_str(input);
assert!(
result.is_ok(),
"event is was not parsed as expected {:?} given {}",
result,
input
);
let request = result.expect("failed to parse request");
assert!(!request.query_string_parameters().is_empty());

// test RequestExt#query_string_parameters does the right thing
assert_eq!(
request.query_string_parameters().all("myKey"),
Some(vec!["?showAll=true", "?showAll=false"])
);
}

#[test]
fn deserialize_apigw_http_sam_local() {
// manually generated from AWS SAM CLI
Expand Down
@@ -0,0 +1,37 @@
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09"
}
},
"httpMethod": "GET",
"path": "/",
"queryStringParameters": { "myKey": "%3FshowAll%3Dtrue" },
"multiValueQueryStringParameters": { "myKey": ["%3FshowAll%3Dtrue", "%3FshowAll%3Dfalse"] },
"headers": {
"accept": "text/html,application/xhtml+xml",
"accept-language": "en-US,en;q=0.8",
"content-type": "text/plain",
"cookie": "name1=value1",
"host": "lambda-846800462-us-east-2.elb.amazonaws.com",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)",
"x-amzn-trace-id": "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520",
"x-forwarded-for": "72.21.198.66",
"x-forwarded-port": "443",
"x-forwarded-proto": "https"
},
"multiValueHeaders": {
"accept": ["text/html,application/xhtml+xml"],
"accept-language": ["en-US,en;q=0.8"],
"content-type": ["text/plain"],
"cookie": ["name1=value1", "name2=value2"],
"host": ["lambda-846800462-us-east-2.elb.amazonaws.com"],
"user-agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)"],
"x-amzn-trace-id": ["Root=1-5bdb40ca-556d8b0c50dc66f0511bf520"],
"x-forwarded-for": ["72.21.198.66"],
"x-forwarded-port": ["443"],
"x-forwarded-proto": ["https"]
},
"isBase64Encoded": false,
"body": "request_body"
}
24 changes: 24 additions & 0 deletions lambda-http/tests/data/alb_request_encoded_query_parameters.json
@@ -0,0 +1,24 @@
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09"
}
},
"httpMethod": "GET",
"path": "/",
"queryStringParameters": { "myKey": "%3FshowAll%3Dtrue"},
"headers": {
"accept": "text/html,application/xhtml+xml",
"accept-language": "en-US,en;q=0.8",
"content-type": "text/plain",
"cookie": "cookies",
"host": "lambda-846800462-us-east-2.elb.amazonaws.com",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)",
"x-amzn-trace-id": "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520",
"x-forwarded-for": "72.21.198.66",
"x-forwarded-port": "443",
"x-forwarded-proto": "https"
},
"isBase64Encoded": false,
"body": "request_body"
}
2 changes: 1 addition & 1 deletion lambda-runtime-api-client/README.md
Expand Up @@ -32,4 +32,4 @@ async fn main() -> Result<(), Error> {
client.call(request).await
}
```
```

0 comments on commit 2201109

Please sign in to comment.