diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 79552ea944..f05d250a4b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -157,7 +157,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput)) + while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) @@ -654,7 +654,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return Encoding.UTF8.GetString(range.Array, range.Offset + startIndex, endIndex - startIndex); } - private bool TakeMessageHeaders(SocketInput input) + public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders) { var scan = input.ConsumingStart(); var consumed = scan; @@ -692,6 +692,22 @@ namespace Microsoft.AspNet.Server.Kestrel.Http chSecond == '\r' || chSecond == '\n') { + if (chSecond == '\r') + { + var scanAhead = scan; + var chAhead = scanAhead.Take(); + if (chAhead == '\n') + { + chAhead = scanAhead.Take(); + // If the "\r\n" isn't part of "linear whitespace", + // then this header has no value. + if (chAhead != ' ' && chAhead != '\t') + { + break; + } + } + } + beginValue = scan; chSecond = scan.Take(); } @@ -729,9 +745,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } var name = beginName.GetArraySegment(endName); -#if DEBUG - var nameString = beginName.GetString(endName); -#endif var value = beginValue.GetString(endValue); if (wrapping) { @@ -739,7 +752,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } consumed = scan; - _requestHeaders.Append(name.Array, name.Offset, name.Count, value); + requestHeaders.Append(name.Array, name.Offset, name.Count, value); break; } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs index ec73fcd3b2..8266a1c2e9 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs @@ -1,9 +1,11 @@ // 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 Microsoft.AspNet.Server.Kestrel.Http; +using System; using System.Linq; using System.Text; +using Microsoft.AspNet.Server.Kestrel.Http; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Xunit; namespace Microsoft.AspNet.Server.KestrelTests @@ -34,5 +36,34 @@ namespace Microsoft.AspNet.Server.KestrelTests Assert.Equal(Encoding.ASCII.GetBytes(expected), beginChunkBytes.ToArray()); } + + [Theory] + [InlineData("Cookie: \r\n\r\n", 1)] + [InlineData("Cookie:\r\n\r\n", 1)] + [InlineData("Cookie:\r\n value\r\n\r\n", 1)] + [InlineData("Cookie\r\n", 0)] + [InlineData("Cookie: \r\nConnection: close\r\n\r\n", 2)] + [InlineData("Connection: close\r\nCookie: \r\n\r\n", 2)] + [InlineData("Connection: close\r\nCookie \r\n", 1)] + [InlineData("Connection:\r\n \r\nCookie \r\n", 1)] + public void EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders) + { + var socketInput = new SocketInput(new MemoryPool2()); + var headerCollection = new FrameRequestHeaders(); + + var headerArray = Encoding.ASCII.GetBytes(rawHeaders); + var inputBuffer = socketInput.IncomingStart(headerArray.Length); + Buffer.BlockCopy(headerArray, 0, inputBuffer.Data.Array, inputBuffer.Data.Offset, headerArray.Length); + socketInput.IncomingComplete(headerArray.Length, null); + + var success = Frame.TakeMessageHeaders(socketInput, headerCollection); + + Assert.True(success); + Assert.Equal(numHeaders, headerCollection.Count()); + + // Assert TakeMessageHeaders consumed all the input + var scan = socketInput.ConsumingStart(); + Assert.True(scan.IsEnd); + } } }