middyjs / middy

🛵 The stylish Node.js middleware engine for AWS Lambda 🛵

Home Page:https://middy.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Streamed responses giving 502 Bad Gateway when payload exceeds threshold (25kb+)

jjason685 opened this issue · comments

Describe the bug
I'm hosting an endpoint using streamifyResponse: true and AWS Lambda Function URLs. When the payload exceeds 20-25kb+ (I'm using a JSON string, but any data will error), the entire function URL 502s, yet there are no errors in the CloudWatch logs. However, when using streamifyResponse: false, the response is returned fine.

To Reproduce
How to reproduce the behaviour:

  1. Create an AWS Lambda with function URL/streaming enabled
  2. Create a handler returning '*'.repeat(25000) and enable streamifyResponse on the middy
  3. Access the lambda function URL, and see that it returns 502 Bad Gateway with the response body "Internal Server Error"

Expected behaviour
Returns response as normal

Environment (please complete the following information):
Using latest version of middy, Lambda environment is node 14

Middy v5 does support Nodejs14 runtime (Only 18/20 support with this version). streamifyResponse was added in nodejs18.x runtime (and I believe back ported to nodejs16.x).

Can you try updating your runtime.

Middy v5 does support Nodejs14 runtime (Only 18/20 support with this version). streamifyResponse was added in nodejs18.x runtime (and I believe back ported to nodejs16.x).

Can you try updating your runtime.

Sorry, we are actually using node 16.x. Also, response streaming appears to work for smaller payloads, but errors on the larger size. This issue may not even necessarily be on Middy's end, as the actual Lambda does not error; perhaps something with AWS?

You need to set a setting on your lambda to allow it to work

You need to set a setting on your lambda to allow it to work

What do you mean? We have Invoke mode set to RESPONSE_STREAM and can stream smaller chunks of text, but errors on this larger JSON.

Function URLs: If receiving no content and non-200 status code are being converted to 200. Be sure to set Invoke Mode to RESPONSE_STREAM over BUFFERED.
https://middy.js.org/docs/intro/streamify-response

Yes, the invoke mode is what was referring too.

Can you test on nodejs18.x? I wonder if the back post was built differently.

Is there a full example you can share?

Function URLs: If receiving no content and non-200 status code are being converted to 200. Be sure to set Invoke Mode to RESPONSE_STREAM over BUFFERED.
https://middy.js.org/docs/intro/streamify-response

Yes, the invoke mode is what was referring too.

Can you test on nodejs18.x? I wonder if the back post was built differently.

Is there a full example you can share?

Nodejs 18.x does not appear to work either. Also, it seems that the function provides the correct response when using the "Test" feature in the Lambda console, meaning the issue is most likely on AWS's Function URLs side.

const middy = require('@middy/core');

const handler = async (event) => {
    const response = {
        statusCode: 200,
        headers: {
            'content-type': 'application/json'
        },
        body: JSON.stringify(['*'.repeat(20000)])
    };

    return response;
};

module.exports.handler = middy(handler, {
  streamifyResponse: true
});

This code snippet errors for me.

Nodejs 18.x does not appear to work either. Also, it seems that the function provides the correct response when using the "Test" feature in the Lambda console, meaning the issue is most likely on AWS's Function URLs side.

Sounds like you may need to open a support ticket.

Note: another thing worth trying; use ESM? v5 deprecated CJS support (but is in v4)

Nodejs 18.x does not appear to work either. Also, it seems that the function provides the correct response when using the "Test" feature in the Lambda console, meaning the issue is most likely on AWS's Function URLs side.

Sounds like you may need to open a support ticket.

Note: another thing worth trying; use ESM? v5 deprecated CJS support (but is in v4)

I believe I figured out the issue.

const pipeline = require("util").promisify(require("stream").pipeline);
const { Readable } = require('stream');

exports.handler = awslambda.streamifyResponse(async (event, responseStream, _context) => {
  // As an example, convert event to a readable stream.
  const response = {
    statusCode: 201,
    headers: {
        'content-type': 'application/json'
    },
    body: JSON.stringify(['*'.repeat(20000)])
  };

  responseStream = awslambda.HttpResponseStream.from(responseStream, response);

  const requestStream = Readable.from(Buffer.from(JSON.stringify(['*'.repeat(20000)])));

  await pipeline(requestStream, responseStream);
});

This above snippet causes the function URL to give 502 Bad Request, but when body is removed as a key from the response object, the function URL returns the response successfully.

handlerBody = handlerResponse.body ?? ''
responseStream = awslambda.HttpResponseStream.from(
responseStream,
handlerResponse
)

In this line in the middy source code, the whole response is passed back from middy without doing any processing, so body is inadvertently being sent as well. Not entirely sure why this is causing the function URL to error, but calling delete handlerResponse.body; before calling awslambda.HttpResponseStream.from should fix it.