7

What we do is take a request for an image like "media/catalog/product/3/0/30123/768x/lorem.jpg", then we use the original image located at "media/catalog/product/3/0/30123.jpg", resize it to 768px and webp if the browser supports that and then return the new image (if not already cached).

If you request: wysiwyg/lorem.jpg it will try to create a webp in maximum 1920 pixels (no enlargement).

This seems to work perfectly fine up to <= 1420 pixels wide image. However above that we only get HTTP 502: The Lambda function returned invalid json: The json output is not parsable.

There is a similar issue on SO that relates to GZIP, however as I understand you shouldn't really GZIP images: https://webmasters.stackexchange.com/questions/8382/gzipped-images-is-it-worth/57590#57590

But it's possible that the original image was uploaded to S3 GZIPPED already. But the gzip might be miss-leading because why would it work for smaller images then? We have GZIP disabled in Cloudfront.

I have given the Lamda@Edge Resize function maximum resources 3GB memory and timeout of 30 seconds.. Is this not sufficient for larger images?

I have deleted the already generated images, invalidated Cloudfront but it still behaves the same..

EDIT: UPDATE:

I simply tried a different image and then it works fine.. I have no idea why and how I should solve the broken image then... I guess Cloudfront has cached the 502 now.. I have invalidated using just "*" but didn't help.. Both original files are jpg.

The original source image for the working one is 6.1 MB and non working is 6.7 MB if that matters.

They have these limits: https://docs.aws.amazon.com/lambda/latest/dg/limits.html

The response.body is about 512 MB when it stops working.

OZZIE
  • 6,609
  • 7
  • 55
  • 59
  • The strangest thing is that it does generate the image on S3, and if I download it an open it, it works fine.. why can't it just show me the image on frontend :@ – OZZIE Mar 13 '19 at 13:07

2 Answers2

9

There are some low limits in Lambda, especially in Lambda@Edge on the response size. The limit is 1 MB for the entire response, headers and body included. If lambda function returns a bigger response it will be truncated which can cause HTTP 500 statuses. See documentation.

You can overcome that by saving result image on S3 (or maybe checking first if it's already there), and then instead of returning it just making a 301 redirect to CloudFront distribution integrated with that bucket - so image request will be redirected to result image.

For example in node.js with Origin-Response trigger:

'use strict';
exports.handler = (event, context, callback) => {
    // get response
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    // create image and save on S3, generate target_url
    // ...

    // modify response and headers
    response.status = 301;
    response.statusDescription = 'Moved Permanently';
    headers['Location'] = [{key: 'Location', value: target_url}]; 
    headers['x-reason'] = [{key: 'X-Reason', value: 'Generated.'}]; 

    // return modified response
    callback(null, response);
};

Version for simple Lambda Gateway (without Origin-Response, replaces headers):

exports.handler = (event, context, callback) => {
    // create image and save on S3, generate target_url
    // ...
    var response = {
      status: 301,
      headers: {
        Location: [{
          key: 'Location',
          value: [],
        }],
        'X-Reason': [{
          key: 'X-Reason',
          value: '',
        }],
      },
    };
    callback(null, response);
}
OZZIE
  • 6,609
  • 7
  • 55
  • 59
Zbyszek
  • 1,420
  • 18
  • 23
  • Now I am getting "The Lambda function result failed validation: The function tried to add, delete, or change a read-only header. " – OZZIE Mar 20 '19 at 10:55
  • I think the solution is to only modify the response instead of replacing it completely – OZZIE Mar 20 '19 at 11:01
  • 1
    I modified answer - added version for Origin-Response (which needs modification of headers and status instead of replacing). – Zbyszek Mar 21 '19 at 12:23
  • great man! Your answer is correct! They limit the response request in lambda@edge to 1MB body + headers total size together, even Amason tech support was wrong and said 6MB but it's 1MB :) (6 is for non edge I believe) – OZZIE Mar 21 '19 at 12:26
1

Additional notes to @Zbyszek's answer, you can roughly estimate if the request is bigger than 1MB like this:

const isRequestBiggerThan1MB = (body, responseWithoutBody) => {
  const responseSizeWithoutBody = JSON.stringify(responseWithoutBody).length;
  return body.length + responseSizeWithoutBody >= 1000 * 1000;
};

the responseWithoutBody can't be too large or contain "recursive keys" (or what it's called) but in this case I can't imagine that you would have that. If it contains recursive keys then you can simply remove those. If the responseWithoutBody is too large you need to remove those values and measure them separatly - for example like I am doing with the response.body.

OZZIE
  • 6,609
  • 7
  • 55
  • 59