Skip to content

Commit 98d2d25

Browse files
committed
Bug fixes
1 parent 1a86609 commit 98d2d25

3 files changed

Lines changed: 27 additions & 10 deletions

File tree

Libraries/src/Amazon.Lambda.Core/ResponseStreaming/HttpResponseStreamPrelude.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33
#if NET8_0_OR_GREATER
4+
using System;
45
using System.Collections.Generic;
56
using System.Net;
67
using System.Runtime.Versioning;
@@ -82,6 +83,11 @@ internal byte[] ToByteArray()
8283
writer.WriteEndObject();
8384
}
8485

86+
if (string.Equals(Environment.GetEnvironmentVariable("LAMBDA_NET_SERIALIZER_DEBUG"), "true", StringComparison.OrdinalIgnoreCase))
87+
{
88+
LambdaLogger.Log(LogLevel.Information, "HTTP Response Stream Prelude JSON: {Prelude}", System.Text.Encoding.UTF8.GetString(bufferWriter.WrittenSpan));
89+
}
90+
8591
return bufferWriter.WrittenSpan.ToArray();
8692
}
8793
}

Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStream.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,17 @@ internal async Task SetHttpOutputStreamAsync(Stream httpOutputStream, Cancellati
8282
// where a handler WriteAsync that is already waiting on the semaphore could
8383
// sneak in and write body data before the prelude, causing intermittent
8484
// "Failed to parse prelude JSON" errors from API Gateway.
85+
//
86+
// Note: we intentionally do NOT check ThrowIfCompletedOrError() here.
87+
// SetHttpOutputStreamAsync is infrastructure setup called by RawStreamingHttpClient,
88+
// not a handler write. For fast-completing responses (e.g. Results.Json),
89+
// LambdaBootstrap may call MarkCompleted() before the TCP connection is established
90+
// and this method is called. The prelude still needs to be written to the wire
91+
// so the response is properly framed.
8592
if (_prelude?.Length > 0)
8693
{
8794
_logger.LogDebug($"Writing prelude of {_prelude.Length} bytes to HTTP stream.");
8895

89-
lock (_lock)
90-
{
91-
ThrowIfCompletedOrError();
92-
}
93-
9496
var combinedLength = _prelude.Length + PreludeDelimiter.Length;
9597
var combined = ArrayPool<byte>.Shared.Rent(combinedLength);
9698
try
@@ -152,7 +154,12 @@ public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationT
152154

153155
lock (_lock)
154156
{
155-
ThrowIfCompletedOrError();
157+
// Only throw on error, not on completed. For buffered ASP.NET Core responses
158+
// (e.g. Results.Json), the pipeline completes and LambdaBootstrap calls
159+
// MarkCompleted() before the pre-start buffer has been flushed to the wire.
160+
// The buffered data still needs to be written even after MarkCompleted.
161+
if (_hasError)
162+
throw new InvalidOperationException("Cannot write to a stream after an error has been reported.");
156163
_bytesWritten += count;
157164
}
158165

Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/ResponseStreamTests.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,18 @@ public async Task ReportErrorAsync_ReleasesCompletionSignal()
140140
}
141141

142142
[Fact]
143-
public async Task WriteAsync_AfterMarkCompleted_Throws()
143+
public async Task WriteAsync_AfterMarkCompleted_StillSucceeds()
144144
{
145-
var (stream, _) = await CreateWiredStream();
145+
var (stream, output) = await CreateWiredStream();
146146
await stream.WriteAsync(new byte[] { 1 }, 0, 1);
147147
stream.MarkCompleted();
148148

149-
await Assert.ThrowsAsync<InvalidOperationException>(
150-
() => stream.WriteAsync(new byte[] { 2 }, 0, 1));
149+
// Writes after MarkCompleted are allowed — buffered ASP.NET Core responses
150+
// (e.g. Results.Json) may flush pre-start buffer data after the pipeline
151+
// completes and LambdaBootstrap calls MarkCompleted.
152+
await stream.WriteAsync(new byte[] { 2 }, 0, 1);
153+
154+
Assert.Equal(new byte[] { 1, 2 }, output.ToArray());
151155
}
152156

153157
[Fact]

0 commit comments

Comments
 (0)