diff --git a/.autover/changes/f6d458e7-d276-40d2-b1ab-d0c4327e396f.json b/.autover/changes/f6d458e7-d276-40d2-b1ab-d0c4327e396f.json new file mode 100644 index 000000000..418f95c23 --- /dev/null +++ b/.autover/changes/f6d458e7-d276-40d2-b1ab-d0c4327e396f.json @@ -0,0 +1,18 @@ +{ + "Projects": [ + { + "Name": "Amazon.Lambda.Core", + "Type": "Patch", + "ChangelogMessages": [ + "Fix type load issue due to Lambda Response Streaming. https://github.com/aws/aws-lambda-dotnet/issues/2430" + ] + }, + { + "Name": "Amazon.Lambda.RuntimeSupport", + "Type": "Patch", + "ChangelogMessages": [ + "Fix type load issue due to Lambda Response Streaming. https://github.com/aws/aws-lambda-dotnet/issues/2430" + ] + } + ] +} \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.Core/ResponseStreaming/ImplLambdaResponseStream.cs b/Libraries/src/Amazon.Lambda.Core/ResponseStreaming/ImplLambdaResponseStream.cs new file mode 100644 index 000000000..69cff8ef7 --- /dev/null +++ b/Libraries/src/Amazon.Lambda.Core/ResponseStreaming/ImplLambdaResponseStream.cs @@ -0,0 +1,45 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#if NET8_0_OR_GREATER + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Amazon.Lambda.Core.ResponseStreaming +{ + internal class ImplLambdaResponseStream : ILambdaResponseStream + { + private readonly Delegates _innerDelegates; + + internal ImplLambdaResponseStream(Delegates innerDelegates) + { + _innerDelegates = innerDelegates ?? throw new ArgumentNullException(nameof(innerDelegates)); + _innerDelegates.WriteAsync = innerDelegates.WriteAsync ?? throw new ArgumentNullException(nameof(innerDelegates.WriteAsync)); + _innerDelegates.BytesWritten = innerDelegates.BytesWritten ?? throw new ArgumentNullException(nameof(innerDelegates.BytesWritten)); + _innerDelegates.HasError = innerDelegates.HasError ?? throw new ArgumentNullException(nameof(innerDelegates.HasError)); + _innerDelegates.Dispose = innerDelegates.Dispose ?? throw new ArgumentNullException(nameof(innerDelegates.Dispose)); + } + + /// + public long BytesWritten => _innerDelegates.BytesWritten(); + + /// + public bool HasError => _innerDelegates.HasError(); + + /// + public void Dispose() => _innerDelegates.Dispose(); + + /// + public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) => _innerDelegates.WriteAsync(buffer, offset, count, cancellationToken); + + internal class Delegates + { + internal Func WriteAsync { get; set; } + internal Func BytesWritten { get; set; } + internal Func HasError { get; set; } + internal Action Dispose { get; set; } + } + } +} +#endif diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamLambdaCoreInitializerIsolated.cs b/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamLambdaCoreInitializerIsolated.cs index 2cb46e3ce..01f1b85ca 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamLambdaCoreInitializerIsolated.cs +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/ResponseStreaming/ResponseStreamLambdaCoreInitializerIsolated.cs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 using System; -using System.Threading; -using System.Threading.Tasks; using Amazon.Lambda.Core.ResponseStreaming; using Amazon.Lambda.RuntimeSupport.Client.ResponseStreaming; #pragma warning disable CA2252 @@ -26,34 +24,22 @@ internal class ResponseStreamLambdaCoreInitializerIsolated internal static void InitializeCore() { #if !ANALYZER_UNIT_TESTS // This precompiler directive is used to avoid the unit tests from needing a dependency on Amazon.Lambda.Core. - Func factory = (byte[] prelude) => new ImplLambdaResponseStream(ResponseStreamFactory.CreateStream(prelude)); - LambdaResponseStreamFactory.SetLambdaResponseStream(factory); -#endif - } - - /// - /// Implements the interface by wrapping a . This is used to connect the internal response streaming implementation to the public interfaces in Amazon.Lambda.Core. - /// - internal class ImplLambdaResponseStream : ILambdaResponseStream - { - private readonly ResponseStream _innerStream; - - internal ImplLambdaResponseStream(ResponseStream innerStream) + Func factory = (byte[] prelude) => { - _innerStream = innerStream; - } - - /// - public long BytesWritten => _innerStream.BytesWritten; + var responseStream = ResponseStreamFactory.CreateStream(prelude); + var delegates = new ImplLambdaResponseStream.Delegates + { + WriteAsync = responseStream.WriteAsync, + BytesWritten = () => responseStream.BytesWritten, + HasError = () => responseStream.HasError, + Dispose = () => responseStream.Dispose() + }; - /// - public bool HasError => _innerStream.HasError; + return new ImplLambdaResponseStream(delegates); + }; - /// - public void Dispose() => _innerStream.Dispose(); - - /// - public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) => _innerStream.WriteAsync(buffer, offset, count, cancellationToken); + LambdaResponseStreamFactory.SetLambdaResponseStream(factory); +#endif } } } diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs index dae6a0d69..73027d682 100644 --- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs +++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs @@ -2397,7 +2397,6 @@ private static DiagnosticResult[] GetExpectedRuntimeSupportDiagnostics() // These are here because the internalvisibleto attribute isn't included in test compilations, so these types are inaccessible. DiagnosticResult.CompilerError("CS0117").WithSpan(snapFile, 13, 34, 13, 71).WithArguments("Amazon.Lambda.Core.SnapshotRestore", "CopyBeforeSnapshotCallbacksToRegistry"), DiagnosticResult.CompilerError("CS0117").WithSpan(snapFile, 14, 34, 14, 69).WithArguments("Amazon.Lambda.Core.SnapshotRestore", "CopyAfterRestoreCallbacksToRegistry"), - DiagnosticResult.CompilerError("CS0122").WithSpan($"Amazon.Lambda.RuntimeSupport{Path.DirectorySeparatorChar}Bootstrap{Path.DirectorySeparatorChar}ResponseStreaming{Path.DirectorySeparatorChar}ResponseStreamLambdaCoreInitializerIsolated.cs", 37, 51, 37, 72).WithArguments("Amazon.Lambda.Core.ResponseStreaming.ILambdaResponseStream"), DiagnosticResult.CompilerError("CS0117").WithSpan($"Amazon.Lambda.RuntimeSupport{Path.DirectorySeparatorChar}Helpers{Path.DirectorySeparatorChar}Logging{Path.DirectorySeparatorChar}ConfigureJsonLogMessageFormatterIsolated.cs", 13, 45, 13, 80).WithArguments("Amazon.Lambda.Core.LambdaLogger", "SetConfigureStructuredLoggingAction"), }; } diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/Common.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/Common.cs index eacaeb43d..6a7f95d8d 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/Common.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/Common.cs @@ -16,6 +16,8 @@ using System; using System.Diagnostics; using System.IO; +using Amazon.Lambda.Core.ResponseStreaming; +using Amazon.Lambda.RuntimeSupport.Client.ResponseStreaming; using Xunit; namespace Amazon.Lambda.RuntimeSupport.UnitTests @@ -88,5 +90,18 @@ public static string GetAllMessages(Exception e) return writer.ToString(); } } + + internal static ImplLambdaResponseStream ConvertToImplLambdaResponseStream(ResponseStream responseStream) + { + var implStream = new ImplLambdaResponseStream(new ImplLambdaResponseStream.Delegates + { + BytesWritten = () => responseStream.BytesWritten, + HasError = () => responseStream.HasError, + Dispose = () => responseStream.Dispose(), + WriteAsync = responseStream.WriteAsync + }); + + return implStream; + } } } diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaBootstrapMultiConcurrencyTests.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaBootstrapMultiConcurrencyTests.cs index 3eb4bbe6e..baf8854b3 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaBootstrapMultiConcurrencyTests.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaBootstrapMultiConcurrencyTests.cs @@ -152,8 +152,6 @@ public async Task ThreadPoolStarvation_BlockingHandlers_AllInvocationsDequeued() var testRuntimeApiClient = new TestMultiConcurrencyRuntimeApiClient(environmentVariables, invocationEvents); - // Use a thread-safe counter to track dequeued invocations - int dequeuedCount = 0; var allDequeuedEvent = new ManualResetEventSlim(false); // Wrap the test client to track dequeue operations in a thread-safe manner diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaResponseStreamingCoreTests.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaResponseStreamingCoreTests.cs index 5da3d5e8b..99e655fa8 100644 --- a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaResponseStreamingCoreTests.cs +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/LambdaResponseStreamingCoreTests.cs @@ -167,7 +167,7 @@ public class LambdaResponseStreamTests var output = new MemoryStream(); await inner.SetHttpOutputStreamAsync(output); - var implStream = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var implStream = Common.ConvertToImplLambdaResponseStream(inner); var lambdaStream = new LambdaResponseStream(implStream); return (lambdaStream, output); } @@ -176,7 +176,7 @@ public class LambdaResponseStreamTests public void LambdaResponseStream_IsStreamSubclass() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.IsAssignableFrom(stream); @@ -186,7 +186,7 @@ public void LambdaResponseStream_IsStreamSubclass() public void CanWrite_IsTrue() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.True(stream.CanWrite); @@ -196,7 +196,7 @@ public void CanWrite_IsTrue() public void CanRead_IsFalse() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.False(stream.CanRead); @@ -206,7 +206,7 @@ public void CanRead_IsFalse() public void CanSeek_IsFalse() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.False(stream.CanSeek); @@ -216,7 +216,7 @@ public void CanSeek_IsFalse() public void Read_ThrowsNotImplementedException() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.Throws(() => stream.Read(new byte[1], 0, 1)); @@ -226,7 +226,7 @@ public void Read_ThrowsNotImplementedException() public void ReadAsync_ThrowsNotImplementedException() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); // ReadAsync throws synchronously (not async) — capture the thrown task @@ -239,7 +239,7 @@ public void ReadAsync_ThrowsNotImplementedException() public void Seek_ThrowsNotImplementedException() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.Throws(() => stream.Seek(0, SeekOrigin.Begin)); @@ -249,7 +249,7 @@ public void Seek_ThrowsNotImplementedException() public void Position_Get_ThrowsNotSupportedException() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.Throws(() => _ = stream.Position); @@ -259,7 +259,7 @@ public void Position_Get_ThrowsNotSupportedException() public void Position_Set_ThrowsNotSupportedException() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.Throws(() => stream.Position = 0); @@ -269,7 +269,7 @@ public void Position_Set_ThrowsNotSupportedException() public void SetLength_ThrowsNotSupportedException() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var stream = new LambdaResponseStream(impl); Assert.Throws(() => stream.SetLength(100)); @@ -342,7 +342,7 @@ public async Task WriteAsync_DelegatesToInnerResponseStream() var output = new MemoryStream(); await inner.SetHttpOutputStreamAsync(output); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); var data = new byte[] { 1, 2, 3 }; await impl.WriteAsync(data, 0, data.Length); @@ -357,7 +357,7 @@ public async Task BytesWritten_ReflectsInnerStreamBytesWritten() var output = new MemoryStream(); await inner.SetHttpOutputStreamAsync(output); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); await impl.WriteAsync(new byte[7], 0, 7); Assert.Equal(7, impl.BytesWritten); @@ -367,7 +367,7 @@ public async Task BytesWritten_ReflectsInnerStreamBytesWritten() public void HasError_InitiallyFalse() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); Assert.False(impl.HasError); } @@ -378,7 +378,7 @@ public void HasError_TrueAfterReportError() var inner = new ResponseStream(Array.Empty()); inner.ReportError(new Exception("test")); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); Assert.True(impl.HasError); } @@ -387,7 +387,7 @@ public void HasError_TrueAfterReportError() public void Dispose_DisposesInnerStream() { var inner = new ResponseStream(Array.Empty()); - var impl = new ResponseStreamLambdaCoreInitializerIsolated.ImplLambdaResponseStream(inner); + var impl = Common.ConvertToImplLambdaResponseStream(inner); // Should not throw impl.Dispose();