From bf21d5a7a8ec9961f8a09de593b295cf9ed4aec2 Mon Sep 17 00:00:00 2001 From: Harold Sun Date: Tue, 22 Mar 2022 11:08:17 +0800 Subject: [PATCH 1/4] allow customized User_Agent for lambda runtime api client --- lambda-runtime-api-client/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lambda-runtime-api-client/README.md b/lambda-runtime-api-client/README.md index 530fefdd..2a5c9e63 100644 --- a/lambda-runtime-api-client/README.md +++ b/lambda-runtime-api-client/README.md @@ -33,3 +33,8 @@ async fn main() -> Result<(), Error> { client.call(request).await } ``` + +## Custom User Agent + +To customize User Agent header sent to Lambda Runtime API, you can configure an environment variable `LAMBDA_RUNTIME_USER_AGENT` at compiling time. +This will overide the default User Agent. \ No newline at end of file From c696de2e6822b862ad7d48436365d97e0770b7ba Mon Sep 17 00:00:00 2001 From: Harold Sun Date: Tue, 11 Oct 2022 19:38:40 +0800 Subject: [PATCH 2/4] decode ALB query strings --- lambda-http/Cargo.toml | 1 + lambda-http/src/request.rs | 68 +++++++++++++++++-- ...alue_request_encoded_query_parameters.json | 37 ++++++++++ .../alb_request_encoded_query_parameters.json | 24 +++++++ 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 lambda-http/tests/data/alb_multi_value_request_encoded_query_parameters.json create mode 100644 lambda-http/tests/data/alb_request_encoded_query_parameters.json diff --git a/lambda-http/Cargo.toml b/lambda-http/Cargo.toml index d9a85484..d4b6167a 100644 --- a/lambda-http/Cargo.toml +++ b/lambda-http/Cargo.toml @@ -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" diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index 27d54cf2..8afbe545 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -21,6 +21,7 @@ use serde_json::error::Error as JsonError; use std::future::Future; use std::pin::Pin; use std::{io::Read, mem}; +use std::str::FromStr; use url::Url; /// Internal representation of an Lambda http event from @@ -201,22 +202,25 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request { 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)); @@ -243,6 +247,12 @@ fn into_alb_request(alb: AlbTargetGroupRequest) -> http::Request { 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 { let http_method = ag.http_method; @@ -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 @@ -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 diff --git a/lambda-http/tests/data/alb_multi_value_request_encoded_query_parameters.json b/lambda-http/tests/data/alb_multi_value_request_encoded_query_parameters.json new file mode 100644 index 00000000..246e1de8 --- /dev/null +++ b/lambda-http/tests/data/alb_multi_value_request_encoded_query_parameters.json @@ -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" +} \ No newline at end of file diff --git a/lambda-http/tests/data/alb_request_encoded_query_parameters.json b/lambda-http/tests/data/alb_request_encoded_query_parameters.json new file mode 100644 index 00000000..d8e1b452 --- /dev/null +++ b/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" +} \ No newline at end of file From ac59cfba18dfd6aa7bf36fed1c48a7fb3f030409 Mon Sep 17 00:00:00 2001 From: Harold Sun Date: Tue, 11 Oct 2022 19:44:26 +0800 Subject: [PATCH 3/4] remove un-necessary updates to README for lambda-api-client --- lambda-runtime-api-client/README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lambda-runtime-api-client/README.md b/lambda-runtime-api-client/README.md index 2a5c9e63..2251cbc7 100644 --- a/lambda-runtime-api-client/README.md +++ b/lambda-runtime-api-client/README.md @@ -32,9 +32,4 @@ async fn main() -> Result<(), Error> { client.call(request).await } -``` - -## Custom User Agent - -To customize User Agent header sent to Lambda Runtime API, you can configure an environment variable `LAMBDA_RUNTIME_USER_AGENT` at compiling time. -This will overide the default User Agent. \ No newline at end of file +``` \ No newline at end of file From cfaa5f8ad44b9d0aa3a65e37dd7e947e9ac25452 Mon Sep 17 00:00:00 2001 From: Harold Sun Date: Tue, 11 Oct 2022 19:48:48 +0800 Subject: [PATCH 4/4] fix fmt issue --- lambda-http/src/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index 8afbe545..978c3b0c 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -20,8 +20,8 @@ use serde::Deserialize; use serde_json::error::Error as JsonError; use std::future::Future; use std::pin::Pin; -use std::{io::Read, mem}; use std::str::FromStr; +use std::{io::Read, mem}; use url::Url; /// Internal representation of an Lambda http event from