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

Support for Lambda Function URLs #460

Open
deki opened this issue Apr 8, 2022 · 16 comments
Open

Support for Lambda Function URLs #460

deki opened this issue Apr 8, 2022 · 16 comments
Assignees

Comments

@deki
Copy link
Collaborator

deki commented Apr 8, 2022

Recently Lambda Function URLs were released: https://aws.amazon.com/blogs/aws/announcing-aws-lambda-function-urls-built-in-https-endpoints-for-single-function-microservices/

Those requests fail with:
com.amazonaws.serverless.exceptions.InvalidRequestEventException: The incoming event is not a valid request from Amazon API Gateway or an Application Load Balancer

@msailes
Copy link

msailes commented Apr 8, 2022

In AwsProxyHttpServletRequestReader

https://github.com/awslabs/aws-serverless-java-container/blob/6d88f503c02988cc947743bad78d965d8aa0b5a2/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReader.java#L47-L49

The condition fails because when a request from a function url is mapped to a AwsProxyRequest type getHttpMethod() returns null.

@deki deki added the docs label Apr 9, 2022
@deki
Copy link
Collaborator Author

deki commented Apr 9, 2022

HttpApiV2ProxyHandler along with HttpApiV2ProxyRequest needs to be used, than it works fine. Will change docs and samples.

@deki deki self-assigned this Apr 9, 2022
@msailes
Copy link

msailes commented Apr 9, 2022

If I use the example request from the developer guide

{
// removed for brevity
  "requestContext": {
    "accountId": "123456789012",
    "apiId": "<urlid>",
    "authentication": null,
    "authorizer": {
        "iam": {
                "accessKey": "AKIA...",
                "accountId": "111122223333",
                "callerId": "AIDA...",
                "cognitoIdentity": null,
                "principalOrgId": null,
                "userArn": "arn:aws:iam::111122223333:user/example-user",
                "userId": "AIDA..."
        }
    },
    "domainName": "<url-id>.lambda-url.us-west-2.on.aws",
    "domainPrefix": "<url-id>",
    "http": {
      "method": "POST",
      "path": "/my/path",
      "protocol": "HTTP/1.1",
      "sourceIp": "123.123.123.123",
      "userAgent": "agent"
    },
// removed for brevity
}

Then I get the following error and a 502 http response.

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "authentication" (class com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequestContext), not marked as ignorable (11 known properties: "apiId", "domainPrefix", "accountId", "authorizer", "requestId", "timeEpoch", "time", "stage", "domainName", "routeKey", "http"])
 at [Source: (ByteArrayInputStream); line: 21, column: 27] (through reference chain: com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest["requestContext"]->com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequestContext["authentication"])

This is because HttpApiV2ProxyRequestContext is not ignoring fields that it doesn't recognise, in this case authentication.

To fix this we could add @JsonIgnoreProperties(ignoreUnknown = true) to the class so that Jackson ignores fields it doesn't recognise.

@VPatrny
Copy link

VPatrny commented Apr 27, 2022

The fix mentioned above works for me. Thx!

public class StreamLambdaHandler implements RequestStreamHandler {
//    private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    private static SpringBootLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse> handler;

