Improve HTTP parsing tests (#1393).
- Add several more test cases - Share data between functional and unit tests
This commit is contained in:
parent
f5ac8c4ebd
commit
568aaff9c4
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26206.0
|
||||
VisualStudioVersion = 15.0.26223.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
|
@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC
|
|||
ProjectSection(SolutionItems) = preProject
|
||||
test\shared\DummyApplication.cs = test\shared\DummyApplication.cs
|
||||
test\shared\HttpClientSlim.cs = test\shared\HttpClientSlim.cs
|
||||
test\shared\HttpParsingData.cs = test\shared\HttpParsingData.cs
|
||||
test\shared\KestrelTestLoggerFactory.cs = test\shared\KestrelTestLoggerFactory.cs
|
||||
test\shared\LifetimeNotImplemented.cs = test\shared\LifetimeNotImplemented.cs
|
||||
test\shared\MockConnection.cs = test\shared\MockConnection.cs
|
||||
|
|
@ -83,38 +84,38 @@ Global
|
|||
{F510611A-3BEE-4B88-A613-5F4A74ED82A1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x64.Build.0 = Debug|x64
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x86.Build.0 = Debug|x86
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|x64.ActiveCfg = Release|x64
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|x64.Build.0 = Release|x64
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x64.Build.0 = Debug|x64
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x86.Build.0 = Debug|x86
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|x64.ActiveCfg = Release|x64
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|x64.Build.0 = Release|x64
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x64.Build.0 = Debug|x64
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x86.Build.0 = Debug|x86
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|x64.ActiveCfg = Release|x64
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|x64.Build.0 = Release|x64
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B35D4D31-E74C-4646-8A11-7A7A40F0021E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{BD2D4D29-1BD9-40D0-BB31-337D5416B63C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -143,26 +144,26 @@ Global
|
|||
{5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x64.Build.0 = Debug|x64
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x86.Build.0 = Debug|x86
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x64.ActiveCfg = Release|x64
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x64.Build.0 = Release|x64
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x64.Build.0 = Debug|x64
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x86.Build.0 = Debug|x86
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x64.ActiveCfg = Release|x64
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x64.Build.0 = Release|x64
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
|
|
|
|||
|
|
@ -1129,6 +1129,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
}
|
||||
else if (ch == BytePercentage)
|
||||
{
|
||||
if (pathStart == -1)
|
||||
{
|
||||
// Empty path is illegal
|
||||
RejectRequestLine(start, end);
|
||||
}
|
||||
|
||||
needDecode = true;
|
||||
}
|
||||
|
||||
|
|
@ -1268,7 +1274,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
const int MaxRequestLineError = 32;
|
||||
RejectRequest(RequestRejectionReason.InvalidRequestLine,
|
||||
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxRequestLineError) : string.Empty);
|
||||
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxRequestLineError) : string.Empty);
|
||||
}
|
||||
|
||||
private static bool IsValidTokenChar(char c)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
{
|
||||
public class BadHttpRequestTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidRequestLineData))]
|
||||
public async Task TestInvalidRequestLines(string request)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(request);
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(UnrecognizedHttpVersionData))]
|
||||
public async Task TestInvalidRequestLinesWithUnrecognizedVersion(string httpVersion)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll($"GET / {httpVersion}\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "505 HTTP Version Not Supported", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidRequestHeaderData))]
|
||||
public async Task TestInvalidHeaders(string rawHeaders)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll($"GET / HTTP/1.1\r\n{rawHeaders}");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BadRequestWhenHeaderNameContainsNonASCIICharacters()
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(
|
||||
"GET / HTTP/1.1",
|
||||
"H\u00eb\u00e4d\u00ebr: value",
|
||||
"",
|
||||
"");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("POST")]
|
||||
[InlineData("PUT")]
|
||||
public async Task BadRequestIfMethodRequiresLengthButNoContentLengthOrTransferEncodingInRequest(string method)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send($"{method} / HTTP/1.1\r\n\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "411 Length Required", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("POST")]
|
||||
[InlineData("PUT")]
|
||||
public async Task BadRequestIfMethodRequiresLengthButNoContentLengthInHttp10Request(string method)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send($"{method} / HTTP/1.0\r\n\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NaN")]
|
||||
[InlineData("-1")]
|
||||
public async Task BadRequestIfContentLengthInvalid(string contentLength)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll($"GET / HTTP/1.1\r\nContent-Length: {contentLength}\r\n\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveBadRequestResponse(TestConnection connection, string expectedResponseStatusCode, string expectedDateHeaderValue)
|
||||
{
|
||||
await connection.ReceiveForcedEnd(
|
||||
$"HTTP/1.1 {expectedResponseStatusCode}",
|
||||
"Connection: close",
|
||||
$"Date: {expectedDateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
|
||||
public static IEnumerable<object> InvalidRequestLineData => HttpParsingData.InvalidRequestLineData.Select(data => new[] { data[0] });
|
||||
|
||||
public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
|
||||
|
||||
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.InvalidRequestHeaderData.Select(data => new[] { data[0] });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,231 +0,0 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||
{
|
||||
public class BadHttpRequestTests
|
||||
{
|
||||
// All test cases for this theory must end in '\n', otherwise the server will spin forever
|
||||
[Theory]
|
||||
// Incomplete request lines
|
||||
[InlineData("G\r\n")]
|
||||
[InlineData("GE\r\n")]
|
||||
[InlineData("GET\r\n")]
|
||||
[InlineData("GET \r\n")]
|
||||
[InlineData("GET /\r\n")]
|
||||
[InlineData("GET / \r\n")]
|
||||
// Missing method
|
||||
[InlineData(" \r\n")]
|
||||
// Missing second space
|
||||
[InlineData("/ \r\n")] // This fails trying to read the '/' because that's invalid for an HTTP method
|
||||
[InlineData("GET /\r\n")]
|
||||
// Missing target
|
||||
[InlineData("GET \r\n")]
|
||||
// Missing version
|
||||
[InlineData("GET / \r\n")]
|
||||
// Missing CR
|
||||
[InlineData("GET / \n")]
|
||||
// Missing LF after CR
|
||||
[InlineData("GET / HTTP/1.0\rA\n")]
|
||||
// Bad HTTP Methods (invalid according to RFC)
|
||||
[InlineData("( / HTTP/1.0\r\n")]
|
||||
[InlineData(") / HTTP/1.0\r\n")]
|
||||
[InlineData("< / HTTP/1.0\r\n")]
|
||||
[InlineData("> / HTTP/1.0\r\n")]
|
||||
[InlineData("@ / HTTP/1.0\r\n")]
|
||||
[InlineData(", / HTTP/1.0\r\n")]
|
||||
[InlineData("; / HTTP/1.0\r\n")]
|
||||
[InlineData(": / HTTP/1.0\r\n")]
|
||||
[InlineData("\\ / HTTP/1.0\r\n")]
|
||||
[InlineData("\" / HTTP/1.0\r\n")]
|
||||
[InlineData("/ / HTTP/1.0\r\n")]
|
||||
[InlineData("[ / HTTP/1.0\r\n")]
|
||||
[InlineData("] / HTTP/1.0\r\n")]
|
||||
[InlineData("? / HTTP/1.0\r\n")]
|
||||
[InlineData("= / HTTP/1.0\r\n")]
|
||||
[InlineData("{ / HTTP/1.0\r\n")]
|
||||
[InlineData("} / HTTP/1.0\r\n")]
|
||||
[InlineData("get@ / HTTP/1.0\r\n")]
|
||||
[InlineData("post= / HTTP/1.0\r\n")]
|
||||
public async Task TestInvalidRequestLines(string request)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(request);
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET / H\r\n")]
|
||||
[InlineData("GET / HT\r\n")]
|
||||
[InlineData("GET / HTT\r\n")]
|
||||
[InlineData("GET / HTTP\r\n")]
|
||||
[InlineData("GET / HTTP/\r\n")]
|
||||
[InlineData("GET / HTTP/1\r\n")]
|
||||
[InlineData("GET / HTTP/1.\r\n")]
|
||||
[InlineData("GET / http/1.0\r\n")]
|
||||
[InlineData("GET / http/1.1\r\n")]
|
||||
[InlineData("GET / HTTP/1.1 \r\n")]
|
||||
[InlineData("GET / HTTP/1.1a\r\n")]
|
||||
[InlineData("GET / HTTP/1.2\r\n")]
|
||||
[InlineData("GET / HTTP/3.0\r\n")]
|
||||
[InlineData("GET / H\r\n")]
|
||||
[InlineData("GET / HTTP/1.\r\n")]
|
||||
[InlineData("GET / hello\r\n")]
|
||||
[InlineData("GET / 8charact\r\n")]
|
||||
public async Task TestInvalidRequestLinesWithUnsupportedVersion(string request)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(request);
|
||||
await ReceiveBadRequestResponse(connection, "505 HTTP Version Not Supported", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Leading whitespace
|
||||
[InlineData(" Header-1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\n Header-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\n\tHeader-2: value2\r\n\r\n")]
|
||||
// Missing LF
|
||||
[InlineData("Header-1: value1\rHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\r\n")]
|
||||
// Line folding
|
||||
[InlineData("Header-1: multi\r\n line\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: multi\r\n line\r\n\r\n")]
|
||||
// Missing ':'
|
||||
[InlineData("Header-1 value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2 value2\r\n\r\n")]
|
||||
// Whitespace in header name
|
||||
[InlineData("Header 1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader 2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1 : value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1\t: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2 : value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2\t: value2\r\n\r\n")]
|
||||
public async Task TestInvalidHeaders(string rawHeaders)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll($"GET / HTTP/1.1\r\n{rawHeaders}");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BadRequestWhenNameHeaderNamesContainsNonASCIICharacters()
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(
|
||||
"GET / HTTP/1.1",
|
||||
"H\u00eb\u00e4d\u00ebr: value",
|
||||
"",
|
||||
"");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("\0")]
|
||||
[InlineData("%00")]
|
||||
[InlineData("/\0")]
|
||||
[InlineData("/%00")]
|
||||
[InlineData("/\0\0")]
|
||||
[InlineData("/%00%00")]
|
||||
[InlineData("/%C8\0")]
|
||||
[InlineData("/%E8%00%84")]
|
||||
[InlineData("/%E8%85%00")]
|
||||
[InlineData("/%F3%00%82%86")]
|
||||
[InlineData("/%F3%85%00%82")]
|
||||
[InlineData("/%F3%85%82%00")]
|
||||
[InlineData("/%E8%85%00")]
|
||||
[InlineData("/%E8%01%00")]
|
||||
public async Task BadRequestIfPathContainsNullCharacters(string path)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll($"GET {path} HTTP/1.1\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("POST")]
|
||||
[InlineData("PUT")]
|
||||
public async Task BadRequestIfMethodRequiresLengthButNoContentLengthOrTransferEncodingInRequest(string method)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send($"{method} / HTTP/1.1\r\n\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "411 Length Required", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("POST")]
|
||||
[InlineData("PUT")]
|
||||
public async Task BadRequestIfMethodRequiresLengthButNoContentLengthInHttp10Request(string method)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send($"{method} / HTTP/1.0\r\n\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NaN")]
|
||||
[InlineData("-1")]
|
||||
public async Task BadRequestIfContentLengthInvalid(string contentLength)
|
||||
{
|
||||
using (var server = new TestServer(context => { return Task.FromResult(0); }))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll($"GET / HTTP/1.1\r\nContent-Length: {contentLength}\r\n\r\n");
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveBadRequestResponse(TestConnection connection, string expectedResponseStatusCode, string expectedDateHeaderValue)
|
||||
{
|
||||
await connection.ReceiveForcedEnd(
|
||||
$"HTTP/1.1 {expectedResponseStatusCode}",
|
||||
"Connection: close",
|
||||
$"Date: {expectedDateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// 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;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
|
|
@ -27,10 +28,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
private readonly TestFrame<object> _frame;
|
||||
private readonly ServiceContext _serviceContext;
|
||||
private readonly ConnectionContext _connectionContext;
|
||||
private PipeFactory _pipelineFactory;
|
||||
|
||||
ReadCursor consumed;
|
||||
ReadCursor examined;
|
||||
private readonly PipeFactory _pipelineFactory;
|
||||
private ReadCursor _consumed;
|
||||
private ReadCursor _examined;
|
||||
|
||||
private class TestFrame<TContext> : Frame<TContext>
|
||||
{
|
||||
|
|
@ -88,13 +88,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
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, (FrameRequestHeaders) _frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, 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);
|
||||
Assert.Equal(readableBuffer.End, _consumed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -113,13 +113,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, 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);
|
||||
Assert.Equal(readableBuffer.End, _consumed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -137,13 +137,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, 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);
|
||||
Assert.Equal(readableBuffer.End, _consumed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -160,32 +160,26 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, 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);
|
||||
Assert.Equal(readableBuffer.End, _consumed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Header: line1\r\n line2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n\tline2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n line2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n \tline2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n\t line2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n\t\tline2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n \t\t line2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n \t \t line2\r\n\r\n")]
|
||||
public async Task TakeMessageHeadersThrowsOnHeaderValueWithLineFolding(string rawHeaders)
|
||||
[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;
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
Assert.Equal("Header value line folding not supported.", exception.Message);
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal(expectedExceptionMessage, exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
|
|
@ -195,107 +189,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("Header-1: value1\r\n"));
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
Assert.False(_frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
Assert.False(_frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(" "));
|
||||
|
||||
readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal("Header value line folding not supported.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Header-1: value1\r\r\n")]
|
||||
[InlineData("Header-1: val\rue1\r\n")]
|
||||
[InlineData("Header-1: value1\rHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: v\ralue2\r\n")]
|
||||
public async Task TakeMessageHeadersThrowsOnHeaderValueContainingCR(string rawHeaders)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
Assert.Equal("Header value must not contain CR characters.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Header-1 value1\r\n\r\n")]
|
||||
[InlineData("Header-1 value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2 value2\r\n\r\n")]
|
||||
public async Task TakeMessageHeadersThrowsOnHeaderLineMissingColon(string rawHeaders)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
Assert.Equal("No ':' character found in header line.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(" Header: value\r\n\r\n")]
|
||||
[InlineData("\tHeader: value\r\n\r\n")]
|
||||
[InlineData(" Header-1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
public async Task TakeMessageHeadersThrowsOnHeaderLineStartingWithWhitespace(string rawHeaders)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
Assert.Equal("Header line must not start with whitespace.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Header : value\r\n\r\n")]
|
||||
[InlineData("Header\t: value\r\n\r\n")]
|
||||
[InlineData("Header 1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header 1 : value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header 1\t: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader 2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2 : value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2\t: value2\r\n\r\n")]
|
||||
public async Task TakeMessageHeadersThrowsOnWhitespaceInHeaderName(string rawHeaders)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
Assert.Equal("Whitespace is not allowed in header name.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r\r")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r ")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r \n")]
|
||||
public async Task TakeMessageHeadersThrowsOnHeadersNotEndingInCRLFLine(string rawHeaders)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
Assert.Equal("Headers corrupted, invalid header sequence.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeMessageHeadersThrowsWhenHeadersExceedTotalSizeLimit()
|
||||
{
|
||||
|
|
@ -306,8 +212,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine}\r\n"));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal("Request headers too long.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status431RequestHeaderFieldsTooLarge, exception.StatusCode);
|
||||
|
|
@ -322,8 +228,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLines}\r\n"));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal("Request contains too many headers.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status431RequestHeaderFieldsTooLarge, exception.StatusCode);
|
||||
|
|
@ -341,12 +247,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(rawHeaders));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var success = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.True(success);
|
||||
Assert.Equal(numHeaders, _frame.RequestHeaders.Count);
|
||||
Assert.Equal(readableBuffer.End, consumed);
|
||||
Assert.Equal(readableBuffer.End, _consumed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -375,8 +281,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine1}\r\n"));
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.True(takeMessageHeaders);
|
||||
Assert.Equal(1, _frame.RequestHeaders.Count);
|
||||
|
|
@ -387,8 +293,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine2}\r\n"));
|
||||
readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.True(takeMessageHeaders);
|
||||
Assert.Equal(1, _frame.RequestHeaders.Count);
|
||||
|
|
@ -478,6 +384,29 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
Assert.Same(originalDuplexStream, _frame.DuplexStream);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ValidRequestLineData))]
|
||||
public async Task TakeStartLineSetsFrameProperties(
|
||||
string requestLine,
|
||||
string expectedMethod,
|
||||
string expectedPath,
|
||||
string expectedQueryString,
|
||||
string expectedHttpVersion)
|
||||
{
|
||||
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.True(returnValue);
|
||||
Assert.Equal(expectedMethod, _frame.Method);
|
||||
Assert.Equal(expectedPath, _frame.Path);
|
||||
Assert.Equal(expectedQueryString, _frame.QueryString);
|
||||
Assert.Equal(expectedHttpVersion, _frame.HttpVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeStartLineCallsConsumingCompleteWithFurthestExamined()
|
||||
{
|
||||
|
|
@ -485,21 +414,21 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(requestLineBytes);
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
_frame.TakeStartLine(readableBuffer, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal(readableBuffer.Start, consumed);
|
||||
Assert.Equal(readableBuffer.End, 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);
|
||||
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal(readableBuffer.End, consumed);
|
||||
Assert.Equal(readableBuffer.End, examined);
|
||||
Assert.Equal(readableBuffer.End, _consumed);
|
||||
Assert.Equal(readableBuffer.End, _examined);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -524,8 +453,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
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);
|
||||
var returnValue = _frame.TakeStartLine(readableBuffer, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.False(returnValue);
|
||||
}
|
||||
|
|
@ -538,8 +467,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("G"));
|
||||
|
||||
_frame.TakeStartLine((await _socketInput.Reader.ReadAsync()).Buffer, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
_frame.TakeStartLine((await _socketInput.Reader.ReadAsync()).Buffer, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
var expectedRequestHeadersTimeout = (long)_serviceContext.ServerOptions.Limits.RequestHeadersTimeout.TotalMilliseconds;
|
||||
connectionControl.Verify(cc => cc.ResetTimeout(expectedRequestHeadersTimeout, TimeoutAction.SendTimeoutResponse));
|
||||
|
|
@ -554,79 +483,58 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _socketInput.Writer.WriteAsync(requestLineBytes);
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeStartLine(readableBuffer, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() =>_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal("Request line too long.", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status414UriTooLong, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET/HTTP/1.1\r\n", "Invalid request line: GET/HTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData(" / HTTP/1.1\r\n", "Invalid request line: / HTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET? / HTTP/1.1\r\n", "Invalid request line: GET? / HTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET /HTTP/1.1\r\n", "Invalid request line: GET /HTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET /a?b=cHTTP/1.1\r\n", "Invalid request line: GET /a?b=cHTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET /a%20bHTTP/1.1\r\n", "Invalid request line: GET /a%20bHTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET /a%20b?c=dHTTP/1.1\r\n", "Invalid request line: GET /a%20b?c=dHTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET HTTP/1.1\r\n", "Invalid request line: GET HTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET / HTTP/1.1\n", "Invalid request line: GET / HTTP/1.1<0x0A>")]
|
||||
[InlineData("GET / \r\n", "Invalid request line: GET / <0x0D><0x0A>")]
|
||||
[InlineData("GET ? HTTP/1.1\r\n", "Invalid request line: GET ? HTTP/1.1<0x0D><0x0A>")]
|
||||
[InlineData("GET / HTTP/1.1\ra\n", "Invalid request line: GET / HTTP/1.1<0x0D>a<0x0A>")]
|
||||
public async Task TakeStartLineThrowsWhenInvalid(string requestLine, string expectedExceptionMessage)
|
||||
[MemberData(nameof(InvalidRequestLineData))]
|
||||
public async Task TakeStartLineThrowsOnInvalidRequestLine(string requestLine, Type expectedExceptionType, string expectedExceptionMessage)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes(requestLine));
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeStartLine(readableBuffer, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
var exception = Assert.Throws(expectedExceptionType, () =>
|
||||
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
|
||||
Assert.Equal(expectedExceptionMessage, exception.Message);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
|
||||
if (expectedExceptionType == typeof(BadHttpRequestException))
|
||||
{
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, (exception as BadHttpRequestException).StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeStartLineThrowsOnUnsupportedHttpVersion()
|
||||
[Theory]
|
||||
[MemberData(nameof(UnrecognizedHttpVersionData))]
|
||||
public async Task TakeStartLineThrowsOnUnrecognizedHttpVersion(string httpVersion)
|
||||
{
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.2\r\n"));
|
||||
await _socketInput.Writer.WriteAsync(Encoding.ASCII.GetBytes($"GET / {httpVersion}\r\n"));
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeStartLine(readableBuffer, out consumed, out examined));
|
||||
_socketInput.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: HTTP/1.2", exception.Message);
|
||||
Assert.Equal(StatusCodes.Status505HttpVersionNotsupported, exception.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeStartLineThrowsOnUnsupportedHttpVersionLongerThanEightCharacters()
|
||||
{
|
||||
var requestLineBytes = Encoding.ASCII.GetBytes("GET / HTTP/1.1ab\r\n");
|
||||
await _socketInput.Writer.WriteAsync(requestLineBytes);
|
||||
|
||||
var readableBuffer = (await _socketInput.Reader.ReadAsync()).Buffer;
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeStartLine(readableBuffer, out consumed, out examined));
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
|
||||
Assert.Equal("Unrecognized HTTP version: HTTP/1.1ab", exception.Message);
|
||||
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"})
|
||||
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, (FrameRequestHeaders)_frame.RequestHeaders, out consumed, out examined);
|
||||
_socketInput.Reader.Advance(consumed, examined);
|
||||
Assert.Equal(readableBuffer.End, examined);
|
||||
_frame.TakeMessageHeaders(readableBuffer, (FrameRequestHeaders)_frame.RequestHeaders, out _consumed, out _examined);
|
||||
_socketInput.Reader.Advance(_consumed, _examined);
|
||||
Assert.Equal(readableBuffer.End, _examined);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -831,5 +739,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
await _frame.ProduceEndAsync();
|
||||
Assert.NotSame(original, _frame.RequestAborted.WaitHandle);
|
||||
}
|
||||
|
||||
public static IEnumerable<object> ValidRequestLineData => HttpParsingData.ValidRequestLineData;
|
||||
|
||||
public static IEnumerable<object> InvalidRequestLineData => HttpParsingData.InvalidRequestLineData;
|
||||
|
||||
public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
|
||||
|
||||
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.InvalidRequestHeaderData;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
// 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 Microsoft.AspNetCore.Server.Kestrel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class HttpParsingData
|
||||
{
|
||||
public static IEnumerable<string[]> ValidRequestLineData
|
||||
{
|
||||
get
|
||||
{
|
||||
var methods = new[]
|
||||
{
|
||||
"GET",
|
||||
"CUSTOM",
|
||||
};
|
||||
var targets = new[]
|
||||
{
|
||||
Tuple.Create("/", "/"),
|
||||
Tuple.Create("/abc", "/abc"),
|
||||
Tuple.Create("/abc/de/f", "/abc/de/f"),
|
||||
Tuple.Create("/%20", "/ "),
|
||||
Tuple.Create("/a%20", "/a "),
|
||||
Tuple.Create("/%20a", "/ a"),
|
||||
Tuple.Create("/a/b%20c", "/a/b c"),
|
||||
Tuple.Create("/%C3%A5", "/\u00E5"),
|
||||
Tuple.Create("/a%C3%A5a", "/a\u00E5a"),
|
||||
Tuple.Create("/%C3%A5/bc", "/\u00E5/bc"),
|
||||
Tuple.Create("/%25", "/%"),
|
||||
Tuple.Create("/%2F", "/%2F"),
|
||||
};
|
||||
var queryStrings = new[]
|
||||
{
|
||||
"",
|
||||
"?",
|
||||
"?arg1=val1",
|
||||
"?arg1=a%20b",
|
||||
"?%A",
|
||||
"?%20=space",
|
||||
"?%C3%A5=val",
|
||||
"?path=/home",
|
||||
"?path=/%C3%A5/",
|
||||
"?question=what?",
|
||||
"?%00",
|
||||
"?arg=%00"
|
||||
};
|
||||
var httpVersions = new[]
|
||||
{
|
||||
"HTTP/1.0",
|
||||
"HTTP/1.1"
|
||||
};
|
||||
|
||||
return from method in methods
|
||||
from target in targets
|
||||
from queryString in queryStrings
|
||||
from httpVersion in httpVersions
|
||||
select new[]
|
||||
{
|
||||
$"{method} {target.Item1}{queryString} {httpVersion}\r\n",
|
||||
method,
|
||||
$"{target.Item2}",
|
||||
queryString,
|
||||
httpVersion
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// All these test cases must end in '\n', otherwise the server will spin forever
|
||||
public static IEnumerable<object[]> InvalidRequestLineData
|
||||
{
|
||||
get
|
||||
{
|
||||
var invalidRequestLines = new[]
|
||||
{
|
||||
"G\r\n",
|
||||
"GE\r\n",
|
||||
"GET\r\n",
|
||||
"GET \r\n",
|
||||
"GET /\r\n",
|
||||
"GET / \r\n",
|
||||
"GET/HTTP/1.1\r\n",
|
||||
"GET /HTTP/1.1\r\n",
|
||||
" \r\n",
|
||||
" \r\n",
|
||||
"/ HTTP/1.1\r\n",
|
||||
" / HTTP/1.1\r\n",
|
||||
"/ \r\n",
|
||||
"GET \r\n",
|
||||
"GET HTTP/1.0\r\n",
|
||||
"GET HTTP/1.1\r\n",
|
||||
"GET / \n",
|
||||
"GET / HTTP/1.0\n",
|
||||
"GET / HTTP/1.1\n",
|
||||
"GET / HTTP/1.0\rA\n",
|
||||
"GET / HTTP/1.1\ra\n",
|
||||
"GET? / HTTP/1.1\r\n",
|
||||
"GET ? HTTP/1.1\r\n",
|
||||
"GET /a?b=cHTTP/1.1\r\n",
|
||||
"GET /a%20bHTTP/1.1\r\n",
|
||||
"GET /a%20b?c=dHTTP/1.1\r\n",
|
||||
"GET %2F HTTP/1.1\r\n",
|
||||
"GET %00 HTTP/1.1\r\n",
|
||||
"CUSTOM \r\n",
|
||||
"CUSTOM /\r\n",
|
||||
"CUSTOM / \r\n",
|
||||
"CUSTOM /HTTP/1.1\r\n",
|
||||
"CUSTOM \r\n",
|
||||
"CUSTOM HTTP/1.0\r\n",
|
||||
"CUSTOM HTTP/1.1\r\n",
|
||||
"CUSTOM / \n",
|
||||
"CUSTOM / HTTP/1.0\n",
|
||||
"CUSTOM / HTTP/1.1\n",
|
||||
"CUSTOM / HTTP/1.0\rA\n",
|
||||
"CUSTOM / HTTP/1.1\ra\n",
|
||||
"CUSTOM ? HTTP/1.1\r\n",
|
||||
"CUSTOM /a?b=cHTTP/1.1\r\n",
|
||||
"CUSTOM /a%20bHTTP/1.1\r\n",
|
||||
"CUSTOM /a%20b?c=dHTTP/1.1\r\n",
|
||||
"CUSTOM %2F HTTP/1.1\r\n",
|
||||
"CUSTOM %00 HTTP/1.1\r\n",
|
||||
// Bad HTTP Methods (invalid according to RFC)
|
||||
"( / HTTP/1.0\r\n",
|
||||
") / HTTP/1.0\r\n",
|
||||
"< / HTTP/1.0\r\n",
|
||||
"> / HTTP/1.0\r\n",
|
||||
"@ / HTTP/1.0\r\n",
|
||||
", / HTTP/1.0\r\n",
|
||||
"; / HTTP/1.0\r\n",
|
||||
": / HTTP/1.0\r\n",
|
||||
"\\ / HTTP/1.0\r\n",
|
||||
"\" / HTTP/1.0\r\n",
|
||||
"/ / HTTP/1.0\r\n",
|
||||
"[ / HTTP/1.0\r\n",
|
||||
"] / HTTP/1.0\r\n",
|
||||
"? / HTTP/1.0\r\n",
|
||||
"= / HTTP/1.0\r\n",
|
||||
"{ / HTTP/1.0\r\n",
|
||||
"} / HTTP/1.0\r\n",
|
||||
"get@ / HTTP/1.0\r\n",
|
||||
"post= / HTTP/1.0\r\n",
|
||||
};
|
||||
|
||||
var encodedNullCharInTargetRequestLines = new[]
|
||||
{
|
||||
"GET /%00 HTTP/1.1\r\n",
|
||||
"GET /%00%00 HTTP/1.1\r\n",
|
||||
"GET /%E8%00%84 HTTP/1.1\r\n",
|
||||
"GET /%E8%85%00 HTTP/1.1\r\n",
|
||||
"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",
|
||||
};
|
||||
|
||||
var 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",
|
||||
};
|
||||
|
||||
return invalidRequestLines.Select(requestLine => new object[]
|
||||
{
|
||||
requestLine,
|
||||
typeof(BadHttpRequestException),
|
||||
$"Invalid request line: {requestLine.Replace("\r", "<0x0D>").Replace("\n", "<0x0A>")}"
|
||||
})
|
||||
.Concat(encodedNullCharInTargetRequestLines.Select(requestLine => new object[]
|
||||
{
|
||||
requestLine,
|
||||
typeof(InvalidOperationException),
|
||||
$"The path contains null characters."
|
||||
}))
|
||||
.Concat(nullCharInTargetRequestLines.Select(requestLine => new object[]
|
||||
{
|
||||
requestLine,
|
||||
typeof(InvalidOperationException),
|
||||
new InvalidOperationException().Message
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData<string> UnrecognizedHttpVersionData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
"H",
|
||||
"HT",
|
||||
"HTT",
|
||||
"HTTP",
|
||||
"HTTP/",
|
||||
"HTTP/1",
|
||||
"HTTP/1.",
|
||||
"http/1.0",
|
||||
"http/1.1",
|
||||
"HTTP/1.1 ",
|
||||
"HTTP/1.0a",
|
||||
"HTTP/1.0ab",
|
||||
"HTTP/1.1a",
|
||||
"HTTP/1.1ab",
|
||||
"HTTP/1.2",
|
||||
"HTTP/3.0",
|
||||
"hello",
|
||||
"8charact",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> InvalidRequestHeaderData
|
||||
{
|
||||
get
|
||||
{
|
||||
// Line folding
|
||||
var headersWithLineFolding = new[]
|
||||
{
|
||||
"Header: line1\r\n line2\r\n\r\n",
|
||||
"Header: line1\r\n\tline2\r\n\r\n",
|
||||
"Header: line1\r\n line2\r\n\r\n",
|
||||
"Header: line1\r\n \tline2\r\n\r\n",
|
||||
"Header: line1\r\n\t line2\r\n\r\n",
|
||||
"Header: line1\r\n\t\tline2\r\n\r\n",
|
||||
"Header: line1\r\n \t\t line2\r\n\r\n",
|
||||
"Header: line1\r\n \t \t line2\r\n\r\n",
|
||||
"Header-1: multi\r\n line\r\nHeader-2: value2\r\n\r\n",
|
||||
"Header-1: value1\r\nHeader-2: multi\r\n line\r\n\r\n",
|
||||
"Header-1: value1\r\n Header-2: value2\r\n\r\n",
|
||||
"Header-1: value1\r\n\tHeader-2: value2\r\n\r\n",
|
||||
};
|
||||
|
||||
// CR in value
|
||||
var headersWithCRInValue = new[]
|
||||
{
|
||||
"Header-1: value1\r\r\n",
|
||||
"Header-1: val\rue1\r\n",
|
||||
"Header-1: value1\rHeader-2: value2\r\n\r\n",
|
||||
"Header-1: value1\r\nHeader-2: value2\r\r\n",
|
||||
"Header-1: value1\r\nHeader-2: v\ralue2\r\n",
|
||||
};
|
||||
|
||||
// Missing colon
|
||||
var headersWithMissingColon = new[]
|
||||
{
|
||||
"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",
|
||||
};
|
||||
|
||||
// Starting with whitespace
|
||||
var headersStartingWithWhitespace = new[]
|
||||
{
|
||||
" Header: value\r\n\r\n",
|
||||
"\tHeader: value\r\n\r\n",
|
||||
" Header-1: value1\r\nHeader-2: value2\r\n\r\n",
|
||||
"\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n",
|
||||
};
|
||||
|
||||
// Whitespace in header name
|
||||
var headersWithWithspaceInName = new[]
|
||||
{
|
||||
"Header : value\r\n\r\n",
|
||||
"Header\t: value\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",
|
||||
"Header 1\t: value1\r\nHeader-2: value2\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",
|
||||
"Header-1: value1\r\nHeader-2\t: value2\r\n\r\n",
|
||||
};
|
||||
|
||||
// Headers not ending in CRLF line
|
||||
var headersNotEndingInCrLfLine = new[]
|
||||
{
|
||||
"Header-1: value1\r\nHeader-2: value2\r\n\r\r",
|
||||
"Header-1: value1\r\nHeader-2: value2\r\n\r ",
|
||||
"Header-1: value1\r\nHeader-2: value2\r\n\r \n",
|
||||
};
|
||||
|
||||
return new[]
|
||||
{
|
||||
Tuple.Create(headersWithLineFolding,"Header value line folding not supported."),
|
||||
Tuple.Create(headersWithCRInValue,"Header value must not contain CR characters."),
|
||||
Tuple.Create(headersWithMissingColon,"No ':' character found in header line."),
|
||||
Tuple.Create(headersStartingWithWhitespace, "Header line must not start with whitespace."),
|
||||
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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue