2
votes

I am invoking a AWS Lambda function from the AWS API Gateway. The returned JSON needs to be zipped since it sometimes became too big (body size too large etc). However, I have some issues getting the response through the API Gateway. This is my Java code:

    @Override
    public JSONObject handleRequest(Object input, Context context) {
        String json_string = "";
        try {
            Gson gson = new Gson();
            json_string = gson.toJson(input, LinkedHashMap.class);
        } catch (ClassCastException ex) {
            json_string = (String) input;
        }

        GenerateJson generateJson = new GenerateJson ();
        String body = "";
        try {
            JSONParser parser = new JSONParser();
            Object jsonObj = parser.parse(json_string);
            JSONObject matchesobj = (JSONObject) jsonObj;

            if (matchesobj.containsKey("body")) {
                body = (String) matchesobj.get("body");
            } else {
                JSONObject error = new JSONObject();
                error.put("error", "No body with Base64 data in Request.");
                System.out.println(error.toJSONString());
                return error;
            }

        } catch (ParseException ex) {
            ex.printStackTrace();
        }

        byte[] decodedBytes = Base64.getDecoder().decode(body);
        String decodedString = new String(decodedBytes);
        // System.out.println(decodedString);

        JSONObject json = generateJson .getJson(decodedString, "", 2);

        JSONObject returnObject = new JSONObject();
        JSONObject headers = new JSONObject();
        returnObject.put("statusCode", 205);
        returnObject.put("isBase64Encoded", true);
        // returnObject.put("Content-Encoding", "gzip");

        returnObject.put("headers", headers);
        returnObject.put("body", compressStringAndReturnBase64(json.toString()));

        return (returnObject);
    }

    public static String compressStringAndReturnBase64(String srcTxt) {

        ByteArrayOutputStream rstBao = new ByteArrayOutputStream();
        GZIPOutputStream zos;
        try {
            zos = new GZIPOutputStream(rstBao);
            zos.write(srcTxt.getBytes());
            IOUtils.closeQuietly(zos);
            byte[] bytes = rstBao.toByteArray();
            String base64comp = Base64.getEncoder().encodeToString(bytes);
            System.out.println("Json String is " + srcTxt.toString().getBytes().length + " compressed " + bytes.length + " compressed Base64 " + base64comp.getBytes().length);

            return base64comp;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";
    }

I've checked the Base64 output and that seems to work fine (pasted it in https://www.base64decode.org/). In addition, when I check with Postman, I get a binary blob which can be unpacked with 7-zip if I save the response to something that ends with .gz.

Under settings, the API Gateway Binary Media Types has been set to / But I'd like to have the client "see" that it is GZIPped and decode it on the fly. However, when I add the line

returnObject.put("Content-Encoding", "gzip");

I get {"message": "Internal server error"} and in the AWS API logs: Execution failed due to configuration error: Malformed Lambda proxy response

The Lambda logs are fine, so it did execute successfully, just wasn't able to be returned.

I am thinking I need some more tweaking on the API Gateway side, any ideas?

2
Can you show the whole class? I'm looking to see the public class XXXX implements ........ What interface are you implementing? - ᴛʜᴇᴘᴀᴛᴇʟ
Content-Encoding belongs inside headers, not as a top level key in returnObject. It appears you are building a malformed response by putting this in the wrong place. - Michael - sqlbot
Michael, your comment worked! But I can't flag it as the answer? - helloworld

2 Answers

2
votes

This sounds like the binary support setting on API Gateway isn't configured correctly; API gateway is trying to parse the response from your lambda rather than passing it on directly to the client.

You can update this setting in the console:

API Gateway binary support

0
votes

In your HTTP request add "Accept" header with payload content type.

Accept: application/gzip

Also in the HTTP response, there should be "Content-Type" header indicating response content type.

Content-Type: application/gzip

Your lambda returns Base64 encoded binary data to API Gateway. So in order to decode data your HTTP request's Accept header and Response's Content-type headers should there.