diff --git a/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs b/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs index a9eecba00b..45848183ba 100644 --- a/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs +++ b/src/Microsoft.AspNetCore.Server.IIS/Core/IISHttpContext.cs @@ -318,29 +318,23 @@ namespace Microsoft.AspNetCore.Server.IIS.Core { var headerValues = headerPair.Value; var knownHeaderIndex = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key); - if (knownHeaderIndex == -1) + for (var i = 0; i < headerValues.Count; i++) { - var headerNameBytes = Encoding.UTF8.GetBytes(headerPair.Key); - for (var i = 0; i < headerValues.Count; i++) + var isFirst = i == 0; + var headerValueBytes = Encoding.UTF8.GetBytes(headerValues[i]); + fixed (byte* pHeaderValue = headerValueBytes) { - var headerValueBytes = Encoding.UTF8.GetBytes(headerValues[i]); - fixed (byte* pHeaderName = headerNameBytes) + if (knownHeaderIndex == -1) { - fixed (byte* pHeaderValue = headerValueBytes) + var headerNameBytes = Encoding.UTF8.GetBytes(headerPair.Key); + fixed (byte* pHeaderName = headerNameBytes) { - NativeMethods.HttpResponseSetUnknownHeader(_pInProcessHandler, pHeaderName, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false); + NativeMethods.HttpResponseSetUnknownHeader(_pInProcessHandler, pHeaderName, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: isFirst); } } - } - } - else - { - for (var i = 0; i < headerValues.Count; i++) - { - var headerValueBytes = Encoding.UTF8.GetBytes(headerValues[i]); - fixed (byte* pHeaderValue = headerValueBytes) + else { - NativeMethods.HttpResponseSetKnownHeader(_pInProcessHandler, knownHeaderIndex, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false); + NativeMethods.HttpResponseSetKnownHeader(_pInProcessHandler, knownHeaderIndex, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: isFirst); } } } diff --git a/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs b/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs index 3f433b155f..fec5c227ec 100644 --- a/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs +++ b/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs @@ -74,5 +74,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests // ReadAsStringAsync returns empty string for empty results Assert.Equal(body ?? string.Empty, await response.Content.ReadAsStringAsync()); } + + [ConditionalFact] + public async Task ServerHeaderIsOverriden() + { + var response = await _fixture.Client.GetAsync("OverrideServer"); + Assert.Equal("MyServer/7.8", response.Headers.Server.Single().Product.ToString()); + } } } diff --git a/test/Common.Tests/Utilities/TestConnections.cs b/test/Common.Tests/Utilities/TestConnections.cs index 3364a0e021..3b7a870cf3 100644 --- a/test/Common.Tests/Utilities/TestConnections.cs +++ b/test/Common.Tests/Utilities/TestConnections.cs @@ -62,18 +62,15 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting public async Task Send(params string[] lines) { - var text = string.Join("\r\n", lines); - var writer = new StreamWriter(_stream, Encoding.GetEncoding("iso-8859-1")); - for (var index = 0; index < text.Length; index++) + var bytes = Encoding.ASCII.GetBytes(string.Join("\r\n", lines)); + + for (var index = 0; index < bytes.Length; index++) { - var ch = text[index]; - writer.Write(ch); - await writer.FlushAsync().ConfigureAwait(false); + await _stream.WriteAsync(bytes, index, 1).ConfigureAwait(false); + await _stream.FlushAsync().ConfigureAwait(false); // Re-add delay to help find socket input consumption bugs more consistently //await Task.Delay(TimeSpan.FromMilliseconds(5)); } - await writer.FlushAsync().ConfigureAwait(false); - await _stream.FlushAsync().ConfigureAwait(false); } public async Task ReadCharAsync() diff --git a/test/IIS.Tests/ClientDisconnectTests.cs b/test/IIS.Tests/ClientDisconnectTests.cs index ee55959a93..9c8855be47 100644 --- a/test/IIS.Tests/ClientDisconnectTests.cs +++ b/test/IIS.Tests/ClientDisconnectTests.cs @@ -206,6 +206,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [ConditionalFact] public async Task ReaderThrowsResetExceptionOnInvalidBody() { + var requestStartedCompletionSource = CreateTaskCompletionSource(); var requestCompletedCompletionSource = CreateTaskCompletionSource(); Exception exception = null; @@ -213,6 +214,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests var data = new byte[1024]; using (var testServer = await TestServer.Create(async ctx => { + requestStartedCompletionSource.SetResult(true); try { await ctx.Request.Body.ReadAsync(data); @@ -233,10 +235,19 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests "Host: localhost", "Connection: close", "", - "ZZZ", ""); - await requestCompletedCompletionSource.Task.DefaultTimeout(); + + await requestStartedCompletionSource.Task; + await connection.Send( + "ZZZZZZZZZZZZZ"); + + await connection.Receive( + "HTTP/1.1 400 Bad Request", + "" + ); + } + await requestCompletedCompletionSource.Task.DefaultTimeout(); } Assert.IsType(exception); diff --git a/test/WebSites/shared/SharedStartup/Startup.shared.cs b/test/WebSites/shared/SharedStartup/Startup.shared.cs index bf9e3bb7fd..375902faea 100644 --- a/test/WebSites/shared/SharedStartup/Startup.shared.cs +++ b/test/WebSites/shared/SharedStartup/Startup.shared.cs @@ -82,5 +82,11 @@ namespace TestSite } await context.Response.WriteAsync(_applicationInitializationCalled.ToString()); } + + public Task OverrideServer(HttpContext context) + { + context.Response.Headers["Server"] = "MyServer/7.8"; + return Task.CompletedTask; + } } }