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

Support for Lambda Response Streaming #1681

Open
serg06 opened this issue Apr 9, 2023 · 7 comments
Open

Support for Lambda Response Streaming #1681

serg06 opened this issue Apr 9, 2023 · 7 comments

Comments

@serg06
Copy link

serg06 commented Apr 9, 2023

Feature Request

Lambda has just released Lambda Response Streaming! It uses Transfer-Encoding: chunked to stream data back to the client. It's an extremely useful feature for my team, and I wish we could test it offline.

Note: It only works through Lambda Function URLs, but serverless-offline doesn't support them.

If a solution is not possible, then a workaround would be highly appreciated

Sample Code

  • file: serverless.yml
service: my-service

plugins:
  - serverless-offline

provider:
  runtime: nodejs18.x
  stage: dev

functions:
  stream:
    handler: handler
    url: true  # Ideal option - Lambda URL
    events:  # Backup option - API gateway
      - http:
          method: ANY
          path: 'stream/{proxy+}'
      - http:
          method: ANY
          path: /stream/

resources:
  extensions:
    StreamLambdaFunctionUrl:
      Properties:
        InvokeMode: RESPONSE_STREAM
  • file: handler.js
// Example 1
exports.handler = awslambda.streamifyResponse(
    async (event, responseStream, context) => {
        responseStream.setContentType(“text/plain”);
        responseStream.write(“Hello, world!);
        responseStream.end();
    }
);

// Example 2
exports.handler = awslambda.streamifyResponse(async (event, responseStream, context) => {
  const httpResponseMetadata = {
    statusCode: 200,
    headers: {
      'Content-Type': 'text/html',
      'X-Custom-Header': 'Example-Custom-Header',
    },
  };

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

  responseStream.write('<html>');
  responseStream.write('<p>First write2!</p>');

  responseStream.write('<h1>Streaming h1</h1>');
  await new Promise((r) => setTimeout(r, 1000));
  responseStream.write('<h2>Streaming h2</h2>');
  await new Promise((r) => setTimeout(r, 1000));
  responseStream.write('<h3>Streaming h3</h3>');
  await new Promise((r) => setTimeout(r, 1000));

  const loremIpsum1 =
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae mi tincidunt tellus ultricies dignissim id et diam. Morbi pharetra eu nisi et finibus. Vivamus diam nulla, vulputate et nisl cursus, pellentesque vehicula libero. Cras imperdiet lorem ante, non posuere dolor sollicitudin a. Vestibulum ipsum lacus, blandit nec augue id, lobortis dictum urna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Morbi auctor orci eget tellus aliquam, non maximus massa porta. In diam ante, pulvinar aliquam nisl non, elementum hendrerit sapien. Vestibulum massa nunc, mattis non congue vitae, placerat in quam. Nam vulputate lectus metus, et dignissim erat varius a.';
  responseStream.write(`<p>${loremIpsum1}</p>`);
  await new Promise((r) => setTimeout(r, 1000));

  responseStream.write('<p>DONE!</p>');
  responseStream.end();
});

Expected behavior/code

Currently serverless-offline doesn't support Lambda URLs, so I have to do it through API gateway. This is what happens:

ANY /dev/stream (λ: stream)
× Unhandled exception in handler 'stream'.
× TypeError: underlyingStream.setContentType is not a function
      at HttpResponseStream2.from (C:\monorepo\node_modules\serverless-offline\src\lambda\handler-runner\in-process-runner\aws-lambda-ric\UserFunction.js:157:28)        
      at C:\monorepo\apps\server\.esbuild\.build\src\functions\stream.js:1426:49    
      at InProcessRunner.run (file:///C:/monorepo/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/InProcessRunner.js:87:20)
      at async HandlerRunner.run (file:///C:/monorepo/node_modules/serverless-offline/src/lambda/handler-runner/HandlerRunner.js:114:14)
      at async LambdaFunction.runHandler (file:///C:/monorepo/node_modules/serverless-offline/src/lambda/LambdaFunction.js:305:16)
      at async file:///C:/monorepo/node_modules/serverless-offline/src/events/http/HttpServer.js:602:18
      at async exports.Manager.execute (C:\monorepo\node_modules\@hapi\hapi\lib\toolkit.js:60:28)
      at async internals.handler (C:\monorepo\node_modules\@hapi\hapi\lib\handler.js:46:20)
      at async exports.execute (C:\monorepo\node_modules\@hapi\hapi\lib\handler.js:31:20)
      at async Request._lifecycle (C:\monorepo\node_modules\@hapi\hapi\lib\request.js:370:32)
      at async Request._execute (C:\monorepo\node_modules\@hapi\hapi\lib\request.js:280:9)
× underlyingStream.setContentType is not a function

Ideal solution: It provides me with a Lambda URL to use.

Minimal solution: I'm able to use it through API Gateway.

Additional context/Screenshots

Here are the contents of UserFunction.js: https://gist.github.com/serg06/0653a4c690a7f5854f8038e3ebe62a64

@grakic
Copy link

grakic commented Apr 10, 2023

As serverless-offline added experimental supports for ALB and not just API Gateway, it could be argued that support for Lambda Function URLs could fit inside this project scope. Many users may be migrating from REST/HTTP API Gateway to Function URLs, and there seems to be no valid alternatives for local development today.

To simulate AWS environment, Lambda Function URLs do require hostname or port based routing, as when function is exposed via Function URL, any HTTP method and request path invokes the Lambda function. Adding path-prefix in serverless-offline to expose different functions will introduce different behavior than the simulated AWS environment.

Lambda Function URL can be set in buffered or streaming mode.

To support streaming mode in serverless-offline with Lambda In-Process runner we have to:

Based on some tests done in AWS environment, both decorated and un-decorated handlers can be invoked via function URLs in both invoke modes.

I am slowly working on this to allow local development for streaming responses.

@harounansari-cj
Copy link

Any update on this?

@PierrickI3
Copy link

PierrickI3 commented Jan 11, 2024

Any updates on this? I can see that RESPONSE_STREAM is supported however I get the same error when trying to call the function locally using serverless-offline: TypeError: underlyingStream.setContentType is not a function.

Thanks!

@asychev
Copy link

asychev commented Jan 18, 2024

+1

@rubenbaraut
Copy link

+1 Same problem here.
Trying with serverless-offline-lambda-function-urls plugin, and is failing too.

@ekarmazin
Copy link

+1 Same problem:

"errorMessage": "underlyingStream.setContentType is not a function",
    "errorType": "TypeError",
    "stackTrace": [
        "TypeError: underlyingStream.setContentType is not a function"

@jayarjo
Copy link

jayarjo commented Mar 9, 2024

Any movement on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants