serverless-java-container icon indicating copy to clipboard operation
serverless-java-container copied to clipboard

Provide several Custom Domains and base paths

Open feinstein opened this issue 4 years ago • 5 comments

  • Framework version: 1.3.2
  • Implementations: Jersey

I have several different systems running my serverless container:

  1. API Gateway + Lambda with Custom Domain Name at https://api.myproject.com/v1/users/12345 custom domain: api.myproject.com base path: v1/users

  2. API Gateway + Lambda with stage path (for testing development releases): https://ixov38idvk.execute-api.us-east-1.amazonaws.com/dev/users/12345 base path: users

  3. SAM Local at http://127.0.0.1:3000/users/12345
    base path: users

  4. Local Tomcat instance for local testing base path: users

So basically the base path changes from the production stage to all other deployments.

Could we then specify several base paths and the one that first matches is the one to be removed?

Also, I tried to return the URI of a created user with:

URI uriLocation = uriInfo.getAbsolutePathBuilder().path(newUserUuid.toString()).build();
return Response.created(uriLocation).build();

But since the service base path was stripped, I get an invalid URI instead, such as https://api.myproject.com/12345, instead of https://api.myproject.com/v1/users/12345

Is it possible to re-inject the stripped path, so we can programatically get the full URI of the original HTTP Request?

feinstein avatar Jul 09 '19 22:07 feinstein

I'll look into this and let you know here. In the meanwhile, why not load the base path from an environment variable in Lambda?

sapessi avatar Jul 09 '19 22:07 sapessi

It's not a bad solution, I will try it for now, it's just that I am like a "one man startup" so I avoid as much as possible having to configure many different things on many different environments, as I tend to forget something on deployments, or having to mirror locally some cloud specific configuration.

Automation usually is a solution for those things, but automating everything doesn't help me much either unfortunately, because is just more things to maintain, debug, catch-up, learn, remember and etc. So the simpler and compatible, the better for my particular development needs.

feinstein avatar Jul 09 '19 23:07 feinstein

I think that the problem is that somewhere the handler is using the path to map to the java method but a much more resilient solution would be to use the resource.

When using a custom domain I found that I needed to modify the handleRequest method of my RequestStreamHandler so that the resource is use to overwrite the path.

For all of the examples above (and all that I have tested) resource remains /users even when path changes from /users to /v1/users

@Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {

        /*
            The handler assigns requests to methods based on the Path and not the Resource. To
            make the handler resilient to things like Custom domains in API gateway with potentially multiple
            mappings I need to get the resource from the request and set that as the path
         */
        AwsProxyRequest request = objectMapper.readValue(inputStream, AwsProxyRequest.class);
        // set the path to be the resource
        request.setPath( request.getResource() );

        //write the request to an out stream
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        objectMapper.writeValue(out, request);

        //proxy the request (a new input stream is made from the modified output stream
        handler.proxyStream(new ByteArrayInputStream(out.toByteArray()), outputStream, context);
    }

markjschreiber avatar Sep 09 '19 20:09 markjschreiber

The issue with resource @markjschreiber is that it does not include the parameters. For example, for the /users/sapessi request the resource in the proxy request object will be /users/{username} if you have defined the username to be a path variable. I haven't had time to look into this yet.

sapessi avatar Sep 09 '19 20:09 sapessi

You're right. This is another attempt that better handles API gateway proxying and is better than what I previously had but may still not be the best solution:

@Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {

        /*
            The handler assigns requests to methods based on the Path and not the Resource. To
            make the handler resilient to things like Custom domains in API gateway with potentially multiple
            mappings I need to get the resource from the request and set that as the path
         */
        AwsProxyRequest request = objectMapper.readValue(inputStream, AwsProxyRequest.class);

        log.info("Request path: {}", request.getPath());
        log.info("Request path parameters: {}", request.getPathParameters());
        log.info("Requested resource: {}", request.getResource());

        if(request.getResource().equals("/{proxy+}")){
            //set the path to be the proxy path parameter
            String param = request.getPathParameters().get("proxy");
            request.setPath("/"+param);
        } else {
            // set the path to be the resource
            request.setPath(request.getResource());
        }
        //write the request to an out stream
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        objectMapper.writeValue(out, request);

        inputStream = new ByteArrayInputStream(out.toByteArray());

        //proxy the request (a new input stream is made from the modified output stream
        handler.proxyStream(inputStream, outputStream, context);
    }

markjschreiber avatar Sep 09 '19 21:09 markjschreiber