Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ApiGatewayHttpApiV2ProxyRequestTranslator and ApiGatewayProxyRequestTranslator #1901

Open
wants to merge 10 commits into
base: asmarp/api-gateway-emulator-skeleton
Choose a base branch
from

Conversation

gcbeattyAWS
Copy link

@gcbeattyAWS gcbeattyAWS commented Dec 9, 2024

DOTNET-7838

Description of changes:

  1. Add functions to translate httpcontext into APIGatewayHttpApiV2ProxyRequest and APIGatewayProxyRequest
  2. Add unit tests for above classes

Testing details

  1. Wrote unit tests
  2. Manually deployed a lambda into my aws account, and wrote a console app that uses my request translator and invokes the function. validated that the request fields seem to be sent correctly

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@gcbeattyAWS gcbeattyAWS requested a review from philasmar December 9, 2024 15:17
@gcbeattyAWS gcbeattyAWS marked this pull request as ready for review December 9, 2024 15:17
@gcbeattyAWS gcbeattyAWS requested a review from normj December 9, 2024 15:45
@gcbeattyAWS gcbeattyAWS added the Release Not Needed Add this label if a PR does not need to be released. label Dec 10, 2024
@gcbeattyAWS gcbeattyAWS marked this pull request as draft December 10, 2024 19:23
@gcbeattyAWS gcbeattyAWS changed the base branch from feature/lambdatesttool-v2 to asmarp/api-gateway-emulator-skeleton December 10, 2024 19:31
@gcbeattyAWS gcbeattyAWS marked this pull request as ready for review December 10, 2024 19:37
Copy link
Member

@normj normj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any tests for special characters in headers, resource paths or query strings. Have we done a comparison yet with API Gateway? We should have tests for those cases. That includes the RawPath property as well.

@gcbeattyAWS
Copy link
Author

gcbeattyAWS commented Dec 11, 2024

I don't see any tests for special characters in headers, resource paths or query strings. Have we done a comparison yet with API Gateway? We should have tests for those cases. That includes the RawPath property as well.

so i did some tests of checking the existing functionality of api gateway in production, with special characters in the header by

  1. deploying an api gateway
  2. deploying a lambda
  3. attaching the lambda to the api gatway
  4. invoking the api gateway via command line (curl), and sending a non url encoded header value of special+++
  5. logging the event (apigatewayrequest) object in the lambda itself, and it prints out special+++ (not encoded), so i assumed no encoding is required for that - but let me double check on this and get back to you

if (string.IsNullOrEmpty(contentType))
return false;

return contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this list of content types we use for response in the ASP.NET Core bridge library. https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.AspNetCoreServer/AbstractAspNetCoreFunction.cs#L59

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also think about how this can be configurable. Probably via environment variables that we can have users configure in Aspire.

Headers = headers,
QueryStringParameters = queryStringParameters,
PathParameters = pathParameters ?? new Dictionary<string, string>(),
Body = HttpRequestUtility.ReadRequestBody(request),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you take note that when we get these ToAPIGatewayXXX() methods hooked up to the main code path that is asking for the APIGatewayHttpApiV2ProxyRequest. When we convert the APIGatewayHttpApiV2ProxyRequest to the MemoryStream to Lambda we should check the size of the stream is no more then 6MB. If so throw back the same error that users would get from API Gateway.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i will connect with phil on where he wants to do this logic at

MultiValueHeaders = multiValueHeaders,
QueryStringParameters = queryStringParameters,
MultiValueQueryStringParameters = multiValueQueryStringParameters,
PathParameters = pathParameters ?? new Dictionary<string, string>(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we initializing this to an empty collection if we don't have any. Is this what API Gateway does?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. i just did a test
Invoke-WebRequest -Uri https://myapi-gateway-url.amazonaws.com/testfunction -Method GET
where it echos the api gateway request object and i see the value is null when there are no path parameters.

pathParameters: null,
  stageVariables: null,
  body: null,
  isBase64Encoded: false
}

i will update the code

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ill also double check the query string and header default values

Copy link
Author

@gcbeattyAWS gcbeattyAWS Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ive updated the logic to handle this now


foreach (var header in headers)
{
singleValueHeaders[header.Key] = header.Value.Last() ?? "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My memory for API Gateway is that in HTTP V2 where there is just a single headers collection the headers are comma delimited. But we need to verify that. I'm sure it isn't just get the last value.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah you are right on this https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html

Format 2.0 doesn't have multiValueHeaders or multiValueQueryStringParameters fields. Duplicate headers are combined with commas and included in the headers field. Duplicate query strings are combined with commas and included in the queryStringParameters field.

i think i got confused when reading the other docs for v1 https://aws.amazon.com/blogs/compute/support-for-multi-value-parameters-in-amazon-api-gateway/

Before this change, API Gateway used to retain only the last value and drop everything else for a multi-valued parameter. You can see the original behavior in the queryStringParameters parameter in the above input, where only the “fish” value is retained.

I will update accordingly

Copy link
Author

@gcbeattyAWS gcbeattyAWS Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ive updated the logic to handle this now. this logic will still just return the dicts as-is but then i have the caller perform the comma separated merging.


foreach (var param in query)
{
singleValueParams[param.Key] = param.Value.Last() ?? "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above for ExtractHeaders

Copy link
Author

@gcbeattyAWS gcbeattyAWS Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ive updated the logic to handle this now

@normj
Copy link
Member

normj commented Dec 11, 2024

4. invoking the api gateway via command line (curl), and sending a non url encoded header value of special+++

@gcbeattyAWS Also check more characters then + like spaces and other white space and Unicode characters. resource path and query strings can also be special. Especially when you through /, & and `?' in the values for resource path segments and query string values. In the ASP.NET Core Lambda bridge library where I have to do all of this in reverse I had to do some encoding handling.

internal static string CreateQueryStringParameters(IDictionary<string, string> singleValues, IDictionary<string, IList<string>> multiValues, bool urlEncodeValue)

@gcbeattyAWS
Copy link
Author

gcbeattyAWS commented Dec 12, 2024

  1. invoking the api gateway via command line (curl), and sending a non url encoded header value of special+++

@gcbeattyAWS Also check more characters then + like spaces and other white space and Unicode characters. resource path and query strings can also be special. Especially when you through /, & and `?' in the values for resource path segments and query string values. In the ASP.NET Core Lambda bridge library where I have to do all of this in reverse I had to do some encoding handling.

internal static string CreateQueryStringParameters(IDictionary<string, string> singleValues, IDictionary<string, IList<string>> multiValues, bool urlEncodeValue)

so i have updated the logic based on my findings:1

  1. For query strings. QueryStringParameters should be the decoded values (this already happens automatically when accessing Request.QueryString. RawQueryString should be the encoded query string without the ?.
  2. Headers do not seem to have any additional logic of decoding that i have seen. they are just a pass-through to lambda.
  3. The path and path parameters should be decoded (this is also happening automatically now by accessing via request.Path)
  4. The api gatway cannot handle %2f in the urls ("/") character

i have added test cases to check all of these scenarios

@gcbeattyAWS gcbeattyAWS requested a review from normj December 12, 2024 17:15

if (request.Cookies.Any())
{
httpApiV2ProxyRequest.Cookies = request.Cookies.Select(c => $"{c.Key}={c.Value}").ToArray();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headers and cookies are just sent as-is (from my testing)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release Not Needed Add this label if a PR does not need to be released.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants