Skip to content

Add support for Lambda Response Streaming#2288

Draft
normj wants to merge 21 commits intofeature/response-streamingfrom
normj/response-streaming
Draft

Add support for Lambda Response Streaming#2288
normj wants to merge 21 commits intofeature/response-streamingfrom
normj/response-streaming

Conversation

@normj
Copy link
Member

@normj normj commented Feb 19, 2026

In draft mode while still cleaning up the experience

Issue #, if available:
#1635

Description of changes:
Integrate Lambda response streaming support into the Amazon.Lambda.RuntimeSupport.

A hello world example of using response streaming. In this case sense I wrapped the Stream returned from CreateStream in a StreamWriter the writes will be buffered in StreamWriter till the buffer is full. I call the flush method every 10 iterations to force sending data back to the client.

using Amazon.Lambda.Core;
using Amazon.Lambda.Core.ResponseStreaming;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.SystemTextJson;

#pragma warning disable CA2252

// The function handler that will be called for each Lambda event
var handler = async (string input, ILambdaContext context) =>
{
    using var responseStream = LambdaResponseStreamFactory.CreateStream();

    using var writer = new StreamWriter(responseStream);
    for (var i = 1; i <= 100; i++)
    {
        var message = $"Hello {input} - {i}";
        await writer.WriteLineAsync(message);

        if (i % 10 == 0)
        {
            await writer.FlushAsync();
            await Task.Delay(1000);
        }
    }
};

// Build the Lambda runtime client passing in the handler to call for each
// event and the JSON serializer to use for translating Lambda JSON documents
// to .NET types.
await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
        .Build()
        .RunAsync();

For a use with API Gateway or Lambda Function URL you need to create the stream with the CreateHttpStream passing the status code and response headers.

using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
using Amazon.Lambda.Core.ResponseStreaming;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.SystemTextJson;
using System.Net;

#pragma warning disable CA2252

// The function handler that will be called for each Lambda event
var handler = async (APIGatewayProxyRequest request, ILambdaContext context) =>
{
    var prelude = new HttpResponseStreamPrelude
    {
        StatusCode = HttpStatusCode.OK,
        Headers =
        {
            { "Content-Type", "text/plain" }
        }
    };
   
    using var responseStream = LambdaResponseStreamFactory.CreateHttpStream(prelude);

    using var writer = new StreamWriter(responseStream);
    for (var i = 1; i <= 10000000; i++)
    {
        var message = $"Hello - {i} ({responseStream.Length.ToString("N0")}) (Remaining Time: {context.RemainingTime})";
        await writer.WriteLineAsync(message);

        if (i % 100 == 0)
        {
            await writer.FlushAsync();
        }

        if (context.RemainingTime < TimeSpan.FromSeconds(5))
        {
            await writer.WriteLineAsync("Approaching Lambda timeout, stopping the stream.");
            await writer.FlushAsync();
            break;
        }
    }
};

// Build the Lambda runtime client passing in the handler to call for each
// event and the JSON serializer to use for translating Lambda JSON documents
// to .NET types.
await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
        .Build()
        .RunAsync();

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@normj normj marked this pull request as draft February 19, 2026 01:50
@normj normj changed the base branch from dev to feature/response-streaming March 10, 2026 23:49
@normj normj marked this pull request as ready for review March 11, 2026 18:25
@normj normj marked this pull request as draft March 11, 2026 18:26
@normj normj force-pushed the normj/response-streaming branch from 2ed63cc to d60bb93 Compare March 11, 2026 20:10
@normj normj force-pushed the normj/response-streaming branch 5 times, most recently from 8c56514 to 1471d93 Compare March 12, 2026 06:42
@normj normj force-pushed the normj/response-streaming branch from 1471d93 to d0861c6 Compare March 12, 2026 07:05
@normj normj marked this pull request as ready for review March 12, 2026 18:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Integrates Lambda response streaming into Amazon.Lambda.RuntimeSupport and exposes corresponding (preview) streaming APIs in Amazon.Lambda.Core, along with unit + integration test coverage and a deployable streaming handler test app.