    static {
        try {
//            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
            handler = SpringBootLambdaContainerHandler.getHttpApiV2ProxyHandler(Application.class);

deki added a commit that referenced this issue Apr 27, 2022
…id UnrecognizedPropertyException for Lambda Function URLs with IAM auth (#460)
@astsiatsko
Copy link

Hey guys, thanks for this change. But is it possible to have ONE handler for all types of events? ALB, API_GW, functional url and so on? So far I see the ways how handlers are initialized are way different and it seems that I have to keep 2X resources in lambda to keep 2 types of handlers in memory - one for ALB, API_GW and another for FURL. Is there a way to share a single handler for all types of events?

@deki
Copy link
Collaborator Author

deki commented Jan 25, 2024

@astsiatsko which framework are you using?

For Spring Boot 3 you have the possibility to use the new SpringDelegatingLambdaContainerHandler as described in https://github.com/aws/serverless-java-container/tree/main/samples/springboot3/alt-pet-store. This one supports both payload versions. More docs on this coming soon...

@astsiatsko
Copy link

@deki thanks! I am using spring boot 3.1.x. Thank you, I will give it a try and let you know asap.

@astsiatsko
Copy link

astsiatsko commented Jan 25, 2024

@deki One question - may I do sth like that?
SpringDelegatingLambdaContainerHandler.getContainerConfig().setInitializationTimeout(timeout);

@deki
Copy link
Collaborator Author

deki commented Jan 25, 2024

This is currently not implemented for this handler type. Please open a new issue describing your requirement and someone will look into it.

@astsiatsko
Copy link

astsiatsko commented Jan 25, 2024

Hey guys. Sorry for asking but are you sure your approach works for complex urls with query and path parameters? I use SpringDelegatingLambdaContainerHandler in complex enterprise lambda function and on

mvc.service(httpServletRequest, httpServletResponse);

I constantly get error 400 and /error context path. I copied SpringDelegatingLambdaContainerHandler to the same package in my app and added logs. In my query I have two query params and path param. In aws logs I see it starts spring boot but I always get 400 and /error in logs instead of my url. I use ALB event, but it doesn't work for others either. Can you help please?

@astsiatsko
Copy link

astsiatsko commented Jan 25, 2024

My endpoint signature:

@RequestMapping(path = "/w6/test/{uuid}/refresh", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<TMaxmindResponse> refresh(
            @Parameter(description = "UUID of the order") @PathVariable("uuid") String uuid,
            @Parameter(description = "ID of the order(primary key)") @RequestParam("order_id") Long orderId,
            @Parameter(description = "tab") @RequestParam("tab") String tab
    ) 

@astsiatsko
Copy link

astsiatsko commented Jan 26, 2024

Hey guys. I think you have a problem with query parameters procession - I changed all my parameters to path params and it worked just fine.
I debugged it in AWS Lambda for 2 days and gave it up, decided to move to path variables. But please, fix that or let me know what I am doing wrong. Thanks

@astsiatsko
Copy link

Here is the stack trace I have. May be it helps

jakarta.servlet.ServletException: Request processing failed: java.lang.NullPointerException: Cannot read the array length because \"\u003cparameter1\u003e\" is null\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1019)\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:527)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc$ProxyFilterChain$ServletFilterProxy.doFilter(ProxyMvc.java:280)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc$ProxyFilterChain.doFilter(ProxyMvc.java:233)\n\tat org.springframework.web.filter.AbstractRequestLoggingFilter.doFilterInternal(AbstractRequestLoggingFilter.java:289)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc$ProxyFilterChain.doFilter(ProxyMvc.java:233)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc$ProxyFilterChain.doFilter(ProxyMvc.java:233)\n\tat org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc$ProxyFilterChain.doFilter(ProxyMvc.java:233)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc.service(ProxyMvc.java:144)\n\tat org.springframework.cloud.function.serverless.web.ProxyMvc.service(ProxyMvc.java:138)\n\tat com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler.handleRequest(SpringDelegatingLambdaContainerHandler.java:74)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)\n\tat java.base/java.lang.reflect.Method.invoke(Unknown Source)\n\tat com.amazonaws.services.lambda.runtime.api.client.EventHandlerLoader$StreamMethodRequestHandler.handleRequest(EventHandlerLoader.java:378)\n\tat com.amazonaws.services.lambda.runtime.api.client.EventHandlerLoader$2.call(EventHandlerLoader.java:905)\n\tat com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:245)\n\tat com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:197)\n\tat com.amazonaws.services.lambda.runtime.api.client.AWSLambda.main(AWSLambda.java:187)\nCaused by: java.lang.NullPointerException: Cannot read the array length because \"\u003cparameter1\u003e\" is null\n\tat java.base/java.io.ByteArrayInputStream.\u003cinit\u003e(Unknown Source)\n\tat org.springframework.cloud.function.serverless.web.ProxyHttpServletRequest.getInputStream(ProxyHttpServletRequest.java:280)\n\tat org.springframework.web.util.ContentCachingRequestWrapper.getInputStream(ContentCachingRequestWrapper.java:98)\n\tat org.springframework.http.server.ServletServerHttpRequest.getBody(ServletServerHttpRequest.java:206)\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver$EmptyBodyCheckingHttpInputMessage.\u003cinit\u003e(AbstractMessageConverterMethodArgumentResolver.java:323)\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:172)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:163)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:136)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:181)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:148)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)\n\t... 27 more\n","timestamp":"2024-01-26 18:07:19.251"}

@deki
Copy link
Collaborator Author

deki commented Jan 30, 2024

@astsiatsko good catch, would you mind opening a new issue for it? This is unrelated to Lambda Function URLs. We'll include a fix in the next release.

@astsiatsko
Copy link

@deki Thank you
I submitted #754

@olegz
Copy link
Collaborator

olegz commented Jan 30, 2024

@deki you can assign it to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants