dherault / dherault/serverless-offline
Support for Lambda Response Streaming
- Dominant language
- JavaScript
- Stars
- 5.3k
- Forks
- 811
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 3
Description
## Feature Request
Lambda has just released [Lambda Response Streaming](https://aws.amazon.com/blogs/compute/introducing-aws-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](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html), but [serverless-offline doesn't support them](https://github.com/dherault/serverless-offline/issues/1382).
If a solution is not possible, then a workaround would be highly appreciated
**Sample Code**
- file: serverless.yml
```yaml
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
```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('');
responseStream.write('
First write2!
');responseStream.write('
Streaming h1
');await new Promise((r) => setTimeout(r, 1000));
responseStream.write('
Streaming h2
');await new Promise((r) => setTimeout(r, 1000));
responseStream.write('
Streaming 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(`
${loremIpsum1}
`);await new Promise((r) => setTimeout(r, 1000));
responseStream.write('
DONE!
');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
Contributor guide
Research direction
Start with the referenced serverless.yml and handler.js examples, then inspect the UserFunction.js gist where the setContentType failure occurs. First verify the existing Lambda URL limitation and response-stream handling; done means the sample can stream chunks locally through a Lambda URL, or the documented API Gateway workaround works without that error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, javascript
- Domain
- api, backend, cloud
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100