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

AwsProxyHttpServletResponseWriter #339

Closed
argenstijn opened this issue Apr 21, 2020 · 8 comments
Closed

AwsProxyHttpServletResponseWriter #339

argenstijn opened this issue Apr 21, 2020 · 8 comments
Assignees
Milestone

Comments

@argenstijn
Copy link

  • Framework version: XX
  • Implementations: Jersey / Spring / Spring Boot / Spark

Scenario

Lambda in combination with ALB should not use base 64 RFC2045 for encoding binary content

Expected behavior

responseString = Base64.getEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes());
awsProxyResponse.setBase64Encoded(true);

Actual behavior

Base64.getMimeEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes());
awsProxyResponse.setBase64Encoded(true);

It;s possible to configure the base 64 encoder to use for creating the response?

@sapessi
Copy link
Collaborator

sapessi commented Apr 21, 2020

Hi @argenstijn - it is not possible today. However, we can add this as a fix for the next release. What is the issue you are running into? What's the output from the ALB?

@argenstijn
Copy link
Author

you will receive a 502 Bad Gateway.

@argenstijn
Copy link
Author

by the ALB

@sapessi sapessi added the bug label Jul 14, 2020
@sapessi sapessi added this to the Release 1.5.1 milestone Jul 14, 2020
@sapessi sapessi self-assigned this Jul 14, 2020
@sapessi sapessi removed this from the Release 1.5.1 milestone Jul 15, 2020
@sapessi
Copy link
Collaborator

sapessi commented Jul 15, 2020

I have tested this and for some reason I cannot seem to return binary content from an ALB no matter what I do - even without using the MIME encoder. This works fine with API Gateway. I will check-in with the ALB team to dive deeper on the issue

@argenstijn
Copy link
Author

argenstijn commented Jul 15, 2020 via email

@argenstijn
Copy link
Author

argenstijn commented Jul 16, 2020

See config below:

/**

  • Same functionality as {@link AwsProxyHttpServletResponseWriter} but uses
  • the default BASE64 encoder when detecting binary data.

*/
public class DefaultBase64EncoderAwsProxyHttpServletResponseWriter extends ResponseWriter<AwsHttpServletResponse, AwsProxyResponse> {

@Override
public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse, Context lambdaContext)
        throws InvalidResponseObjectException {
   
    AwsProxyResponse awsProxyResponse = new AwsProxyResponse();
    if (containerResponse.getAwsResponseBodyString() != null) {
        String responseString;

        if (!isBinary(containerResponse.getContentType()) && isValidUtf8(containerResponse.getAwsResponseBodyBytes())) {
            responseString = containerResponse.getAwsResponseBodyString();
        } else {
            responseString = Base64.getEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes());
            awsProxyResponse.setBase64Encoded(true);
        }

        awsProxyResponse.setBody(responseString);
    }
    awsProxyResponse.setMultiValueHeaders(containerResponse.getAwsResponseHeaders());

    awsProxyResponse.setStatusCode(containerResponse.getStatus());

    if (containerResponse.getAwsProxyRequest().getRequestSource() == AwsProxyRequest.RequestSource.ALB) {
        awsProxyResponse.setStatusDescription(containerResponse.getStatus() + " " + Response.Status.fromStatusCode(containerResponse.getStatus()).getReasonPhrase());
    }
   
    return awsProxyResponse;
}

private boolean isBinary(String contentType) {
        
    if(contentType != null) {
        int semidx = contentType.indexOf(';');
        if(semidx >= 0) {
            return LambdaContainerHandler.getContainerConfig().isBinaryContentType(contentType.substring(0, semidx));
        }
        else {
            return LambdaContainerHandler.getContainerConfig().isBinaryContentType(contentType);
        }
    }
    return false;
}

}

Then you should use this class and make sure that you also add the binary you try to send:

 JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> newHandler =
        new JerseyLambdaContainerHandler<>(AwsProxyRequest.class, AwsProxyResponse.class,
            new AwsProxyHttpServletRequestReader(),
            new DefaultBase64EncoderAwsProxyHttpServletResponseWriter(),
            new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler(), app);

 
    newHandler.initialize();  
String[] BINARTY_CONTENT_TYPES = {"application/pdf"};  
    LambdaContainerHandler.getContainerConfig().addBinaryContentTypes(BINARTY_CONTENT_TYPES});    

In this example you should be able to download a PDF

@sapessi sapessi added this to the 1.5.2 milestone Oct 6, 2020
@sapessi sapessi mentioned this issue Oct 6, 2020
2 tasks
sapessi added a commit that referenced this issue Oct 6, 2020
* fix: Use single value headers for HTTP API response (#377)

Parametrized the use of the single value headers for the response writer to support the v2 proxy schema for HTTP API

* fix: Updated HTTP API handler constructor (#377)

Changed default httpApiV2 constructor for the handler in all the framework implementations

* fix: Switched to non-mime encoder for respponse (#339)
@sapessi
Copy link
Collaborator

sapessi commented Oct 6, 2020

1.5.2 has now hit maven central. Closing this issue.

@sapessi sapessi closed this as completed Oct 6, 2020
@swagsb
Copy link

swagsb commented May 20, 2021

Hi,

I am still facing this issue using 1.5.2 . My scenario :

I am simply trying to download a zip which has an xml file. My controller

@GetMapping("/downloadZip")
public void downloadFile(HttpServletResponse response) {

    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment;filename=download.zip");
    response.setStatus(HttpServletResponse.SC_OK);

    try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
            Resource resource = new ClassPathResource("logback.xml");
            ZipEntry e = new ZipEntry(resource.getFilename());
            e.setSize(resource.contentLength());
            e.setTime(System.currentTimeMillis());
            // etc.
            zippedOut.putNextEntry(e);
            // And the content of the resource:
            StreamUtils.copy(resource.getInputStream(), zippedOut);
            zippedOut.closeEntry();

        zippedOut.finish();
    } catch (Exception e) {
        // Exception handling goes here
    }
}

This downloads the zip fine on local. On aws , it downloads the zip , but cant extract it . Its corrupted. Windows says invalid.
I tested it by putting breakpoint on AwsProxyHttpServletResponseWriter , line no 38 , here :
if (!this.isBinary(containerResponse.getContentType()) && this.isValidUtf8(containerResponse.getAwsResponseBodyBytes())) {
responseString = containerResponse.getAwsResponseBodyString();
} else {
responseString = Base64.getEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes());
awsProxyResponse.setBase64Encoded(true);
}

I can see that its going to the else block and using Base 64 encoder.
Any idea why this still doesnt work on ALB ? Should I check this with AWS then ?

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

3 participants