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 "".
This commit is contained in:
Cesar Blum Silveira 2017-03-06 17:15:59 -08:00
parent e2f8c226ef
commit 5743d740b4
5 changed files with 531 additions and 355 deletions

View File

@ -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)

View File

@ -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<object> InvalidRequestLineData => HttpParsingData.InvalidRequestLineData
.Select(requestLine => new object[]
public static TheoryData<string, string> 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<string, string>();
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<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.InvalidRequestHeaderData;
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.RequestHeaderInvalidData;
}
}

View File

@ -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<object> _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<IConnectionControl>()
};
@ -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<BadHttpRequestException>(() => _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<BadHttpRequestException>(() => _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<BadHttpRequestException>(() => _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<BadHttpRequestException>(() => _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<IConnectionControl>();
_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<BadHttpRequestException>(() => _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<InvalidOperationException>(() =>
_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<InvalidOperationException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_input.Reader.Advance(_consumed, _examined);
var exception = Assert.Throws<BadHttpRequestException>(() => _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<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<object> ValidRequestLineData => HttpParsingData.ValidRequestLineData;
public static IEnumerable<object> ValidRequestLineData => HttpParsingData.RequestLineValidData;
public static IEnumerable<object> InvalidRequestLineData => HttpParsingData.InvalidRequestLineData
.Select(requestLine => new object[]
public static TheoryData<string> 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<string>();
public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
foreach (var requestLine in HttpParsingData.RequestLineWithEncodedNullCharInTargetData)
{
data.Add(requestLine);
}
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.InvalidRequestHeaderData;
return data;
}
}
public static TheoryData<string> RequestLineWithNullCharInTargetData
{
get
{
var data = new TheoryData<string>();
foreach (var requestLine in HttpParsingData.RequestLineWithNullCharInTargetData)
{
data.Add(requestLine);
}
return data;
}
}
}
}

View File

@ -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<IKestrelTrace>());
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<IHttpRequestLineHandler>();
requestLineHandler
.Setup(handler => handler.OnStartLine(
It.IsAny<HttpMethod>(),
It.IsAny<HttpVersion>(),
It.IsAny<Span<byte>>(),
It.IsAny<Span<byte>>(),
It.IsAny<Span<byte>>(),
It.IsAny<Span<byte>>()))
.Callback<HttpMethod, HttpVersion, Span<byte>, Span<byte>, Span<byte>, Span<byte>>((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<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine));
Assert.False(parser.ParseRequestLine(Mock.Of<IHttpRequestLineHandler>(), buffer, out var consumed, out var examined));
}
[Theory]
[MemberData(nameof(RequestLineIncompleteData))]
public void ParseRequestLineDoesNotConsumeIncompleteRequestLine(string requestLine)
{
var parser = CreateParser(Mock.Of<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(requestLine));
Assert.False(parser.ParseRequestLine(Mock.Of<IHttpRequestLineHandler>(), 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<IKestrelTrace>();
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<BadHttpRequestException>(() =>
parser.ParseRequestLine(Mock.Of<IHttpRequestLineHandler>(), 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<IKestrelTrace>();
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<BadHttpRequestException>(() =>
parser.ParseRequestLine(Mock.Of<IHttpRequestLineHandler>(), 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<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders));
Assert.False(parser.ParseHeaders(Mock.Of<IHttpHeadersHandler>(), 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<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders));
parser.ParseHeaders(Mock.Of<IHttpHeadersHandler>(), 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<IKestrelTrace>());
const string headerLine = "Header: value\r\n\r";
var buffer1 = ReadableBuffer.Create(Encoding.ASCII.GetBytes(headerLine));
Assert.False(parser.ParseHeaders(Mock.Of<IHttpHeadersHandler>(), 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<IHttpHeadersHandler>(), 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<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders));
var exception = Assert.Throws<BadHttpRequestException>(() =>
parser.ParseHeaders(Mock.Of<IHttpHeadersHandler>(), 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<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes($"{headerName}:{rawHeaderValue}\r\n"));
string parsedHeaderName = "unexpected";
string parsedHeaderValue = "unexpected";
var headersHandler = new Mock<IHttpHeadersHandler>();
headersHandler
.Setup(handler => handler.OnHeader(It.IsAny<Span<byte>>(), It.IsAny<Span<byte>>()))
.Callback<Span<byte>, Span<byte>>((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<string> expectedHeaderNames, IEnumerable<string> expectedHeaderValues)
{
Assert.True(expectedHeaderNames.Count() == expectedHeaderValues.Count(), $"{nameof(expectedHeaderNames)} and {nameof(expectedHeaderValues)} sizes must match");
var parser = CreateParser(Mock.Of<IKestrelTrace>());
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders));
var parsedHeaders = new List<Tuple<string, string>>();
var headersHandler = new Mock<IHttpHeadersHandler>();
headersHandler
.Setup(handler => handler.OnHeader(It.IsAny<Span<byte>>(), It.IsAny<Span<byte>>()))
.Callback<Span<byte>, Span<byte>>((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<string[]> RequestLineValidData => HttpParsingData.RequestLineValidData;
public static IEnumerable<object[]> RequestLineIncompleteData => HttpParsingData.RequestLineIncompleteData.Select(requestLine => new[] { requestLine });
public static IEnumerable<object[]> RequestLineInvalidData => HttpParsingData.RequestLineInvalidData.Select(requestLine => new[] { requestLine });
public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
public static IEnumerable<object[]> RequestHeaderInvalidData => HttpParsingData.RequestHeaderInvalidData;
}
}

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Testing
{
public class HttpParsingData
{
public static IEnumerable<string[]> ValidRequestLineData
public static IEnumerable<string[]> 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<string> InvalidRequestLineData => new[]
public static IEnumerable<string> 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<string> 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<string> EncodedNullCharInTargetRequestLines => new[]
public static IEnumerable<string> 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<string> 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<string> 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<string> UnrecognizedHttpVersionData => new TheoryData<string>
{
@ -183,7 +203,7 @@ namespace Microsoft.AspNetCore.Testing
"8charact",
};
public static IEnumerable<object[]> InvalidRequestHeaderData
public static IEnumerable<object[]> 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 }));