From 5743d740b454874a3d056d2f480297be51abcb71 Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Mon, 6 Mar 2017 17:15:59 -0800 Subject: [PATCH] Convert HTTP parsing FrameTests to IHttpParser tests (#1416). - Also fix an issue in KestrelHttpParser where "Header: \r\n" is parsed with a value of " " instead of "". --- .../Internal/Http/KestrelHttpParser.cs | 2 +- .../BadHttpRequestTests.cs | 42 +- .../FrameTests.cs | 398 ++++-------------- .../HttpParserTests.cs | 383 +++++++++++++++++ test/shared/HttpParsingData.cs | 61 ++- 5 files changed, 531 insertions(+), 355 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/KestrelHttpParser.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/KestrelHttpParser.cs index d8da5a89bc..a5ce732aec 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/KestrelHttpParser.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/KestrelHttpParser.cs @@ -459,7 +459,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http } // Ignore end whitespace - for (; valueEnd > valueStart; valueEnd--) + for (; valueEnd >= valueStart; valueEnd--) { var ch = headerLine[valueEnd]; if (ch != ByteTab && ch != ByteSpace) diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/BadHttpRequestTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/BadHttpRequestTests.cs index 80f127c9d1..565b06c2f1 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/BadHttpRequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/BadHttpRequestTests.cs @@ -1,9 +1,7 @@ // 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.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; @@ -127,25 +125,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests ""); } - public static IEnumerable InvalidRequestLineData => HttpParsingData.InvalidRequestLineData - .Select(requestLine => new object[] + public static TheoryData InvalidRequestLineData + { + get { - requestLine, - $"Invalid request line: {requestLine.Replace("\r", "<0x0D>").Replace("\n", "<0x0A>")}", - }) - .Concat(HttpParsingData.EncodedNullCharInTargetRequestLines.Select(requestLine => new object[] - { - requestLine, - "Invalid request line." - })) - .Concat(HttpParsingData.NullCharInTargetRequestLines.Select(requestLine => new object[] - { - requestLine, - "Invalid request line." - })); + var data = new TheoryData(); + + foreach (var requestLine in HttpParsingData.RequestLineInvalidData) + { + data.Add(requestLine, $"Invalid request line: {requestLine.Replace("\r", "<0x0D>").Replace("\n", "<0x0A>")}"); + } + + foreach (var requestLine in HttpParsingData.RequestLineWithEncodedNullCharInTargetData) + { + data.Add(requestLine, "Invalid request line."); + } + + foreach (var requestLine in HttpParsingData.RequestLineWithNullCharInTargetData) + { + data.Add(requestLine, "Invalid request line."); + } + + return data; + } + } public static TheoryData UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData; - public static IEnumerable InvalidRequestHeaderData => HttpParsingData.InvalidRequestHeaderData; + public static IEnumerable InvalidRequestHeaderData => HttpParsingData.RequestHeaderInvalidData; } } diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs index 46d54d1b40..8aeb066862 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using System.IO.Pipelines; -using System.Linq; using System.Net; using System.Text; using System.Threading; @@ -25,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests { public class FrameTests : IDisposable { - private readonly IPipe _socketInput; + private readonly IPipe _input; private readonly TestFrame _frame; private readonly ServiceContext _serviceContext; private readonly ConnectionContext _connectionContext; @@ -50,7 +49,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests { var trace = new KestrelTrace(new TestKestrelTrace()); _pipelineFactory = new PipeFactory(); - _socketInput = _pipelineFactory.Create(); + _input = _pipelineFactory.Create(); _serviceContext = new ServiceContext { @@ -65,7 +64,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests }; _connectionContext = new ConnectionContext(listenerContext) { - Input = _socketInput, + Input = _input, Output = new MockSocketOutput(), ConnectionControl = Mock.Of() }; @@ -77,149 +76,30 @@ namespace Microsoft.AspNetCore.Server.KestrelTests public void Dispose() { - _socketInput.Reader.Complete(); - _socketInput.Writer.Complete(); + _input.Reader.Complete(); + _input.Writer.Complete(); _pipelineFactory.Dispose(); } - [Fact] - public async Task CanReadHeaderValueWithoutLeadingWhitespace() - { - _frame.InitializeHeaders(); - - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("Header:value\r\n\r\n")); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - var success = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.True(success); - Assert.Equal(1, _frame.RequestHeaders.Count); - Assert.Equal("value", _frame.RequestHeaders["Header"]); - Assert.Equal(readableBuffer.End, _consumed); - } - - [Theory] - [InlineData("Header: value\r\n\r\n")] - [InlineData("Header: value\r\n\r\n")] - [InlineData("Header:\tvalue\r\n\r\n")] - [InlineData("Header: \tvalue\r\n\r\n")] - [InlineData("Header:\t value\r\n\r\n")] - [InlineData("Header:\t\tvalue\r\n\r\n")] - [InlineData("Header:\t\t value\r\n\r\n")] - [InlineData("Header: \t\tvalue\r\n\r\n")] - [InlineData("Header: \t\t value\r\n\r\n")] - [InlineData("Header: \t \t value\r\n\r\n")] - public async Task LeadingWhitespaceIsNotIncludedInHeaderValue(string rawHeaders) - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders)); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - var success = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.True(success); - Assert.Equal(1, _frame.RequestHeaders.Count); - Assert.Equal("value", _frame.RequestHeaders["Header"]); - Assert.Equal(readableBuffer.End, _consumed); - } - - [Theory] - [InlineData("Header: value \r\n\r\n")] - [InlineData("Header: value\t\r\n\r\n")] - [InlineData("Header: value \t\r\n\r\n")] - [InlineData("Header: value\t \r\n\r\n")] - [InlineData("Header: value\t\t\r\n\r\n")] - [InlineData("Header: value\t\t \r\n\r\n")] - [InlineData("Header: value \t\t\r\n\r\n")] - [InlineData("Header: value \t\t \r\n\r\n")] - [InlineData("Header: value \t \t \r\n\r\n")] - public async Task TrailingWhitespaceIsNotIncludedInHeaderValue(string rawHeaders) - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders)); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - var success = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.True(success); - Assert.Equal(1, _frame.RequestHeaders.Count); - Assert.Equal("value", _frame.RequestHeaders["Header"]); - Assert.Equal(readableBuffer.End, _consumed); - } - - [Theory] - [InlineData("Header: one two three\r\n\r\n", "one two three")] - [InlineData("Header: one two three\r\n\r\n", "one two three")] - [InlineData("Header: one\ttwo\tthree\r\n\r\n", "one\ttwo\tthree")] - [InlineData("Header: one two\tthree\r\n\r\n", "one two\tthree")] - [InlineData("Header: one\ttwo three\r\n\r\n", "one\ttwo three")] - [InlineData("Header: one \ttwo \tthree\r\n\r\n", "one \ttwo \tthree")] - [InlineData("Header: one\t two\t three\r\n\r\n", "one\t two\t three")] - [InlineData("Header: one \ttwo\t three\r\n\r\n", "one \ttwo\t three")] - public async Task WhitespaceWithinHeaderValueIsPreserved(string rawHeaders, string expectedValue) - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders)); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - var success = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.True(success); - Assert.Equal(1, _frame.RequestHeaders.Count); - Assert.Equal(expectedValue, _frame.RequestHeaders["Header"]); - Assert.Equal(readableBuffer.End, _consumed); - } - - [Theory] - [MemberData(nameof(InvalidRequestHeaderData))] - public async Task TakeMessageHeadersThrowsOnInvalidRequestHeaders(string rawHeaders, string expectedExceptionMessage) - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders)); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - - var exception = Assert.Throws(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.Equal(expectedExceptionMessage, exception.Message); - Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode); - } - [Fact] public async Task TakeMessageHeadersThrowsOnHeaderValueWithLineFolding_CharacterNotAvailableOnFirstAttempt() { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("Header-1: value1\r\n")); + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes("Header-1: value1\r\n")); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; Assert.False(_frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(" ")); + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes(" ")); - readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + readableBuffer = (await _input.Reader.ReadAsync()).Buffer; var exception = Assert.Throws(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.Equal("Whitespace is not allowed in header name.", exception.Message); Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode); } - [Fact] - public async Task TakeMessageHeadersConsumesBytesCorrectlyAtEnd() - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("Header-1: value1\r\n\r")); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - Assert.False(_frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); - - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("\n")); - - readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - Assert.True(_frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); - } - [Fact] public async Task TakeMessageHeadersThrowsWhenHeadersExceedTotalSizeLimit() { @@ -227,11 +107,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests _serviceContext.ServerOptions.Limits.MaxRequestHeadersTotalSize = headerLine.Length - 1; _frame.Reset(); - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine}\r\n")); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine}\r\n")); + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; var exception = Assert.Throws(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.Equal("Request headers too long.", exception.Message); Assert.Equal(StatusCodes.Status431RequestHeaderFieldsTooLarge, exception.StatusCode); @@ -243,36 +123,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests const string headerLines = "Header-1: value1\r\nHeader-2: value2\r\n"; _serviceContext.ServerOptions.Limits.MaxRequestHeaderCount = 1; - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLines}\r\n")); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLines}\r\n")); + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; var exception = Assert.Throws(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.Equal("Request contains too many headers.", exception.Message); Assert.Equal(StatusCodes.Status431RequestHeaderFieldsTooLarge, exception.StatusCode); } - [Theory] - [InlineData("Cookie: \r\n\r\n", 1)] - [InlineData("Cookie:\r\n\r\n", 1)] - [InlineData("Cookie: \r\nConnection: close\r\n\r\n", 2)] - [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\r\n", 2)] - public async Task EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders) - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders)); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - - var success = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.True(success); - Assert.Equal(numHeaders, _frame.RequestHeaders.Count); - Assert.Equal(readableBuffer.End, _consumed); - } - [Fact] public void ResetResetsScheme() { @@ -296,11 +156,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests options.Limits.MaxRequestHeaderCount = 1; _serviceContext.ServerOptions = options; - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine1}\r\n")); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine1}\r\n")); + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; var takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.True(takeMessageHeaders); Assert.Equal(1, _frame.RequestHeaders.Count); @@ -308,11 +168,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests _frame.Reset(); - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine2}\r\n")); - readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine2}\r\n")); + readableBuffer = (await _input.Reader.ReadAsync()).Buffer; takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.True(takeMessageHeaders); Assert.Equal(1, _frame.RequestHeaders.Count); @@ -407,86 +267,37 @@ namespace Microsoft.AspNetCore.Server.KestrelTests public async Task TakeStartLineSetsFrameProperties( string requestLine, string expectedMethod, - string expectedPath, + string expectedRawTarget, + string expectedRawPath, + string expectedDecodedPath, string expectedQueryString, string expectedHttpVersion) { var requestLineBytes = Encoding.ASCII.GetBytes(requestLine); - await _socketInput.Writer.WriteAsync(requestLineBytes); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + await _input.Writer.WriteAsync(requestLineBytes); + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; var returnValue = _frame.TakeStartLine(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.True(returnValue); Assert.Equal(expectedMethod, _frame.Method); - Assert.Equal(expectedPath, _frame.Path); + Assert.Equal(expectedRawTarget, _frame.RawTarget); + Assert.Equal(expectedDecodedPath, _frame.Path); Assert.Equal(expectedQueryString, _frame.QueryString); Assert.Equal(expectedHttpVersion, _frame.HttpVersion); } - [Fact] - public async Task TakeStartLineCallsConsumingCompleteWithFurthestExamined() - { - var requestLineBytes = Encoding.ASCII.GetBytes("GET / "); - await _socketInput.Writer.WriteAsync(requestLineBytes); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - - _frame.TakeStartLine(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.Equal(readableBuffer.Start, _consumed); - Assert.Equal(readableBuffer.End, _examined); - - requestLineBytes = Encoding.ASCII.GetBytes("HTTP/1.1\r\n"); - await _socketInput.Writer.WriteAsync(requestLineBytes); - readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - - _frame.TakeStartLine(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.Equal(readableBuffer.End, _consumed); - Assert.Equal(readableBuffer.End, _examined); - } - - [Theory] - [InlineData("G")] - [InlineData("GE")] - [InlineData("GET")] - [InlineData("GET ")] - [InlineData("GET /")] - [InlineData("GET / ")] - [InlineData("GET / H")] - [InlineData("GET / HT")] - [InlineData("GET / HTT")] - [InlineData("GET / HTTP")] - [InlineData("GET / HTTP/")] - [InlineData("GET / HTTP/1")] - [InlineData("GET / HTTP/1.")] - [InlineData("GET / HTTP/1.1")] - [InlineData("GET / HTTP/1.1\r")] - public async Task TakeStartLineReturnsWhenGivenIncompleteRequestLines(string requestLine) - { - var requestLineBytes = Encoding.ASCII.GetBytes(requestLine); - await _socketInput.Writer.WriteAsync(requestLineBytes); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - var returnValue = _frame.TakeStartLine(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.False(returnValue); - } - [Fact] public async Task ParseRequestStartsRequestHeadersTimeoutOnFirstByteAvailable() { var connectionControl = new Mock(); _connectionContext.ConnectionControl = connectionControl.Object; - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("G")); + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes("G")); - _frame.ParseRequest((await _socketInput.Reader.ReadAsync()).Buffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); + _frame.ParseRequest((await _input.Reader.ReadAsync()).Buffer, out _consumed, out _examined); + _input.Reader.Advance(_consumed, _examined); var expectedRequestHeadersTimeout = (long)_serviceContext.ServerOptions.Limits.RequestHeadersTimeout.TotalMilliseconds; connectionControl.Verify(cc => cc.ResetTimeout(expectedRequestHeadersTimeout, TimeoutAction.SendTimeoutResponse)); @@ -498,93 +309,42 @@ namespace Microsoft.AspNetCore.Server.KestrelTests _serviceContext.ServerOptions.Limits.MaxRequestLineSize = "GET / HTTP/1.1\r\n".Length; var requestLineBytes = Encoding.ASCII.GetBytes("GET /a HTTP/1.1\r\n"); - await _socketInput.Writer.WriteAsync(requestLineBytes); + await _input.Writer.WriteAsync(requestLineBytes); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; var exception = Assert.Throws(() => _frame.TakeStartLine(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); Assert.Equal("Request line too long.", exception.Message); Assert.Equal(StatusCodes.Status414UriTooLong, exception.StatusCode); } [Theory] - [MemberData(nameof(InvalidRequestLineData))] - public async Task TakeStartLineThrowsOnInvalidRequestLine(string requestLine, Type expectedExceptionType, string expectedExceptionMessage) + [MemberData(nameof(RequestLineWithEncodedNullCharInTargetData))] + public async Task TakeStartLineThrowsOnEncodedNullCharInTarget(string requestLine) { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(requestLine)); - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes(requestLine)); + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; - var exception = Assert.Throws(expectedExceptionType, () => + var exception = Assert.Throws(() => _frame.TakeStartLine(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); + _input.Reader.Advance(_consumed, _examined); - Assert.Equal(expectedExceptionMessage, exception.Message); - - if (expectedExceptionType == typeof(BadHttpRequestException)) - { - Assert.Equal(StatusCodes.Status400BadRequest, (exception as BadHttpRequestException).StatusCode); - } + Assert.Equal("The path contains null characters.", exception.Message); } [Theory] - [MemberData(nameof(UnrecognizedHttpVersionData))] - public async Task TakeStartLineThrowsOnUnrecognizedHttpVersion(string httpVersion) + [MemberData(nameof(RequestLineWithNullCharInTargetData))] + public async Task TakeStartLineThrowsOnNullCharInTarget(string requestLine) { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"GET / {httpVersion}\r\n")); + await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes(requestLine)); + var readableBuffer = (await _input.Reader.ReadAsync()).Buffer; - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; + var exception = Assert.Throws(() => + _frame.TakeStartLine(readableBuffer, out _consumed, out _examined)); + _input.Reader.Advance(_consumed, _examined); - var exception = Assert.Throws(() => _frame.TakeStartLine(readableBuffer, out _consumed, out _examined)); - _socketInput.Reader.Advance(_consumed, _examined); - - Assert.Equal($"Unrecognized HTTP version: {httpVersion}", exception.Message); - Assert.Equal(StatusCodes.Status505HttpVersionNotsupported, exception.StatusCode); - } - - [Fact] - public async Task TakeMessageHeadersCallsConsumingCompleteWithFurthestExamined() - { - foreach (var rawHeader in new[] { "Header: ", "value\r\n", "\r\n" }) - { - await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeader)); - - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined); - _socketInput.Reader.Advance(_consumed, _examined); - Assert.Equal(readableBuffer.End, _examined); - } - } - - [Theory] - [InlineData("\r")] - [InlineData("H")] - [InlineData("He")] - [InlineData("Hea")] - [InlineData("Head")] - [InlineData("Heade")] - [InlineData("Header")] - [InlineData("Header:")] - [InlineData("Header: ")] - [InlineData("Header: v")] - [InlineData("Header: va")] - [InlineData("Header: val")] - [InlineData("Header: valu")] - [InlineData("Header: value")] - [InlineData("Header: value\r")] - [InlineData("Header: value\r\n")] - [InlineData("Header: value\r\n\r")] - public async Task TakeMessageHeadersReturnsWhenGivenIncompleteHeaders(string headers) - { - var headerBytes = Encoding.ASCII.GetBytes(headers); - await _socketInput.Writer.WriteAsync(headerBytes); - - ReadCursor consumed; - ReadCursor examined; - var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer; - - Assert.Equal(false, _frame.TakeMessageHeaders(readableBuffer, out consumed, out examined)); - _socketInput.Reader.Advance(consumed, examined); + Assert.Equal(new InvalidOperationException().Message, exception.Message); } [Fact] @@ -599,7 +359,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests connectionControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.CloseConnection)); _frame.StopAsync(); - _socketInput.Writer.Complete(); + _input.Writer.Complete(); requestProcessingTask.Wait(); } @@ -681,13 +441,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests _frame.Start(); var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"); - await _socketInput.Writer.WriteAsync(data); + await _input.Writer.WriteAsync(data); var requestProcessingTask = _frame.StopAsync(); Assert.IsNotType(typeof(Task), requestProcessingTask); await requestProcessingTask.TimeoutAfter(TimeSpan.FromSeconds(10)); - _socketInput.Writer.Complete(); + _input.Writer.Complete(); } [Fact] @@ -758,30 +518,36 @@ namespace Microsoft.AspNetCore.Server.KestrelTests Assert.NotSame(original, _frame.RequestAborted.WaitHandle); } - public static IEnumerable ValidRequestLineData => HttpParsingData.ValidRequestLineData; + public static IEnumerable ValidRequestLineData => HttpParsingData.RequestLineValidData; - public static IEnumerable InvalidRequestLineData => HttpParsingData.InvalidRequestLineData - .Select(requestLine => new object[] + public static TheoryData RequestLineWithEncodedNullCharInTargetData + { + get { - requestLine, - typeof(BadHttpRequestException), - $"Invalid request line: {requestLine.Replace("\r", "<0x0D>").Replace("\n", "<0x0A>")}", - }) - .Concat(HttpParsingData.EncodedNullCharInTargetRequestLines.Select(requestLine => new object[] - { - requestLine, - typeof(InvalidOperationException), - "The path contains null characters." - })) - .Concat(HttpParsingData.NullCharInTargetRequestLines.Select(requestLine => new object[] - { - requestLine, - typeof(InvalidOperationException), - new InvalidOperationException().Message - })); + var data = new TheoryData(); - public static TheoryData UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData; + foreach (var requestLine in HttpParsingData.RequestLineWithEncodedNullCharInTargetData) + { + data.Add(requestLine); + } - public static IEnumerable InvalidRequestHeaderData => HttpParsingData.InvalidRequestHeaderData; + return data; + } + } + + public static TheoryData RequestLineWithNullCharInTargetData + { + get + { + var data = new TheoryData(); + + foreach (var requestLine in HttpParsingData.RequestLineWithNullCharInTargetData) + { + data.Add(requestLine); + } + + return data; + } + } } } diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs new file mode 100644 index 0000000000..7d16b5f06c --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs @@ -0,0 +1,383 @@ +// 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.Collections.Generic; +using System.IO.Pipelines; +using System.Linq; +using System.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Server.KestrelTests +{ + public class HttpParserTests + { + // Returns true when all headers parsed + // Return false otherwise + + [Theory] + [MemberData(nameof(RequestLineValidData))] + public void ParsesRequestLine( + string requestLine, + string expectedMethod, + string expectedRawTarget, + string expectedRawPath, + string expectedDecodedPath, + string expectedQueryString, + string expectedVersion) + { + var parser = CreateParser(Mock.Of()); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine)); + + string parsedMethod = null; + string parsedVersion = null; + string parsedRawTarget = null; + string parsedRawPath = null; + string parsedQuery = null; + var requestLineHandler = new Mock(); + requestLineHandler + .Setup(handler => handler.OnStartLine( + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny>(), + It.IsAny>(), + It.IsAny>())) + .Callback, Span, Span, Span>((method, version, target, path, query, customMethod) => + { + parsedMethod = method != HttpMethod.Custom ? HttpUtilities.MethodToString(method) : customMethod.GetAsciiStringNonNullCharacters(); + parsedVersion = HttpUtilities.VersionToString(version); + parsedRawTarget = target.GetAsciiStringNonNullCharacters(); + parsedRawPath = path.GetAsciiStringNonNullCharacters(); + parsedQuery = query.GetAsciiStringNonNullCharacters(); + }); + + Assert.True(parser.ParseRequestLine(requestLineHandler.Object, buffer, out var consumed, out var examined)); + + Assert.Equal(parsedMethod, expectedMethod); + Assert.Equal(parsedVersion, expectedVersion); + Assert.Equal(parsedRawTarget, expectedRawTarget); + Assert.Equal(parsedRawPath, expectedRawPath); + Assert.Equal(parsedVersion, expectedVersion); + Assert.Equal(buffer.End, consumed); + Assert.Equal(buffer.End, examined); + } + + [Theory] + [MemberData(nameof(RequestLineIncompleteData))] + public void ParseRequestLineReturnsFalseWhenGivenIncompleteRequestLines(string requestLine) + { + var parser = CreateParser(Mock.Of()); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine)); + + Assert.False(parser.ParseRequestLine(Mock.Of(), buffer, out var consumed, out var examined)); + } + + [Theory] + [MemberData(nameof(RequestLineIncompleteData))] + public void ParseRequestLineDoesNotConsumeIncompleteRequestLine(string requestLine) + { + var parser = CreateParser(Mock.Of()); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine)); + + Assert.False(parser.ParseRequestLine(Mock.Of(), buffer, out var consumed, out var examined)); + + Assert.Equal(buffer.Start, consumed); + Assert.Equal(buffer.End, examined); + } + + [Theory] + [MemberData(nameof(RequestLineInvalidData))] + public void ParseRequestLineThrowsOnInvalidRequestLine(string requestLine) + { + var mockTrace = new Mock(); + mockTrace + .Setup(trace => trace.IsEnabled(LogLevel.Information)) + .Returns(true); + + var parser = CreateParser(mockTrace.Object); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine)); + + var exception = Assert.Throws(() => + parser.ParseRequestLine(Mock.Of(), buffer, out var consumed, out var examined)); + + Assert.Equal($"Invalid request line: {requestLine.Replace("\r", "<0x0D>").Replace("\n", "<0x0A>")}", exception.Message); + Assert.Equal(StatusCodes.Status400BadRequest, (exception as BadHttpRequestException).StatusCode); + } + + [Theory] + [MemberData(nameof(UnrecognizedHttpVersionData))] + public void ParseRequestLineThrowsOnUnrecognizedHttpVersion(string httpVersion) + { + var requestLine = $"GET / {httpVersion}\r\n"; + + var mockTrace = new Mock(); + mockTrace + .Setup(trace => trace.IsEnabled(LogLevel.Information)) + .Returns(true); + + var parser = CreateParser(mockTrace.Object); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine)); + + var exception = Assert.Throws(() => + parser.ParseRequestLine(Mock.Of(), buffer, out var consumed, out var examined)); + + Assert.Equal($"Unrecognized HTTP version: {httpVersion}", exception.Message); + Assert.Equal(StatusCodes.Status505HttpVersionNotsupported, (exception as BadHttpRequestException).StatusCode); + } + + [Theory] + [InlineData("\r")] + [InlineData("H")] + [InlineData("He")] + [InlineData("Hea")] + [InlineData("Head")] + [InlineData("Heade")] + [InlineData("Header")] + [InlineData("Header:")] + [InlineData("Header: ")] + [InlineData("Header: v")] + [InlineData("Header: va")] + [InlineData("Header: val")] + [InlineData("Header: valu")] + [InlineData("Header: value")] + [InlineData("Header: value\r")] + [InlineData("Header: value\r\n")] + [InlineData("Header: value\r\n\r")] + [InlineData("Header-1: value1\r\nH")] + [InlineData("Header-1: value1\r\nHe")] + [InlineData("Header-1: value1\r\nHea")] + [InlineData("Header-1: value1\r\nHead")] + [InlineData("Header-1: value1\r\nHeade")] + [InlineData("Header-1: value1\r\nHeader")] + [InlineData("Header-1: value1\r\nHeader-")] + [InlineData("Header-1: value1\r\nHeader-2")] + [InlineData("Header-1: value1\r\nHeader-2:")] + [InlineData("Header-1: value1\r\nHeader-2: ")] + [InlineData("Header-1: value1\r\nHeader-2: v")] + [InlineData("Header-1: value1\r\nHeader-2: va")] + [InlineData("Header-1: value1\r\nHeader-2: val")] + [InlineData("Header-1: value1\r\nHeader-2: valu")] + [InlineData("Header-1: value1\r\nHeader-2: value")] + [InlineData("Header-1: value1\r\nHeader-2: value2")] + [InlineData("Header-1: value1\r\nHeader-2: value2\r")] + [InlineData("Header-1: value1\r\nHeader-2: value2\r\n")] + [InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r")] + public void ParseHeadersReturnsFalseWhenGivenIncompleteHeaders(string rawHeaders) + { + var parser = CreateParser(Mock.Of()); + + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders)); + Assert.False(parser.ParseHeaders(Mock.Of(), buffer, out var consumed, out var examined, out var consumedBytes)); + } + + [Theory] + [InlineData("\r")] + [InlineData("H")] + [InlineData("He")] + [InlineData("Hea")] + [InlineData("Head")] + [InlineData("Heade")] + [InlineData("Header")] + [InlineData("Header:")] + [InlineData("Header: ")] + [InlineData("Header: v")] + [InlineData("Header: va")] + [InlineData("Header: val")] + [InlineData("Header: valu")] + [InlineData("Header: value")] + [InlineData("Header: value\r")] + public void ParseHeadersDoesNotConsumeIncompleteHeader(string rawHeaders) + { + var parser = CreateParser(Mock.Of()); + + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders)); + parser.ParseHeaders(Mock.Of(), buffer, out var consumed, out var examined, out var consumedBytes); + + Assert.Equal(buffer.Start, consumed); + Assert.Equal(buffer.End, examined); + Assert.Equal(0, consumedBytes); + } + + [Fact] + public void ParseHeadersCanReadHeaderValueWithoutLeadingWhitespace() + { + VerifyHeader("Header", "value", "value"); + } + + [Theory] + [InlineData("Cookie: \r\n\r\n", "Cookie", "", null, null)] + [InlineData("Cookie:\r\n\r\n", "Cookie", "", null, null)] + [InlineData("Cookie: \r\nConnection: close\r\n\r\n", "Cookie", "", "Connection", "close")] + [InlineData("Cookie:\r\nConnection: close\r\n\r\n", "Cookie", "", "Connection", "close")] + [InlineData("Connection: close\r\nCookie: \r\n\r\n", "Connection", "close", "Cookie", "")] + [InlineData("Connection: close\r\nCookie:\r\n\r\n", "Connection", "close", "Cookie", "")] + public void ParseHeadersCanParseEmptyHeaderValues( + string rawHeaders, + string expectedHeaderName1, + string expectedHeaderValue1, + string expectedHeaderName2, + string expectedHeaderValue2) + { + var expectedHeaderNames = expectedHeaderName2 == null + ? new[] { expectedHeaderName1 } + : new[] { expectedHeaderName1, expectedHeaderName2 }; + var expectedHeaderValues = expectedHeaderValue2 == null + ? new[] { expectedHeaderValue1 } + : new[] { expectedHeaderValue1, expectedHeaderValue2 }; + + VerifyRawHeaders(rawHeaders, expectedHeaderNames, expectedHeaderValues); + } + + [Theory] + [InlineData(" value")] + [InlineData(" value")] + [InlineData("\tvalue")] + [InlineData(" \tvalue")] + [InlineData("\t value")] + [InlineData("\t\tvalue")] + [InlineData("\t\t value")] + [InlineData(" \t\tvalue")] + [InlineData(" \t\t value")] + [InlineData(" \t \t value")] + public void ParseHeadersDoesNotIncludeLeadingWhitespaceInHeaderValue(string rawHeaderValue) + { + VerifyHeader("Header", rawHeaderValue, "value"); + } + + [Theory] + [InlineData("value ")] + [InlineData("value\t")] + [InlineData("value \t")] + [InlineData("value\t ")] + [InlineData("value\t\t")] + [InlineData("value\t\t ")] + [InlineData("value \t\t")] + [InlineData("value \t\t ")] + [InlineData("value \t \t ")] + public void ParseHeadersDoesNotIncludeTrailingWhitespaceInHeaderValue(string rawHeaderValue) + { + VerifyHeader("Header", rawHeaderValue, "value"); + } + + [Theory] + [InlineData("one two three")] + [InlineData("one two three")] + [InlineData("one\ttwo\tthree")] + [InlineData("one two\tthree")] + [InlineData("one\ttwo three")] + [InlineData("one \ttwo \tthree")] + [InlineData("one\t two\t three")] + [InlineData("one \ttwo\t three")] + public void ParseHeadersPreservesWhitespaceWithinHeaderValue(string headerValue) + { + VerifyHeader("Header", headerValue, headerValue); + } + + [Fact] + public void ParseHeadersConsumesBytesCorrectlyAtEnd() + { + var parser = CreateParser(Mock.Of()); + + const string headerLine = "Header: value\r\n\r"; + var buffer1 = ReadableBuffer.Create(Encoding.ASCII.GetBytes(headerLine)); + Assert.False(parser.ParseHeaders(Mock.Of(), buffer1, out var consumed, out var examined, out var consumedBytes)); + + Assert.Equal(buffer1.Move(buffer1.Start, headerLine.Length - 1), consumed); + Assert.Equal(buffer1.End, examined); + Assert.Equal(headerLine.Length - 1, consumedBytes); + + var buffer2 = ReadableBuffer.Create(Encoding.ASCII.GetBytes("\r\n")); + Assert.True(parser.ParseHeaders(Mock.Of(), buffer2, out consumed, out examined, out consumedBytes)); + + Assert.Equal(buffer2.End, consumed); + Assert.Equal(buffer2.End, examined); + Assert.Equal(2, consumedBytes); + } + + [Theory] + [MemberData(nameof(RequestHeaderInvalidData))] + public void ParseHeadersThrowsOnInvalidRequestHeaders(string rawHeaders, string expectedExceptionMessage) + { + var parser = CreateParser(Mock.Of()); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders)); + + var exception = Assert.Throws(() => + parser.ParseHeaders(Mock.Of(), buffer, out var consumed, out var examined, out var consumedBytes)); + + Assert.Equal(expectedExceptionMessage, exception.Message); + Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode); + } + + private void VerifyHeader( + string headerName, + string rawHeaderValue, + string expectedHeaderValue) + { + var parser = CreateParser(Mock.Of()); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes($"{headerName}:{rawHeaderValue}\r\n")); + + string parsedHeaderName = "unexpected"; + string parsedHeaderValue = "unexpected"; + var headersHandler = new Mock(); + headersHandler + .Setup(handler => handler.OnHeader(It.IsAny>(), It.IsAny>())) + .Callback, Span>((name, value) => + { + parsedHeaderName = name.GetAsciiStringNonNullCharacters(); + parsedHeaderValue = value.GetAsciiStringNonNullCharacters(); + }); + + parser.ParseHeaders(headersHandler.Object, buffer, out var consumed, out var examined, out var consumedBytes); + + Assert.Equal(headerName, parsedHeaderName); + Assert.Equal(expectedHeaderValue, parsedHeaderValue); + Assert.Equal(buffer.End, consumed); + Assert.Equal(buffer.End, examined); + } + + private void VerifyRawHeaders(string rawHeaders, IEnumerable expectedHeaderNames, IEnumerable expectedHeaderValues) + { + Assert.True(expectedHeaderNames.Count() == expectedHeaderValues.Count(), $"{nameof(expectedHeaderNames)} and {nameof(expectedHeaderValues)} sizes must match"); + + var parser = CreateParser(Mock.Of()); + var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders)); + + var parsedHeaders = new List>(); + var headersHandler = new Mock(); + headersHandler + .Setup(handler => handler.OnHeader(It.IsAny>(), It.IsAny>())) + .Callback, Span>((name, value) => + { + parsedHeaders.Add(Tuple.Create(name.GetAsciiStringNonNullCharacters(), value.GetAsciiStringNonNullCharacters())); + }); + + parser.ParseHeaders(headersHandler.Object, buffer, out var consumed, out var examined, out var consumedBytes); + + Assert.Equal(expectedHeaderNames.Count(), parsedHeaders.Count); + Assert.Equal(expectedHeaderNames, parsedHeaders.Select(t => t.Item1)); + Assert.Equal(expectedHeaderValues, parsedHeaders.Select(t => t.Item2)); + Assert.Equal(buffer.End, consumed); + Assert.Equal(buffer.End, examined); + } + + private IHttpParser CreateParser(IKestrelTrace log) => new KestrelHttpParser(log); + + public static IEnumerable RequestLineValidData => HttpParsingData.RequestLineValidData; + + public static IEnumerable RequestLineIncompleteData => HttpParsingData.RequestLineIncompleteData.Select(requestLine => new[] { requestLine }); + + public static IEnumerable RequestLineInvalidData => HttpParsingData.RequestLineInvalidData.Select(requestLine => new[] { requestLine }); + + public static TheoryData UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData; + + public static IEnumerable RequestHeaderInvalidData => HttpParsingData.RequestHeaderInvalidData; + } +} diff --git a/test/shared/HttpParsingData.cs b/test/shared/HttpParsingData.cs index 72a579feb0..7d84ba37b3 100644 --- a/test/shared/HttpParsingData.cs +++ b/test/shared/HttpParsingData.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Testing { public class HttpParsingData { - public static IEnumerable ValidRequestLineData + public static IEnumerable RequestLineValidData { get { @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Testing "GET", "CUSTOM", }; - var targets = new[] + var paths = new[] { Tuple.Create("/", "/"), Tuple.Create("/abc", "/abc"), @@ -56,21 +56,42 @@ namespace Microsoft.AspNetCore.Testing }; return from method in methods - from target in targets + from path in paths from queryString in queryStrings from httpVersion in httpVersions select new[] { - $"{method} {target.Item1}{queryString} {httpVersion}\r\n", + $"{method} {path.Item1}{queryString} {httpVersion}\r\n", method, - $"{target.Item2}", + $"{path.Item1}{queryString}", + $"{path.Item1}", + $"{path.Item2}", queryString, httpVersion }; } } - public static IEnumerable InvalidRequestLineData => new[] + public static IEnumerable RequestLineIncompleteData => new[] + { + "G", + "GE", + "GET", + "GET ", + "GET /", + "GET / ", + "GET / H", + "GET / HT", + "GET / HTT", + "GET / HTTP", + "GET / HTTP/", + "GET / HTTP/1", + "GET / HTTP/1.", + "GET / HTTP/1.1", + "GET / HTTP/1.1\r", + }; + + public static IEnumerable RequestLineInvalidData => new[] { "G\r\n", "GE\r\n", @@ -140,7 +161,7 @@ namespace Microsoft.AspNetCore.Testing "post= / HTTP/1.0\r\n", }; - public static IEnumerable EncodedNullCharInTargetRequestLines => new[] + public static IEnumerable RequestLineWithEncodedNullCharInTargetData => new[] { "GET /%00 HTTP/1.1\r\n", "GET /%00%00 HTTP/1.1\r\n", @@ -149,17 +170,16 @@ namespace Microsoft.AspNetCore.Testing "GET /%F3%00%82%86 HTTP/1.1\r\n", "GET /%F3%85%00%82 HTTP/1.1\r\n", "GET /%F3%85%82%00 HTTP/1.1\r\n", - "GET /%E8%85%00 HTTP/1.1\r\n", "GET /%E8%01%00 HTTP/1.1\r\n", }; - public static IEnumerable NullCharInTargetRequestLines => new[] - { - "GET \0 HTTP/1.1\r\n", - "GET /\0 HTTP/1.1\r\n", - "GET /\0\0 HTTP/1.1\r\n", - "GET /%C8\0 HTTP/1.1\r\n", - }; + public static IEnumerable RequestLineWithNullCharInTargetData => new[] + { + "GET \0 HTTP/1.1\r\n", + "GET /\0 HTTP/1.1\r\n", + "GET /\0\0 HTTP/1.1\r\n", + "GET /%C8\0 HTTP/1.1\r\n", + }; public static TheoryData UnrecognizedHttpVersionData => new TheoryData { @@ -183,7 +203,7 @@ namespace Microsoft.AspNetCore.Testing "8charact", }; - public static IEnumerable InvalidRequestHeaderData + public static IEnumerable RequestHeaderInvalidData { get { @@ -228,6 +248,7 @@ namespace Microsoft.AspNetCore.Testing "Header-1 value1\r\n\r\n", "Header-1 value1\r\nHeader-2: value2\r\n\r\n", "Header-1: value1\r\nHeader-2 value2\r\n\r\n", + "\n" }; // Starting with whitespace @@ -273,11 +294,11 @@ namespace Microsoft.AspNetCore.Testing return new[] { - Tuple.Create(headersWithLineFolding,"Whitespace is not allowed in header name."), - Tuple.Create(headersWithCRInValue,"Header value must not contain CR characters."), - Tuple.Create(headersWithMissingColon,"No ':' character found in header line."), + Tuple.Create(headersWithLineFolding, "Whitespace is not allowed in header name."), + Tuple.Create(headersWithCRInValue, "Header value must not contain CR characters."), + Tuple.Create(headersWithMissingColon, "No ':' character found in header line."), Tuple.Create(headersStartingWithWhitespace, "Whitespace is not allowed in header name."), - Tuple.Create(headersWithWithspaceInName,"Whitespace is not allowed in header name."), + Tuple.Create(headersWithWithspaceInName, "Whitespace is not allowed in header name."), Tuple.Create(headersNotEndingInCrLfLine, "Headers corrupted, invalid header sequence.") } .SelectMany(t => t.Item1.Select(headers => new[] { headers, t.Item2 }));