diff --git a/src/IISIntegration/IISIntegration.NoV1.sln b/src/IISIntegration/IISIntegration.NoV1.sln index e0e3d4967e96..c0985af5556c 100644 --- a/src/IISIntegration/IISIntegration.NoV1.sln +++ b/src/IISIntegration/IISIntegration.NoV1.sln @@ -38,7 +38,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7E80C58E build\applicationhost.iis.config = build\applicationhost.iis.config build\Build.Settings = build\Build.Settings build\Config.Definitions.Props = build\Config.Definitions.Props - build\dependencies.props = build\dependencies.props build\functional-test-assets.targets = build\functional-test-assets.targets build\Key.snk = build\Key.snk build\launchSettings.json = build\launchSettings.json @@ -116,7 +115,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.BackwardsCompatibility. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.ForwardsCompatibility.FunctionalTests", "test\IIS.ForwardsCompatibility.FunctionalTests\IIS.ForwardsCompatibility.FunctionalTests.csproj", "{D1EA5D99-28FD-4197-81DE-17098846B38B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessForwardsCompatWebSite", "test\WebSites\InProcessForwardsCompatWebSite\InProcessWebSite.csproj", "{BBBC85B2-5D7A-4D09-90B1-8DBCC9059493}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "test\WebSites\InProcessForwardsCompatWebSite\InProcessWebSite.csproj", "{BBBC85B2-5D7A-4D09-90B1-8DBCC9059493}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/IISIntegration/src/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp b/src/IISIntegration/src/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp index 2185f975a4b9..6085f6587746 100644 --- a/src/IISIntegration/src/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp +++ b/src/IISIntegration/src/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp @@ -288,13 +288,13 @@ EXTERN_C __MIDL_DECLSPEC_DLLEXPORT HRESULT http_flush_response_bytes( _In_ IN_PROCESS_HANDLER* pInProcessHandler, + _In_ BOOL fMoreData, _Out_ BOOL* pfCompletionExpected ) { IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse(); BOOL fAsync = TRUE; - BOOL fMoreData = TRUE; DWORD dwBytesSent = 0; HRESULT hr = pHttpResponse->Flush( diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.IO.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.IO.cs index e945500672b9..119cac01e37b 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.IO.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.IO.cs @@ -158,7 +158,7 @@ private async Task WriteBody(bool flush = false) if (flush) { - await AsyncIO.FlushAsync(); + await AsyncIO.FlushAsync(moreData: true); flush = false; } } diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs index 09e478a989b0..a0d28247f56e 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs @@ -243,12 +243,12 @@ private async Task ProduceStart(bool flushHeaders) SetResponseHeaders(); EnsureIOInitialized(); - + var canHaveNonEmptyBody = StatusCodeCanHaveBody(); if (flushHeaders) { try { - await AsyncIO.FlushAsync(); + await AsyncIO.FlushAsync(canHaveNonEmptyBody); } // Client might be disconnected at this point // don't leak the exception @@ -258,7 +258,20 @@ private async Task ProduceStart(bool flushHeaders) } } - _writeBodyTask = WriteBody(!flushHeaders); + if (!canHaveNonEmptyBody) + { + _bodyOutput.Dispose(); + } + else + { + _writeBodyTask = WriteBody(!flushHeaders); + } + } + + private bool StatusCodeCanHaveBody() + { + return StatusCode != 204 + && StatusCode != 304; } private void InitializeRequestIO() diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.Flush.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.Flush.cs index 1187c5b3f246..c83cbf3106fe 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.Flush.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.Flush.cs @@ -12,21 +12,23 @@ internal class AsyncFlushOperation : AsyncIOOperation private readonly AsyncIOEngine _engine; private IntPtr _requestHandler; + private bool _moreData; public AsyncFlushOperation(AsyncIOEngine engine) { _engine = engine; } - public void Initialize(IntPtr requestHandler) + public void Initialize(IntPtr requestHandler, bool moreData) { _requestHandler = requestHandler; + _moreData = moreData; } protected override bool InvokeOperation(out int hr, out int bytes) { bytes = 0; - hr = NativeMethods.HttpFlushResponseBytes(_requestHandler, out var fCompletionExpected); + hr = NativeMethods.HttpFlushResponseBytes(_requestHandler, _moreData, out var fCompletionExpected); return !fCompletionExpected; } @@ -36,6 +38,7 @@ protected override void ResetOperation() base.ResetOperation(); _requestHandler = default; + _moreData = false; _engine.ReturnOperation(this); } } diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.cs index f169cebf7746..12f980248e48 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOEngine.cs @@ -90,10 +90,10 @@ private void Run(AsyncIOOperation ioOperation) } - public ValueTask FlushAsync() + public ValueTask FlushAsync(bool moreData) { var flush = GetFlushOperation(); - flush.Initialize(_handler); + flush.Initialize(_handler, moreData); Run(flush); return new ValueTask(flush, 0); } diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/IAsyncIOEngine.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/IAsyncIOEngine.cs index 6e6896b8c462..295b4c0e7c2a 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/IAsyncIOEngine.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/IAsyncIOEngine.cs @@ -11,7 +11,7 @@ internal interface IAsyncIOEngine: IDisposable { ValueTask ReadAsync(Memory memory); ValueTask WriteAsync(ReadOnlySequence data); - ValueTask FlushAsync(); + ValueTask FlushAsync(bool moreData); void NotifyCompletion(int hr, int bytes); } } diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.Initialize.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.Initialize.cs index 10e1f0da7dd4..6abd79de8e45 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.Initialize.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.Initialize.cs @@ -25,7 +25,7 @@ public void Initialize(IntPtr requestHandler) protected override bool InvokeOperation(out int hr, out int bytes) { - hr = NativeMethods.HttpFlushResponseBytes(_requestHandler, out var completionExpected); + hr = NativeMethods.HttpFlushResponseBytes(_requestHandler, fMoreData: true, out var completionExpected); bytes = 0; return !completionExpected; } diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.cs index 38c737dbcee7..a796af23b34e 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/Core/IO/WebSocketsAsyncIOEngine.cs @@ -56,7 +56,7 @@ public ValueTask WriteAsync(ReadOnlySequence data) } } - public ValueTask FlushAsync() + public ValueTask FlushAsync(bool moreData) { lock (_contextLock) { diff --git a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/NativeMethods.cs b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/NativeMethods.cs index 4d24b3dd779c..8e5e484edde6 100644 --- a/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/NativeMethods.cs +++ b/src/IISIntegration/src/Microsoft.AspNetCore.Server.IIS/NativeMethods.cs @@ -67,7 +67,7 @@ private static extern int register_callbacks(IntPtr pInProcessApplication, private static extern unsafe int http_write_response_bytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] - private static extern int http_flush_response_bytes(IntPtr pInProcessHandler, out bool fCompletionExpected); + private static extern int http_flush_response_bytes(IntPtr pInProcessHandler, bool fMoreData, out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] private static extern unsafe HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(IntPtr pInProcessHandler); @@ -171,9 +171,9 @@ public static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpAp return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); } - public static int HttpFlushResponseBytes(IntPtr pInProcessHandler, out bool fCompletionExpected) + public static int HttpFlushResponseBytes(IntPtr pInProcessHandler, bool fMoreData, out bool fCompletionExpected) { - return http_flush_response_bytes(pInProcessHandler, out fCompletionExpected); + return http_flush_response_bytes(pInProcessHandler, fMoreData, out fCompletionExpected); } public static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(IntPtr pInProcessHandler) diff --git a/src/IISIntegration/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs b/src/IISIntegration/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs index fec5c227ecf9..a779f73585a1 100644 --- a/src/IISIntegration/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs +++ b/src/IISIntegration/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs @@ -1,8 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Net.Http.Headers; @@ -75,6 +77,18 @@ public async Task CustomErrorCodeWorks(int code, string reason, string expectedR Assert.Equal(body ?? string.Empty, await response.Content.ReadAsStringAsync()); } + [ConditionalTheory] + [RequiresNewHandler] + [InlineData(204, "GET")] + [InlineData(304, "GET")] + public async Task TransferEncodingNotSetForStatusCodes(int code, string method) + { + var request = new HttpRequestMessage(new HttpMethod(method), _fixture.Client.BaseAddress + $"SetCustomErorCode?code={code}"); + var response = await _fixture.Client.SendAsync(request); + Assert.Equal((HttpStatusCode)code, response.StatusCode); + Assert.DoesNotContain(response.Headers, h => h.Key.Equals("transfer-encoding", StringComparison.InvariantCultureIgnoreCase)); + } + [ConditionalFact] public async Task ServerHeaderIsOverriden() {