Changes:

  • Add streaming send path to RuntimeApiClient / LambdaBootstrap, including per-invocation streaming state management.
  • Introduce streaming primitives (ResponseStream, StreamingHttpContent, prelude support) and Core-facing preview APIs (LambdaResponseStreamFactory, HttpResponseStreamPrelude).
  • Add extensive unit/integration tests and a ResponseStreamingFunctionHandlers test Lambda project.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/ResponseStreamingFunctionHandlers/aws-lambda-tools-defaults.json Adds Lambda tooling defaults for streaming handler test app.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/ResponseStreamingFunctionHandlers/ResponseStreamingFunctionHandlers.csproj New net10 test Lambda app project for streaming scenarios.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/ResponseStreamingFunctionHandlers/Function.cs Streaming handler scenarios used by integration tests.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/TestHelpers/TestStreamingRuntimeApiClient.cs Test runtime client that simulates streaming without real HTTP.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/TestHelpers/NoOpInternalRuntimeApiClient.cs No-op internal runtime API client for unit tests.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/StreamingHttpContentTests.cs Unit tests for StreamingHttpContent behavior.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/StreamingE2EWithMoq.cs End-to-end tests covering bootstrap → streaming pipeline behavior.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/RuntimeApiClientTests.cs Tests for streaming vs buffered header behavior on requests.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/ResponseStreamTests.cs Unit tests for new ResponseStream implementation.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/ResponseStreamFactoryTests.cs Unit tests for streaming factory state + send-task behavior.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaResponseStreamingCoreTests.cs Tests for new Amazon.Lambda.Core.ResponseStreaming APIs and prelude serialization.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaBootstrapTests.cs Adds bootstrap integration tests for streaming vs buffered paths.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/HandlerTests.cs Updates test collection for streaming factory isolation.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/ResponseStreamingTests.cs New integration tests invoking Lambda with response stream.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/IntegrationTestFixture.cs Packages additional streaming test app; adjusts temp path/tool install flow.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/IntegrationTestCollection.cs Disables parallelization and wires streaming fixture into integration collection.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/Helpers/LambdaToolsHelper.cs Adjusts temp directory behavior (DEBUG uses repo paths).
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/Helpers/CommandLineWrapper.cs Captures command output while running packaging commands.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/CustomRuntimeTests.cs Removes NET6 target enum entry.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/BaseCustomRuntimeTest.cs Updates runtime/framework assumptions (Dotnet10) + improves missing bundle diagnostics.
Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.IntegrationTests/Amazon.Lambda.RuntimeSupport.IntegrationTests.csproj Moves integration tests to net10.0 and bumps package refs.
Libraries/src/Amazon.Lambda.RuntimeSupport/Client/RuntimeApiClient.cs Adds StartStreamingResponseAsync entry point and streaming headers.
Libraries/src/Amazon.Lambda.RuntimeSupport/Client/ResponseStream.cs Removes old buffered-chunk streaming ResponseStream implementation.
Libraries/src/Amazon.Lambda.RuntimeSupport/Client/IResponseStream.cs Removes old public IResponseStream surface.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/StreamingHttpContent.cs New HttpContent for streaming serialization + error signaling.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/StreamingConstants.cs Moves streaming constants into streaming namespace and trims unused max size constant.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamLambdaCoreInitializerIsolated.cs Wires RuntimeSupport streaming into Core via isolated initializer.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamFactory.cs New per-invocation streaming state + send-task coordination.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamContext.cs Extends context to carry runtime client, cancellation token, and send task.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStream.cs New “true streaming” ResponseStream writing directly to HTTP output stream.
Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/LambdaBootstrap.cs Initializes streaming integration, routes mid-stream errors to trailers, awaits send task, cleans up state.
Libraries/src/Amazon.Lambda.Core/ResponseStreaming/LambdaResponseStreamFactory.cs Adds preview public factory API for response streaming streams.
Libraries/src/Amazon.Lambda.Core/ResponseStreaming/LambdaResponseStream.cs Adds preview Stream subclass wrapper for streaming writes.
Libraries/src/Amazon.Lambda.Core/ResponseStreaming/ILambdaResponseStream.cs Internal bridge interface for RuntimeSupport to implement.
Libraries/src/Amazon.Lambda.Core/ResponseStreaming/HttpResponseStreamPrelude.cs Adds HTTP response prelude model + JSON serialization.
Libraries/Libraries.sln Adds new test app project and updates VS version metadata.
.gitignore Ignores **/.kiro/.
.autover/changes/c27a62e6-91ca-4a59-9406-394866cdfa62.json Declares minor version changes for RuntimeSupport + Core (preview streaming).
Comments suppressed due to low confidence (1)

Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamFactory.cs:27

  • ResponseStreamFactory was changed to internal, but the unit tests in Amazon.Lambda.RuntimeSupport.Tests call it directly. There is no InternalsVisibleTo configured for the RuntimeSupport assembly, so this will not compile. Either make the type (and other test-used streaming types) public again, or add an InternalsVisibleTo for the test assembly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +209 to +213
