Reject HTTP/1.1 requests that do not have a correct Host header

Improves Kestrel to reject requests that don't conform to HTTP spec.

RFC 7230 section 5.4: "A server MUST respond with a 400 (Bad Request)
status code to any HTTP/1.1 request message that lacks a Host header
 field and to any request message that contains more than one Host
 header field or a Host header field with an invalid field-value."

See https://tools.ietf.org/html/rfc7230#section-5.4.

Other changes:

 - update VS code settings to work better with CLI 2.0
 - update tests that were subject to infinite hangs
This commit is contained in:
Nate McMaster 2017-04-12 12:05:22 -07:00
parent 9e37272f06
commit c08c57f764
25 changed files with 398 additions and 94 deletions

6
.vscode/launch.json vendored
View File

@ -12,8 +12,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Compile: solution",
"program": "${workspaceRoot}/samples/SampleApp/bin/Debug/netcoreapp1.1/SampleApp.dll",
"args": [],
"program": "${workspaceRoot}/samples/SampleApp/bin/Debug/netcoreapp2.0/SampleApp.dll",
"cwd": "${workspaceRoot}/samples/SampleApp",
"console": "internalConsole",
"stopAtEntry": false,
@ -38,8 +37,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Compile: solution",
"program": "${workspaceRoot}/samples/LargeResponseApp/bin/Debug/netcoreapp1.1/LargeResponseApp.dll",
"args": [],
"program": "${workspaceRoot}/samples/LargeResponseApp/bin/Debug/netcoreapp2.0/LargeResponseApp.dll",
"cwd": "${workspaceRoot}/samples/LargeResponseApp",
"console": "internalConsole",
"stopAtEntry": false,

51
.vscode/tasks.json vendored
View File

@ -5,10 +5,20 @@
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE": "true"
}
},
// requires that you first run build.cmd or build.sh to install local builds of dotnet
"windows": {
"command": "${env.LOCALAPPDATA}/Microsoft/dotnet/dotnet.exe"
},
"osx": {
"command": "${env.HOME}/.dotnet/dotnet"
},
"linux": {
"command": "${env.HOME}/.dotnet/dotnet"
},
"suppressTaskName": true,
"tasks": [
{
"taskName": "Restore: solution",
"command": "dotnet",
"args": [
"restore"
]
@ -16,7 +26,6 @@
{
"taskName": "Compile: solution",
"isBuildCommand": true,
"command": "dotnet",
"args": [
"build",
"${workspaceRoot}/KestrelHttpServer.sln"
@ -40,9 +49,16 @@
}
}
},
{
"taskName": "Test",
"args": [
"test"
],
"problemMatcher": "$msCompile",
"isTestCommand": true
},
{
"taskName": "Compile: CodeGenerator",
"command": "dotnet",
"args": [
"build"
],
@ -53,7 +69,6 @@
},
{
"taskName": "Run: CodeGenerator",
"command": "dotnet",
"args": [
"run"
],
@ -63,7 +78,6 @@
},
{
"taskName": "Run: Benchmarks",
"command": "dotnet",
"args": [
"run",
"-c",
@ -72,33 +86,6 @@
"options": {
"cwd": "${workspaceRoot}/test/Microsoft.AspNetCore.Server.Kestrel.Performance/"
}
},
{
"taskName": "Test: KestrelTests",
"isTestCommand": true,
"command": "dotnet",
"args": [
"test",
"-f",
"netcoreapp1.1"
],
"options": {
"cwd": "${workspaceRoot}/test/Microsoft.AspNetCore.Server.KestrelTests"
},
"problemMatcher": "$msCompile"
},
{
"taskName": "Test: FunctionalTests",
"command": "dotnet",
"args": [
"test",
"-f",
"netcoreapp1.1"
],
"options": {
"cwd": "${workspaceRoot}/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests"
},
"problemMatcher": "$msCompile"
}
]
}

View File

