diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props index 44b7a873f570..0447c82a69d5 100644 --- a/eng/PatchConfig.props +++ b/eng/PatchConfig.props @@ -27,9 +27,10 @@ Later on, this will be checked using this condition: - @aspnet/signalr; + Microsoft.AspNetCore.AspNetCoreModuleV2; Microsoft.AspNetCore.Authentication.Google; Microsoft.AspNetCore.Http; + Microsoft.AspNetCore.Server.IIS; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp index 6085f6587746..e32e34201667 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp @@ -243,12 +243,6 @@ http_read_request_bytes( fAsync, pdwBytesReceived, pfCompletionPending); - - if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) - { - // We reached the end of the data - hr = S_OK; - } } else { @@ -330,12 +324,6 @@ http_websockets_read_bytes( pDwBytesReceived, pfCompletionPending); - if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) - { - // We reached the end of the data - hr = S_OK; - } - return hr; } diff --git a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs index 00c670945378..abe60ccea6a6 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs @@ -58,6 +58,8 @@ public override void FreeOperationResources(int hr, int bytes) { _inputHandle.Dispose(); } + + protected override bool IsSuccessfulResult(int hr) => hr == NativeMethods.ERROR_HANDLE_EOF; } } } diff --git a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs index 80e9234ea292..395457dc1155 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs @@ -102,7 +102,7 @@ public AsyncContinuation Complete(int hr, int bytes) if (hr != NativeMethods.ERROR_OPERATION_ABORTED) { _result = bytes; - if (hr != NativeMethods.HR_OK) + if (hr != NativeMethods.HR_OK && !IsSuccessfulResult(hr)) { // Treat all errors as the client disconnect _exception = new ConnectionResetException("The client has disconnected", Marshal.GetExceptionForHR(hr)); @@ -126,6 +126,8 @@ public AsyncContinuation Complete(int hr, int bytes) return asyncContinuation; } + protected virtual bool IsSuccessfulResult(int hr) => false; + public virtual void FreeOperationResources(int hr, int bytes) { } protected virtual void ResetOperation() diff --git a/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs b/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs index 413fa777032a..2dac1a234e7d 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs @@ -74,6 +74,8 @@ protected override void ResetOperation() _engine.ReturnOperation(this); } + + protected override bool IsSuccessfulResult(int hr) => hr == NativeMethods.ERROR_HANDLE_EOF; } } } diff --git a/src/Servers/IIS/IIS/src/NativeMethods.cs b/src/Servers/IIS/IIS/src/NativeMethods.cs index 8e5e484edde6..4702d29a5736 100644 --- a/src/Servers/IIS/IIS/src/NativeMethods.cs +++ b/src/Servers/IIS/IIS/src/NativeMethods.cs @@ -14,7 +14,7 @@ internal static class NativeMethods internal const int ERROR_NOT_FOUND = unchecked((int)0x80070490); internal const int ERROR_OPERATION_ABORTED = unchecked((int)0x800703E3); internal const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057); - internal const int COR_E_IO = unchecked((int)0x80131620); + internal const int ERROR_HANDLE_EOF = unchecked((int)0x80070026); private const string KERNEL32 = "kernel32.dll"; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs index 03aa0e16e6b1..ae8bf44217d2 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs @@ -193,5 +193,44 @@ await connection.Receive( await connection.WaitForConnectionClose(); } } + + [ConditionalFact] + [RequiresNewHandler] + public async Task AsyncChunkedPostIsAccepted() + { + // This test sends a lot of request because we are trying to force + // different async completion modes from IIS + for (int i = 0; i < 100; i++) + { + using (var connection = _fixture.CreateTestConnection()) + { + await connection.Send( + "POST /ReadFullBody HTTP/1.1", + $"Transfer-Encoding: chunked", + "Host: localhost", + "Connection: close", + "", + ""); + + await connection.Send("5", + "Hello", + ""); + + await connection.Send( + "0", + "", + ""); + + await connection.Receive( + "HTTP/1.1 200 OK", + ""); + + await connection.ReceiveHeaders(); + await connection.Receive("Completed"); + + await connection.WaitForConnectionClose(); + } + } + } } } diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index eb84272b76cd..c19b4e80db9c 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -306,6 +306,13 @@ private async Task ReadRequestBody(HttpContext ctx) } } + private async Task ReadFullBody(HttpContext ctx) + { + await ReadRequestBody(ctx); + ctx.Response.ContentLength = 9; + await ctx.Response.WriteAsync("Completed"); + } + private async Task WriteManyTimesToResponseBody(HttpContext ctx) { for (var i = 0; i < 10000; i++)