// Declare trailers upfront — we always declare them since we don't know
// at request start time whether an error will occur mid-stream.
request.Headers.Add("Trailer",
$"{StreamingConstants.ErrorTypeTrailer}, {StreamingConstants.ErrorBodyTrailer}");

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request declares a Trailer header, but the current implementation doesn’t appear to send actual HTTP trailing headers (it writes trailer-looking lines into the content stream). Consider using HttpRequestMessage.TrailingHeaders (populated once the handler outcome is known) so the error type/body are emitted as real trailers rather than body bytes.

Suggested change
// Declare trailers upfront — we always declare them since we don't know
// at request start time whether an error will occur mid-stream.
request.Headers.Add("Trailer",
$"{StreamingConstants.ErrorTypeTrailer}, {StreamingConstants.ErrorBodyTrailer}");

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +56
if (_responseStream.HasError)
{
InternalLogger.GetDefaultLogger().LogError(_responseStream.ReportedError, "An error occurred during Lambda execution. Writing error trailers to response.");
await WriteErrorTrailersAsync(stream, _responseStream.ReportedError);
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an error occurs, SerializeToStreamAsync writes trailer-looking data via WriteErrorTrailersAsync directly into the content stream. With HTTP chunked transfer, these bytes are typically part of the body, not HTTP trailing headers. If Lambda expects real HTTP trailers, this should be implemented via HttpRequestMessage.TrailingHeaders instead of writing to the payload stream.

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +75
var errorTypeHeader = $"{StreamingConstants.ErrorTypeTrailer}: {exceptionInfo.ErrorType}\r\n";
var errorTypeBytes = Encoding.UTF8.GetBytes(errorTypeHeader);
await stream.WriteAsync(errorTypeBytes, 0, errorTypeBytes.Length, _cancellationToken);

var errorBodyJson = LambdaJsonExceptionWriter.WriteJson(exceptionInfo);
var errorBodyHeader = $"{StreamingConstants.ErrorBodyTrailer}: {errorBodyJson}\r\n";
var errorBodyBytes = Encoding.UTF8.GetBytes(errorBodyHeader);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WriteErrorTrailersAsync formats the error type/body as UTF-8 header lines and writes them to the content stream. That is unlikely to be interpreted as HTTP trailers by the receiver. Prefer setting true trailing headers on the HttpRequestMessage rather than emitting these lines into the response payload.

Suggested change
var errorTypeHeader = $"{StreamingConstants.ErrorTypeTrailer}: {exceptionInfo.ErrorType}\r\n";
var errorTypeBytes = Encoding.UTF8.GetBytes(errorTypeHeader);
await stream.WriteAsync(errorTypeBytes, 0, errorTypeBytes.Length, _cancellationToken);
var errorBodyJson = LambdaJsonExceptionWriter.WriteJson(exceptionInfo);
var errorBodyHeader = $"{StreamingConstants.ErrorBodyTrailer}: {errorBodyJson}\r\n";
var errorBodyBytes = Encoding.UTF8.GetBytes(errorBodyHeader);
// Serialize the exception information as JSON in the response payload
var errorBodyJson = LambdaJsonExceptionWriter.WriteJson(exceptionInfo);
var errorBodyBytes = Encoding.UTF8.GetBytes(errorBodyJson);

Copilot uses AI. Check for mistakes.
@normj normj marked this pull request as draft March 13, 2026 06:31
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

Successfully merging this pull request may close these issues.

2 participants