@ -80,6 +80,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
case RequestRejectionReason.ConnectMethodRequired:
ex = new BadHttpRequestException("Method not allowed.", StatusCodes.Status405MethodNotAllowed, HttpMethod.Connect);
break;
case RequestRejectionReason.MissingHostHeader:
ex = new BadHttpRequestException("Request is missing Host header.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.MultipleHostHeaders:
ex = new BadHttpRequestException("Multiple Host headers.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.InvalidHostHeader:
ex = new BadHttpRequestException("Invalid Host header.", StatusCodes.Status400BadRequest);
break;
default:
ex = new BadHttpRequestException("Bad request.", StatusCodes.Status400BadRequest);
break;
@ -116,6 +125,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
case RequestRejectionReason.LengthRequiredHttp10:
ex = new BadHttpRequestException($"{detail} request contains no Content-Length header", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.InvalidHostHeader:
ex = new BadHttpRequestException($"Invalid Host header: '{detail}'", StatusCodes.Status400BadRequest);
break;
default:
ex = new BadHttpRequestException("Bad request.", StatusCodes.Status400BadRequest);
break;

View File

@ -82,6 +82,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private readonly FrameContext _frameContext;
private readonly IHttpParser<FrameAdapter> _parser;
private HttpRequestTarget _requestTargetForm = HttpRequestTarget.Unknown;
private Uri _absoluteRequestTarget;
public Frame(FrameContext frameContext)
{
_frameContext = frameContext;
@ -141,6 +144,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public string Path { get; set; }
public string QueryString { get; set; }
public string RawTarget { get; set; }
public string HttpVersion
{
get
@ -365,6 +369,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Method = null;
PathBase = null;
Path = null;
RawTarget = null;
_requestTargetForm = HttpRequestTarget.Unknown;
_absoluteRequestTarget = null;
QueryString = null;
_httpVersion = Http.HttpVersion.Unknown;
StatusCode = StatusCodes.Status200OK;
@ -1266,6 +1273,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
Debug.Assert(target[0] == ByteForwardSlash, "Should only be called when path starts with /");
_requestTargetForm = HttpRequestTarget.OriginForm;
// URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
// Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8;
// then encoded/escaped to ASCII https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs"
@ -1325,8 +1334,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private void OnAuthorityFormTarget(HttpMethod method, Span<byte> target)
{
// TODO Validate that target is a correct host[:port] string.
// Reject as 400 if not. This is just a quick scan for invalid characters
_requestTargetForm = HttpRequestTarget.AuthorityForm;
// This is not complete validation. It is just a quick scan for invalid characters
// but doesn't check that the target fully matches the URI spec.
for (var i = 0; i < target.Length; i++)
{
@ -1360,6 +1370,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private void OnAsteriskFormTarget(HttpMethod method)
{
_requestTargetForm = HttpRequestTarget.AsteriskForm;
// The asterisk-form of request-target is only used for a server-wide
// OPTIONS request (https://tools.ietf.org/html/rfc7231#section-4.3.7).
if (method != HttpMethod.Options)
@ -1374,6 +1386,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private void OnAbsoluteFormTarget(Span<byte> target, Span<byte> query)
{
_requestTargetForm = HttpRequestTarget.AbsoluteForm;
// absolute-form
// https://tools.ietf.org/html/rfc7230#section-5.3.2
@ -1394,6 +1408,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
RejectRequestTarget(target);
}
_absoluteRequestTarget = uri;
Path = uri.LocalPath;
// don't use uri.Query because we need the unescaped version
QueryString = query.GetAsciiStringNonNullCharacters();
@ -1420,5 +1435,62 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
FrameRequestHeaders.Append(name, valueString);
}
protected void EnsureHostHeaderExists()
{
if (_httpVersion == Http.HttpVersion.Http10)
{
return;
}
// https://tools.ietf.org/html/rfc7230#section-5.4
// A server MUST respond with a 400 (Bad Request) status code to any
// HTTP/1.1 request message that lacks a Host header field and to any
// request message that contains more than one Host header field or a
// Host header field with an invalid field-value.
var host = FrameRequestHeaders.HeaderHost;
if (host.Count <= 0)
{
RejectRequest(RequestRejectionReason.MissingHostHeader);
}
else if (host.Count > 1)
{
RejectRequest(RequestRejectionReason.MultipleHostHeaders);
}
else if (_requestTargetForm == HttpRequestTarget.AuthorityForm)
{
if (!host.Equals(RawTarget))
{
RejectRequest(RequestRejectionReason.InvalidHostHeader, host.ToString());
}
}
else if (_requestTargetForm == HttpRequestTarget.AbsoluteForm)
{
// If the target URI includes an authority component, then a
// client MUST send a field - value for Host that is identical to that
// authority component, excluding any userinfo subcomponent and its "@"
// delimiter.
// System.Uri doesn't not tell us if the port was in the original string or not.
// When IsDefaultPort = true, we will allow Host: with or without the default port
var authorityAndPort = _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port;
if ((host != _absoluteRequestTarget.Authority || !_absoluteRequestTarget.IsDefaultPort)
&& host != authorityAndPort)
{
RejectRequest(RequestRejectionReason.InvalidHostHeader, host.ToString());
}
}
}
private enum HttpRequestTarget
{
Unknown = -1,
// origin-form is the most common
OriginForm,
AbsoluteForm,
AuthorityForm,
AsteriskForm
}
}
}

View File

@ -85,6 +85,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
if (!_requestProcessingStopping)
{
EnsureHostHeaderExists();
var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);
_keepAlive = messageBody.RequestKeepAlive;
_upgrade = messageBody.RequestUpgrade;

View File

@ -27,5 +27,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
LengthRequiredHttp10,
OptionsMethodRequired,
ConnectMethodRequired,
MissingHostHeader,
MultipleHostHeaders,
InvalidHostHeader,
}
}

View File

@ -533,7 +533,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
_frame.Start();
var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n");
await _input.Writer.WriteAsync(data);
var requestProcessingTask = _frame.StopAsync();

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public Task BadRequestIfMethodRequiresLengthButNoContentLengthOrTransferEncodingInRequest(string method)
{
return TestBadRequest(
$"{method} / HTTP/1.1\r\n\r\n",
$"{method} / HTTP/1.1\r\nHost:\r\n\r\n",
"411 Length Required",
$"{method} request contains no Content-Length or Transfer-Encoding header");
}
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public Task BadRequestIfContentLengthInvalid(string contentLength)
{
return TestBadRequest(
$"POST / HTTP/1.1\r\nContent-Length: {contentLength}\r\n\r\n",
$"POST / HTTP/1.1\r\nHost:\r\nContent-Length: {contentLength}\r\n\r\n",
"400 Bad Request",
$"Invalid content length: {contentLength}");
}
@ -106,6 +106,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
$"Allow: {allowedMethod}");
}
[Fact]
public Task BadRequestIfHostHeaderMissing()
{
return TestBadRequest(
"GET / HTTP/1.1\r\n\r\n",
"400 Bad Request",
"Request is missing Host header.");
}
[Fact]
public Task BadRequestIfMultipleHostHeaders()
{
return TestBadRequest("GET / HTTP/1.1\r\nHost: localhost\r\nHost: localhost\r\n\r\n",
"400 Bad Request",
"Multiple Host headers.");
}
[Theory]
[MemberData(nameof(InvalidHostHeaderData))]
public Task BadRequestIfHostHeaderDoesNotMatchRequestTarget(string requestTarget, string host)
{
return TestBadRequest(
$"{requestTarget} HTTP/1.1\r\nHost: {host}\r\n\r\n",
"400 Bad Request",
$"Invalid Host header: '{host.Trim()}'");
}
[Fact]
public async Task BadRequestLogsAreNotHigherThanInformation()
{
@ -211,5 +238,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.RequestHeaderInvalidData;
public static TheoryData<string,string> InvalidHostHeaderData => HttpParsingData.HostHeaderInvalidData;
}
}

