diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs index 5571e9c56b..df6761e3e0 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -7828,8 +7828,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _CacheControl) { - output.CopyFrom(_headerBytes, 0, 17); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 0, 17); + output.CopyFromAscii(value); + } } } @@ -7841,8 +7844,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } else foreach(var value in _Connection) { - output.CopyFrom(_headerBytes, 17, 14); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 17, 14); + output.CopyFromAscii(value); + } } } @@ -7854,8 +7860,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } else foreach(var value in _Date) { - output.CopyFrom(_headerBytes, 31, 8); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 31, 8); + output.CopyFromAscii(value); + } } } @@ -7863,8 +7872,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _KeepAlive) { - output.CopyFrom(_headerBytes, 39, 14); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 39, 14); + output.CopyFromAscii(value); + } } } @@ -7872,8 +7884,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Pragma) { - output.CopyFrom(_headerBytes, 53, 10); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 53, 10); + output.CopyFromAscii(value); + } } } @@ -7881,8 +7896,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Trailer) { - output.CopyFrom(_headerBytes, 63, 11); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 63, 11); + output.CopyFromAscii(value); + } } } @@ -7894,8 +7912,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } else foreach(var value in _TransferEncoding) { - output.CopyFrom(_headerBytes, 74, 21); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 74, 21); + output.CopyFromAscii(value); + } } } @@ -7903,8 +7924,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Upgrade) { - output.CopyFrom(_headerBytes, 95, 11); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 95, 11); + output.CopyFromAscii(value); + } } } @@ -7912,8 +7936,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Via) { - output.CopyFrom(_headerBytes, 106, 7); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 106, 7); + output.CopyFromAscii(value); + } } } @@ -7921,8 +7948,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Warning) { - output.CopyFrom(_headerBytes, 113, 11); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 113, 11); + output.CopyFromAscii(value); + } } } @@ -7930,8 +7960,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Allow) { - output.CopyFrom(_headerBytes, 124, 9); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 124, 9); + output.CopyFromAscii(value); + } } } @@ -7943,8 +7976,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } else foreach(var value in _ContentLength) { - output.CopyFrom(_headerBytes, 133, 18); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 133, 18); + output.CopyFromAscii(value); + } } } @@ -7952,8 +7988,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentType) { - output.CopyFrom(_headerBytes, 151, 16); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 151, 16); + output.CopyFromAscii(value); + } } } @@ -7961,8 +8000,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentEncoding) { - output.CopyFrom(_headerBytes, 167, 20); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 167, 20); + output.CopyFromAscii(value); + } } } @@ -7970,8 +8012,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentLanguage) { - output.CopyFrom(_headerBytes, 187, 20); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 187, 20); + output.CopyFromAscii(value); + } } } @@ -7979,8 +8024,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentLocation) { - output.CopyFrom(_headerBytes, 207, 20); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 207, 20); + output.CopyFromAscii(value); + } } } @@ -7988,8 +8036,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentMD5) { - output.CopyFrom(_headerBytes, 227, 15); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 227, 15); + output.CopyFromAscii(value); + } } } @@ -7997,8 +8048,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentRange) { - output.CopyFrom(_headerBytes, 242, 17); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 242, 17); + output.CopyFromAscii(value); + } } } @@ -8006,8 +8060,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Expires) { - output.CopyFrom(_headerBytes, 259, 11); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 259, 11); + output.CopyFromAscii(value); + } } } @@ -8015,8 +8072,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _LastModified) { - output.CopyFrom(_headerBytes, 270, 17); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 270, 17); + output.CopyFromAscii(value); + } } } @@ -8024,8 +8084,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _AcceptRanges) { - output.CopyFrom(_headerBytes, 287, 17); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 287, 17); + output.CopyFromAscii(value); + } } } @@ -8033,8 +8096,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Age) { - output.CopyFrom(_headerBytes, 304, 7); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 304, 7); + output.CopyFromAscii(value); + } } } @@ -8042,8 +8108,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ETag) { - output.CopyFrom(_headerBytes, 311, 8); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 311, 8); + output.CopyFromAscii(value); + } } } @@ -8051,8 +8120,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Location) { - output.CopyFrom(_headerBytes, 319, 12); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 319, 12); + output.CopyFromAscii(value); + } } } @@ -8060,8 +8132,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ProxyAutheticate) { - output.CopyFrom(_headerBytes, 331, 21); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 331, 21); + output.CopyFromAscii(value); + } } } @@ -8069,8 +8144,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _RetryAfter) { - output.CopyFrom(_headerBytes, 352, 15); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 352, 15); + output.CopyFromAscii(value); + } } } @@ -8082,8 +8160,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } else foreach(var value in _Server) { - output.CopyFrom(_headerBytes, 367, 10); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 367, 10); + output.CopyFromAscii(value); + } } } @@ -8091,8 +8172,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _SetCookie) { - output.CopyFrom(_headerBytes, 377, 14); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 377, 14); + output.CopyFromAscii(value); + } } } @@ -8100,8 +8184,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Vary) { - output.CopyFrom(_headerBytes, 391, 8); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 391, 8); + output.CopyFromAscii(value); + } } } @@ -8109,8 +8196,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _WWWAuthenticate) { - output.CopyFrom(_headerBytes, 399, 20); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_headerBytes, 399, 20); + output.CopyFromAscii(value); + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs index 693c58e912..a5a42e6a98 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs @@ -39,10 +39,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach (var value in kv.Value) { - output.CopyFrom(_CrLf, 0, 2); - output.CopyFromAscii(kv.Key); - output.CopyFrom(_colonSpace, 0, 2); - output.CopyFromAscii(value); + if (value != null) + { + output.CopyFrom(_CrLf, 0, 2); + output.CopyFromAscii(kv.Key); + output.CopyFrom(_colonSpace, 0, 2); + output.CopyFromAscii(value); + } } } } diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs index c2479c388c..67e8203e05 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs @@ -2,12 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.Http; using Microsoft.AspNet.Testing.xunit; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests @@ -44,7 +47,7 @@ namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests await context.Response.Body.WriteAsync(bytes, 0, bytes.Length); } }); - }); + }); using (var app = hostBuilder.Build().Start()) { @@ -70,5 +73,73 @@ namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests } } } + + [ConditionalTheory, MemberData(nameof(NullHeaderData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on mono.")] + public async Task IgnoreNullHeaderValues(string headerName, StringValues headerValue, string expectedValue) + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "server.urls", "http://localhost:8793/" } + }) + .Build(); + + var hostBuilder = new WebHostBuilder(config) + .UseServerFactory("Microsoft.AspNet.Server.Kestrel") + .UseStartup(app => + { + app.Run(async context => + { + context.Response.Headers.Add(headerName, headerValue); + + await context.Response.WriteAsync(""); + }); + }); + + using (var app = hostBuilder.Build().Start()) + { + using (var client = new HttpClient()) + { + var response = await client.GetAsync("http://localhost:8793/"); + response.EnsureSuccessStatusCode(); + + var headers = response.Headers; + + if (expectedValue == null) + { + Assert.False(headers.Contains(headerName)); + } + else + { + Assert.True(headers.Contains(headerName)); + Assert.Equal(headers.GetValues(headerName).Single(), expectedValue); + } + } + } + } + + public static TheoryData NullHeaderData + { + get + { + var dataset = new TheoryData(); + + // Unknown headers + dataset.Add("NullString", (string)null, null); + dataset.Add("EmptyString", "", ""); + dataset.Add("NullStringArray", new string[] { null }, null); + dataset.Add("EmptyStringArray", new string[] { "" }, ""); + dataset.Add("MixedStringArray", new string[] { null, "" }, ""); + // Known headers + dataset.Add("Location", (string)null, null); + dataset.Add("Location", "", ""); + dataset.Add("Location", new string[] { null }, null); + dataset.Add("Location", new string[] { "" }, ""); + dataset.Add("Location", new string[] { null, "" }, ""); + + return dataset; + } + } } } diff --git a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs index aaf19e8300..4dd45dcfbe 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -391,8 +391,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http }} else ")} foreach(var value in _{header.Identifier}) {{ - output.CopyFrom(_headerBytes, {header.BytesOffset}, {header.BytesCount}); - output.CopyFromAscii(value); + if (value != null) + {{ + output.CopyFrom(_headerBytes, {header.BytesOffset}, {header.BytesCount}); + output.CopyFromAscii(value); + }} }} }} ")}