View File

@ -66,6 +66,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.0",
"Host:",
"Transfer-Encoding: chunked",
"",
"5", "Hello",
@ -95,6 +96,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.0",
"Host:",
"Transfer-Encoding: chunked",
"Connection: keep-alive",
"",
@ -146,15 +148,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 5",
"",
"HelloPOST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"C", "HelloChunked",
"0",
"",
"POST / HTTP/1.1",
"Host:",
"Content-Length: 7",
"",
"Goodbye");
@ -225,6 +230,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
IEnumerable<string> sendSequence = new string[] {
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"C",
@ -236,6 +242,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"C",
@ -247,6 +254,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1",
"Host:",
"Content-Length: 7",
"",
"Goodbye"
@ -286,6 +294,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
$"{transferEncodingHeaderLine}",
$"{headerLine}",
"",
@ -327,6 +336,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
$"{transferEncodingHeaderLine}",
$"{headerLine}",
"",
@ -396,6 +406,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
IEnumerable<string> sendSequence = new string[] {
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"C;hello there",
@ -407,6 +418,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"C;hello there",
@ -418,6 +430,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1",
"Host:",
"Content-Length: 7",
"",
"Goodbye"
@ -459,6 +472,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"Cii");
@ -502,6 +516,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"C",
@ -535,6 +550,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: not-chunked",
"",
"C",
@ -557,6 +573,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: not-chunked",
"Content-Length: 22",
"",
@ -579,6 +596,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked, not-chunked",
"",
"C",
@ -601,6 +619,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.SendAll(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked, not-chunked",
"Content-Length: 22",
"",

View File

@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -104,6 +105,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"Connection: close",
"",
"");
@ -141,6 +143,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveEnd(
@ -178,6 +181,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveEnd(
@ -218,6 +222,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
@ -256,6 +261,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveEnd(
@ -289,6 +295,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// client closing the connection.
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveForcedEnd(
@ -321,6 +328,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// SendEnd is not called, so it isn't the client closing the connection.
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
@ -358,6 +366,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
@ -401,6 +410,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveEnd(

View File

@ -24,6 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.0",
"",
@ -46,4 +47,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
}
}
}
}

View File

@ -40,6 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var connection = server.CreateConnection())
{
await connection.SendAll("GET / HTTP/1.1",
"Host:",
"",
"")
.TimeoutAfter(TimeSpan.FromSeconds(10));

View File

@ -137,7 +137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12,
checkCertificateRevocation: false);
var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n");
await sslStream.WriteAsync(request, 0, request.Length);
await sslStream.ReadAsync(new byte[32], 0, 32);
}
@ -187,7 +187,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12,
checkCertificateRevocation: false);
var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n");
await sslStream.WriteAsync(request, 0, request.Length);
await sslStream.ReadAsync(new byte[32], 0, 32);
}

View File

@ -57,6 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await ReceiveResponse(connection);
@ -72,6 +73,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await Task.Delay(ShortDelay);
@ -93,6 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"");
@ -120,6 +123,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET /longrunning HTTP/1.1",
"Host:",
"",
"");
cts.CancelAfter(LongDelay);
@ -133,6 +137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await ReceiveResponse(connection);
@ -154,6 +159,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET /upgrade HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(

View File

@ -89,8 +89,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Initialize data with random bytes
(new Random()).NextBytes(data);
var startReadingRequestBody = new ManualResetEvent(false);
var clientFinishedSendingRequestBody = new ManualResetEvent(false);
var startReadingRequestBody = new TaskCompletionSource<object>();
var clientFinishedSendingRequestBody = new TaskCompletionSource<object>();
var lastBytesWritten = DateTime.MaxValue;
using (var host = StartWebHost(maxRequestBufferSize, data, connectionAdapter, startReadingRequestBody, clientFinishedSendingRequestBody))
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
Assert.Equal(data.Length, bytesWritten);
clientFinishedSendingRequestBody.Set();
clientFinishedSendingRequestBody.TrySetResult(null);
};
var sendTask = sendFunc();
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
Assert.InRange(bytesWritten, minimumExpectedBytesWritten, maximumExpectedBytesWritten);
// Tell server to start reading request body
startReadingRequestBody.Set();
startReadingRequestBody.TrySetResult(null);
// Wait for sendTask to finish sending the remaining bytes
await sendTask;
@ -160,7 +160,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await sendTask;
// Tell server to start reading request body
startReadingRequestBody.Set();
startReadingRequestBody.TrySetResult(null);
}
using (var reader = new StreamReader(stream, Encoding.ASCII))
@ -181,8 +181,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var bytesWrittenPollingInterval = TimeSpan.FromMilliseconds(bytesWrittenTimeout.TotalMilliseconds / 10);
var maxSendSize = 4096;
var startReadingRequestBody = new ManualResetEvent(false);
var clientFinishedSendingRequestBody = new ManualResetEvent(false);
var startReadingRequestBody = new TaskCompletionSource<object>();
var clientFinishedSendingRequestBody = new TaskCompletionSource<object>();
var lastBytesWritten = DateTime.MaxValue;
using (var host = StartWebHost(16 * 1024, data, false, startReadingRequestBody, clientFinishedSendingRequestBody))
@ -205,7 +205,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
lastBytesWritten = DateTime.Now;
}
clientFinishedSendingRequestBody.Set();
clientFinishedSendingRequestBody.TrySetResult(null);
};
var ignore = sendFunc();
@ -244,8 +244,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
}
private static IWebHost StartWebHost(long? maxRequestBufferSize, byte[] expectedBody, bool useConnectionAdapter, ManualResetEvent startReadingRequestBody,
ManualResetEvent clientFinishedSendingRequestBody)
private static IWebHost StartWebHost(long? maxRequestBufferSize,
byte[] expectedBody,
bool useConnectionAdapter,
TaskCompletionSource<object> startReadingRequestBody,
TaskCompletionSource<object> clientFinishedSendingRequestBody)
{
var host = new WebHostBuilder()
.UseKestrel(options =>
@ -275,7 +278,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
.UseContentRoot(Directory.GetCurrentDirectory())
.Configure(app => app.Run(async context =>
{
startReadingRequestBody.WaitOne();
await startReadingRequestBody.Task.TimeoutAfter(TimeSpan.FromSeconds(30));
var buffer = new byte[expectedBody.Length];
var bytesRead = 0;
@ -284,7 +287,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
bytesRead += await context.Request.Body.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead);
}
clientFinishedSendingRequestBody.WaitOne();
await clientFinishedSendingRequestBody.Task.TimeoutAfter(TimeSpan.FromSeconds(30));
// Verify client didn't send extra bytes
if (context.Request.Body.ReadByte() != -1)

View File

@ -12,18 +12,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public class MaxRequestLineSizeTests
{
[Theory]
[InlineData("GET / HTTP/1.1\r\n\r\n", 16)]
[InlineData("GET / HTTP/1.1\r\n\r\n", 17)]
[InlineData("GET / HTTP/1.1\r\n\r\n", 137)]
[InlineData("POST /abc/de HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 23)]
[InlineData("POST /abc/de HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 24)]
[InlineData("POST /abc/de HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 287)]
[InlineData("PUT /abc/de?f=ghi HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 28)]
[InlineData("PUT /abc/de?f=ghi HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 29)]
[InlineData("PUT /abc/de?f=ghi HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 589)]
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\n\r\n", 40)]
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\n\r\n", 41)]
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\n\r\n", 1027)]
[InlineData("GET / HTTP/1.1\r\nHost:\r\n\r\n", 16)]
[InlineData("GET / HTTP/1.1\r\nHost:\r\n\r\n", 17)]
[InlineData("GET / HTTP/1.1\r\nHost:\r\n\r\n", 137)]
[InlineData("POST /abc/de HTTP/1.1\r\nHost:\r\nContent-Length: 0\r\n\r\n", 23)]
[InlineData("POST /abc/de HTTP/1.1\r\nHost:\r\nContent-Length: 0\r\n\r\n", 24)]
[InlineData("POST /abc/de HTTP/1.1\r\nHost:\r\nContent-Length: 0\r\n\r\n", 287)]
[InlineData("PUT /abc/de?f=ghi HTTP/1.1\r\nHost:\r\nContent-Length: 0\r\n\r\n", 28)]
[InlineData("PUT /abc/de?f=ghi HTTP/1.1\r\nHost:\r\nContent-Length: 0\r\n\r\n", 29)]
[InlineData("PUT /abc/de?f=ghi HTTP/1.1\r\nHost:\r\nContent-Length: 0\r\n\r\n", 589)]
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\nHost:\r\n\r\n", 40)]
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\nHost:\r\n\r\n", 41)]
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\nHost:\r\n\r\n", 1027)]
public async Task ServerAcceptsRequestLineWithinLimit(string request, int limit)
{
using (var server = CreateServer(limit))

View File

@ -8,6 +8,7 @@
<PlatformTarget Condition="'$(TargetFramework)' == 'net46'">x64</PlatformTarget>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--
Workaround for "Explicit RID still required for .NET Framework test projects" (https://github.com/dotnet/sdk/issues/909).

View File

@ -125,9 +125,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
private static string MakeHeaders(int count)
{
return string.Join("", Enumerable
.Range(0, count)
.Select(i => $"Header-{i}: value{i}\r\n"));
const string host = "Host:\r\n";
if (count <= 1) return host;
return string.Join("", new[] { host }
.Concat(Enumerable
.Range(0, count -1)
.Select(i => $"Header-{i}: value{i}\r\n")));
}
private TestServer CreateServer(int? maxRequestHeaderCount = null, int? maxRequestHeadersTotalSize = null)

View File

@ -25,9 +25,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
var tasks = new[]
{
ConnectionAbortedWhenRequestHeadersNotReceivedInTime(server, ""),
ConnectionAbortedWhenRequestHeadersNotReceivedInTime(server, "Content-Length: 1\r\n"),
ConnectionAbortedWhenRequestHeadersNotReceivedInTime(server, "Content-Length: 1\r\n\r"),
ConnectionAbortedWhenRequestHeadersNotReceivedInTime(server, "Host:\r\n"),
ConnectionAbortedWhenRequestHeadersNotReceivedInTime(server, "Host:\r\nContent-Length: 1\r\n"),
ConnectionAbortedWhenRequestHeadersNotReceivedInTime(server, "Host:\r\nContent-Length: 1\r\n\r"),
RequestHeadersTimeoutCanceledAfterHeadersReceived(server),
ConnectionAbortedWhenRequestLineNotReceivedInTime(server, "P"),
ConnectionAbortedWhenRequestLineNotReceivedInTime(server, "POST / HTTP/1.1\r"),
@ -55,6 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 1",
"",
"");
@ -80,7 +81,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await Assert.ThrowsAsync<IOException>(async () =>
{
foreach (var ch in "POST / HTTP/1.1\r\n\r\n")
foreach (var ch in "POST / HTTP/1.1\r\nHost:\r\n\r\n")
{
await connection.Send(ch.ToString());
await Task.Delay(ShortDelay);

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET /%41%CC%8A/A/../B/%41%CC%8A HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -77,6 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
$"GET {requestTarget} HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -109,8 +111,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
using (var connection = server.CreateConnection())
{
var host = method == HttpMethod.Connect
? requestTarget
: string.Empty;
await connection.Send(
$"{HttpUtilities.MethodToString(method)} {requestTarget} HTTP/1.1",
$"Host: {host}",
"",
"");
await connection.ReceiveEnd(

View File

@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nConnection: keep-alive, upgrade\r\n\r\n"));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\nConnection: keep-alive, upgrade\r\n\r\n"));
socket.Send(Encoding.ASCII.GetBytes("abc"));
while (socket.Receive(new byte[1024]) > 0) ;
@ -357,7 +357,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n"));
// Wait until request is done being processed
await requestDone.WaitAsync(TimeSpan.FromSeconds(10));
@ -417,7 +417,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n"));
// Wait until connection is established
await requestStarted.WaitAsync(TimeSpan.FromSeconds(10));
@ -470,7 +470,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
socket.LingerState = new LingerOption(true, 0);
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nContent-Length: 1\r\n\r\n"));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\nContent-Length: 1\r\n\r\n"));
Assert.True(await requestStarted.WaitAsync(_semaphoreWaitTimeout));
}
@ -505,7 +505,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n"));
await appStarted.WaitAsync();
socket.Shutdown(SocketShutdown.Send);
await requestAborted.WaitAsync().TimeoutAfter(TimeSpan.FromSeconds(10));
@ -534,13 +534,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
var pathTcs = new TaskCompletionSource<PathString>();
var rawTargetTcs = new TaskCompletionSource<string>();
var hostTcs = new TaskCompletionSource<HostString>();
var queryTcs = new TaskCompletionSource<IQueryCollection>();
using (var server = new TestServer(async context =>
{
pathTcs.TrySetResult(context.Request.Path);
hostTcs.TrySetResult(context.Request.Host);
queryTcs.TrySetResult(context.Request.Query);
rawTargetTcs.TrySetResult(context.Features.Get<IHttpRequestFeature>().RawTarget);
await context.Response.WriteAsync("Done");
@ -548,10 +546,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
using (var connection = server.CreateConnection())
{
var requestTarget = new Uri(requestUrl, UriKind.Absolute);
var host = requestTarget.Authority;
if (!requestTarget.IsDefaultPort)
{
host += ":" + requestTarget.Port;
}
await connection.Send(
$"GET {requestUrl} HTTP/1.1",
"Content-Length: 0",
"Host: localhost",
$"Host: {host}",
"",
"");
@ -563,10 +568,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"Done")
.TimeoutAfter(TimeSpan.FromSeconds(10));
await Task.WhenAll(pathTcs.Task, rawTargetTcs.Task, hostTcs.Task, queryTcs.Task).TimeoutAfter(TimeSpan.FromSeconds(30));
await Task.WhenAll(pathTcs.Task, rawTargetTcs.Task, queryTcs.Task).TimeoutAfter(TimeSpan.FromSeconds(30));
Assert.Equal(new PathString(expectedPath), pathTcs.Task.Result);
Assert.Equal(requestUrl, rawTargetTcs.Task.Result);
Assert.Equal("localhost", hostTcs.Task.Result.ToString());
if (queryValue == null)
{
Assert.False(queryTcs.Task.Result.ContainsKey("q"));
@ -626,6 +630,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
for (var i = 0; i < iterations; i++)
{
await connection.Send("GET / HTTP/1.1",
"Host:",
"",
"");
@ -657,8 +662,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.1",
"Host:",
"Connection: close",
"Content-Length: 7",
"",
@ -862,6 +869,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Expect: 100-continue",
"Connection: close",
"Content-Length: 11",
@ -901,6 +909,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// https://github.com/aspnet/KestrelHttpServer/issues/1104 is not regressing.
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"Connection: close",
"",
"");
@ -917,6 +926,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.0",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -950,6 +960,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 7");
connection.Shutdown(SocketShutdown.Send);
await connection.ReceiveForcedEnd();
@ -1008,9 +1019,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Never send the body so CopyToAsync always fails.
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 5",
"",
"HelloPOST / HTTP/1.1",
"Host:",
"Content-Length: 5",
"",
"");
@ -1062,8 +1075,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -1107,6 +1122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"Connection: Upgrade",
"",
message);
@ -1163,8 +1179,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var connection = server.CreateConnection())
{
var requestData =
Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount)
.Concat(new[] { "GET / HTTP/1.1\r\nContent-Length: 7\r\nConnection: close\r\n\r\nGoodbye" });
Enumerable.Repeat("GET / HTTP/1.1\r\nHost:\r\n", loopCount)
.Concat(new[] { "GET / HTTP/1.1\r\nHost:\r\nContent-Length: 7\r\nConnection: close\r\n\r\nGoodbye" });
var response = string.Join("\r\n", new string[] {
"HTTP/1.1 200 OK",
@ -1197,6 +1213,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
}
[Theory]
[MemberData(nameof(HostHeaderData))]
public async Task MatchesValidRequestTargetAndHostHeader(string request, string hostHeader)
{
using (var server = new TestServer(context => Task.CompletedTask))
{
using (var connection = server.CreateConnection())
{
await connection.Send($"{request} HTTP/1.1",
$"Host: {hostHeader}",
"",
"");
await connection.Receive("HTTP/1.1 200 OK");
}
}
}
private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress)
{
var builder = new WebHostBuilder()
@ -1233,5 +1267,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
Assert.NotEmpty(facts["RemotePort"].Value<string>());
}
}
public static TheoryData<string, string> HostHeaderData => HttpParsingData.HostHeaderData;
}
}

View File

@ -326,6 +326,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"gg");
@ -360,6 +361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"gg");
@ -389,6 +391,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -423,6 +426,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -446,6 +450,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"HEAD / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -478,6 +483,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"HEAD / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -515,6 +521,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -524,7 +531,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"",
"hello,");
await connection.WaitForConnectionClose();
await connection.WaitForConnectionClose().TimeoutAfter(TimeSpan.FromSeconds(30));
}
}
@ -554,6 +561,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -589,6 +597,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -624,6 +633,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -664,6 +674,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
@ -718,6 +729,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -757,8 +769,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -801,6 +815,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -836,6 +851,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -872,6 +888,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -900,6 +917,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"HEAD / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -928,6 +946,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"HEAD / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -959,6 +978,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"HEAD / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -990,6 +1010,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -1033,17 +1054,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
requestStarted.Wait();
connection.Shutdown(SocketShutdown.Send);
await connection.WaitForConnectionClose();
await connection.WaitForConnectionClose().TimeoutAfter(TimeSpan.FromSeconds(30));
}
connectionClosed.Set();
await tcs.Task;
await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(30));
}
}
@ -1073,10 +1095,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"gg");
await responseWritten.WaitAsync();
await responseWritten.WaitAsync().TimeoutAfter(TimeSpan.FromSeconds(30));
await connection.ReceiveEnd(
"HTTP/1.1 400 Bad Request",
$"Date: {server.Context.DateHeaderValue}",
@ -1104,6 +1127,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -1151,6 +1175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -1197,6 +1222,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -1209,6 +1235,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Make sure connection was kept open
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -1245,6 +1272,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -1286,6 +1314,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -1327,6 +1356,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -1367,6 +1397,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
@ -1397,6 +1428,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 1",
"",
"");
@ -1430,6 +1462,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"",
"gg");
@ -1460,6 +1493,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked",
"Expect: 100-continue",
"",
@ -1508,6 +1542,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 2",
"Expect: 100-continue",
"",
@ -1578,6 +1613,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.0",
"Connection: keep-alive",
@ -1613,6 +1649,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"Connection: close",
"",
"");
@ -1654,6 +1691,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"HEAD / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -1687,15 +1725,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 3",
"",
"204POST / HTTP/1.1",
"Host:",
"Content-Length: 3",
"",
"205POST / HTTP/1.1",
"Host:",
"Content-Length: 3",
"",
"304POST / HTTP/1.1",
"Host:",
"Content-Length: 3",
"",
"200");
@ -1736,6 +1778,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -1792,8 +1835,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.1",
"Host:",
"Connection: close",
"",
"");
@ -1850,8 +1895,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -1909,6 +1956,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -1955,6 +2003,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -1999,6 +2048,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveForcedEnd(
@ -2061,6 +2111,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"POST / HTTP/1.1",
"Host:",
"Content-Length: 5",
"",
"Hello");
@ -2166,8 +2217,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -2217,6 +2270,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(
@ -2268,6 +2322,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.ReceiveEnd(

View File

@ -57,9 +57,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
catch
{
_transport.UnbindAsync().Wait();
_transport.StopAsync().Wait();
_transport = null;
if (_transport != null)
{
_transport.UnbindAsync().Wait();
_transport.StopAsync().Wait();
_transport = null;
}
throw;
}
}

View File

@ -7,6 +7,7 @@
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
<PlatformTarget Condition="'$(TargetFramework)' == 'net46'">x64</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--
Workaround for "Explicit RID still required for .NET Framework test projects" (https://github.com/dotnet/sdk/issues/909).

View File

@ -427,5 +427,58 @@ namespace Microsoft.AspNetCore.Testing
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r ", @"Invalid request headers: missing final CRLF in header fields." },
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r \n", @"Invalid request headers: missing final CRLF in header fields." },
};
public static TheoryData<string, string> HostHeaderData
=> new TheoryData<string, string>
{
{ "OPTIONS *", "" },
{ "GET /pub/WWW/", "" },
{ "GET /pub/WWW/", " " },
{ "GET /pub/WWW/", "www.example.org" },
{ "GET http://localhost/", "localhost" },
{ "GET http://localhost:80/", "localhost:80" },
{ "GET https://localhost/", "localhost" },
{ "GET https://localhost:443/", "localhost:443" },
{ "CONNECT asp.net:80", "asp.net:80" },
{ "CONNECT asp.net:443", "asp.net:443" },
};
public static TheoryData<string, string> HostHeaderInvalidData
{
get
{
// see https://tools.ietf.org/html/rfc7230#section-5.4
var invalidHostValues = new[] {
"",
" ",
"contoso.com:4000",
"contoso.com/",
"not-contoso.com",
"user@password:contoso.com",
"user@contoso.com",
"http://contoso.com/",
"http://contoso.com"
};
var data = new TheoryData<string, string>();
foreach (var host in invalidHostValues)
{
// absolute form
// expected: GET http://contoso.com/ => Host: contoso.com
data.Add("GET http://contoso.com/", host);
// authority-form
// expected: CONNECT contoso.com => Host: contoso.com
data.Add("CONNECT contoso.com", host);
}
// port mismatch when target contains port
data.Add("GET https://contoso.com:443/", "contoso.com:5000");
data.Add("CONNECT contoso.com:443", "contoso.com:5000");
return data;
}
}
}
}