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

51
.vscode/tasks.json vendored
View File

@ -5,10 +5,20 @@
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE": "true" "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": [ "tasks": [
{ {
"taskName": "Restore: solution", "taskName": "Restore: solution",
"command": "dotnet",
"args": [ "args": [
"restore" "restore"
] ]
@ -16,7 +26,6 @@
{ {
"taskName": "Compile: solution", "taskName": "Compile: solution",
"isBuildCommand": true, "isBuildCommand": true,
"command": "dotnet",
"args": [ "args": [
"build", "build",
"${workspaceRoot}/KestrelHttpServer.sln" "${workspaceRoot}/KestrelHttpServer.sln"
@ -40,9 +49,16 @@
} }
} }
}, },
{
"taskName": "Test",
"args": [
"test"
],
"problemMatcher": "$msCompile",
"isTestCommand": true
},
{ {
"taskName": "Compile: CodeGenerator", "taskName": "Compile: CodeGenerator",
"command": "dotnet",
"args": [ "args": [
"build" "build"
], ],
@ -53,7 +69,6 @@
}, },
{ {
"taskName": "Run: CodeGenerator", "taskName": "Run: CodeGenerator",
"command": "dotnet",
"args": [ "args": [
"run" "run"
], ],
@ -63,7 +78,6 @@
}, },
{ {
"taskName": "Run: Benchmarks", "taskName": "Run: Benchmarks",
"command": "dotnet",
"args": [ "args": [
"run", "run",
"-c", "-c",
@ -72,33 +86,6 @@
"options": { "options": {
"cwd": "${workspaceRoot}/test/Microsoft.AspNetCore.Server.Kestrel.Performance/" "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: case RequestRejectionReason.ConnectMethodRequired:
ex = new BadHttpRequestException("Method not allowed.", StatusCodes.Status405MethodNotAllowed, HttpMethod.Connect); ex = new BadHttpRequestException("Method not allowed.", StatusCodes.Status405MethodNotAllowed, HttpMethod.Connect);
break; 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: default:
ex = new BadHttpRequestException("Bad request.", StatusCodes.Status400BadRequest); ex = new BadHttpRequestException("Bad request.", StatusCodes.Status400BadRequest);
break; break;
@ -116,6 +125,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
case RequestRejectionReason.LengthRequiredHttp10: case RequestRejectionReason.LengthRequiredHttp10:
ex = new BadHttpRequestException($"{detail} request contains no Content-Length header", StatusCodes.Status400BadRequest); ex = new BadHttpRequestException($"{detail} request contains no Content-Length header", StatusCodes.Status400BadRequest);
break; break;
case RequestRejectionReason.InvalidHostHeader:
ex = new BadHttpRequestException($"Invalid Host header: '{detail}'", StatusCodes.Status400BadRequest);
break;
default: default:
ex = new BadHttpRequestException("Bad request.", StatusCodes.Status400BadRequest); ex = new BadHttpRequestException("Bad request.", StatusCodes.Status400BadRequest);
break; break;

View File

@ -82,6 +82,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private readonly FrameContext _frameContext; private readonly FrameContext _frameContext;
private readonly IHttpParser<FrameAdapter> _parser; private readonly IHttpParser<FrameAdapter> _parser;
private HttpRequestTarget _requestTargetForm = HttpRequestTarget.Unknown;
private Uri _absoluteRequestTarget;
public Frame(FrameContext frameContext) public Frame(FrameContext frameContext)
{ {
_frameContext = frameContext; _frameContext = frameContext;
@ -141,6 +144,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public string Path { get; set; } public string Path { get; set; }
public string QueryString { get; set; } public string QueryString { get; set; }
public string RawTarget { get; set; } public string RawTarget { get; set; }
public string HttpVersion public string HttpVersion
{ {
get get
@ -365,6 +369,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Method = null; Method = null;
PathBase = null; PathBase = null;
Path = null; Path = null;
RawTarget = null;
_requestTargetForm = HttpRequestTarget.Unknown;
_absoluteRequestTarget = null;
QueryString = null; QueryString = null;
_httpVersion = Http.HttpVersion.Unknown; _httpVersion = Http.HttpVersion.Unknown;
StatusCode = StatusCodes.Status200OK; 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 /"); 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 // 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; // 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" // 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) private void OnAuthorityFormTarget(HttpMethod method, Span<byte> target)
{ {
// TODO Validate that target is a correct host[:port] string. _requestTargetForm = HttpRequestTarget.AuthorityForm;
// Reject as 400 if not. This is just a quick scan for invalid characters
// 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. // but doesn't check that the target fully matches the URI spec.
for (var i = 0; i < target.Length; i++) 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) private void OnAsteriskFormTarget(HttpMethod method)
{ {
_requestTargetForm = HttpRequestTarget.AsteriskForm;
// The asterisk-form of request-target is only used for a server-wide // 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). // OPTIONS request (https://tools.ietf.org/html/rfc7231#section-4.3.7).
if (method != HttpMethod.Options) 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) private void OnAbsoluteFormTarget(Span<byte> target, Span<byte> query)
{ {
_requestTargetForm = HttpRequestTarget.AbsoluteForm;
// absolute-form // absolute-form
// https://tools.ietf.org/html/rfc7230#section-5.3.2 // https://tools.ietf.org/html/rfc7230#section-5.3.2
@ -1394,6 +1408,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
RejectRequestTarget(target); RejectRequestTarget(target);
} }
_absoluteRequestTarget = uri;
Path = uri.LocalPath; Path = uri.LocalPath;
// don't use uri.Query because we need the unescaped version // don't use uri.Query because we need the unescaped version
QueryString = query.GetAsciiStringNonNullCharacters(); QueryString = query.GetAsciiStringNonNullCharacters();
@ -1420,5 +1435,62 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
FrameRequestHeaders.Append(name, valueString); 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) if (!_requestProcessingStopping)
{ {
EnsureHostHeaderExists();
var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this); var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);
_keepAlive = messageBody.RequestKeepAlive; _keepAlive = messageBody.RequestKeepAlive;
_upgrade = messageBody.RequestUpgrade; _upgrade = messageBody.RequestUpgrade;

View File

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

View File

@ -533,7 +533,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{ {
_frame.Start(); _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); await _input.Writer.WriteAsync(data);
var requestProcessingTask = _frame.StopAsync(); var requestProcessingTask = _frame.StopAsync();

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public Task BadRequestIfMethodRequiresLengthButNoContentLengthOrTransferEncodingInRequest(string method) public Task BadRequestIfMethodRequiresLengthButNoContentLengthOrTransferEncodingInRequest(string method)
{ {
return TestBadRequest( return TestBadRequest(
$"{method} / HTTP/1.1\r\n\r\n", $"{method} / HTTP/1.1\r\nHost:\r\n\r\n",
"411 Length Required", "411 Length Required",
$"{method} request contains no Content-Length or Transfer-Encoding header"); $"{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) public Task BadRequestIfContentLengthInvalid(string contentLength)
{ {
return TestBadRequest( 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", "400 Bad Request",
$"Invalid content length: {contentLength}"); $"Invalid content length: {contentLength}");
} }
@ -106,6 +106,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
$"Allow: {allowedMethod}"); $"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] [Fact]
public async Task BadRequestLogsAreNotHigherThanInformation() public async Task BadRequestLogsAreNotHigherThanInformation()
{ {
@ -211,5 +238,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData; public static TheoryData<string> UnrecognizedHttpVersionData => HttpParsingData.UnrecognizedHttpVersionData;
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.RequestHeaderInvalidData; 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( await connection.Send(
"POST / HTTP/1.0", "POST / HTTP/1.0",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"5", "Hello", "5", "Hello",
@ -95,6 +96,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.0", "POST / HTTP/1.0",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"Connection: keep-alive", "Connection: keep-alive",
"", "",
@ -146,15 +148,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 5", "Content-Length: 5",
"", "",
"HelloPOST / HTTP/1.1", "HelloPOST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"C", "HelloChunked", "C", "HelloChunked",
"0", "0",
"", "",
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye"); "Goodbye");
@ -225,6 +230,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
IEnumerable<string> sendSequence = new string[] { IEnumerable<string> sendSequence = new string[] {
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"C", "C",
@ -236,6 +242,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
sendSequence = sendSequence.Concat(new string[] { sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"C", "C",
@ -247,6 +254,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
sendSequence = sendSequence.Concat(new string[] { sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye" "Goodbye"
@ -286,6 +294,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
$"{transferEncodingHeaderLine}", $"{transferEncodingHeaderLine}",
$"{headerLine}", $"{headerLine}",
"", "",
@ -327,6 +336,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
$"{transferEncodingHeaderLine}", $"{transferEncodingHeaderLine}",
$"{headerLine}", $"{headerLine}",
"", "",
@ -396,6 +406,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
IEnumerable<string> sendSequence = new string[] { IEnumerable<string> sendSequence = new string[] {
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"C;hello there", "C;hello there",
@ -407,6 +418,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
sendSequence = sendSequence.Concat(new string[] { sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"C;hello there", "C;hello there",
@ -418,6 +430,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
sendSequence = sendSequence.Concat(new string[] { sendSequence = sendSequence.Concat(new string[] {
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye" "Goodbye"
@ -459,6 +472,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"Cii"); "Cii");
@ -502,6 +516,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"C", "C",
@ -535,6 +550,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: not-chunked", "Transfer-Encoding: not-chunked",
"", "",
"C", "C",
@ -557,6 +573,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: not-chunked", "Transfer-Encoding: not-chunked",
"Content-Length: 22", "Content-Length: 22",
"", "",
@ -579,6 +596,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked, not-chunked", "Transfer-Encoding: chunked, not-chunked",
"", "",
"C", "C",
@ -601,6 +619,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.SendAll( await connection.SendAll(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked, not-chunked", "Transfer-Encoding: chunked, not-chunked",
"Content-Length: 22", "Content-Length: 22",
"", "",

View File

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

View File

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

View File

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

View File

@ -137,7 +137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12,
checkCertificateRevocation: false); 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.WriteAsync(request, 0, request.Length);
await sslStream.ReadAsync(new byte[32], 0, 32); await sslStream.ReadAsync(new byte[32], 0, 32);
} }
@ -187,7 +187,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12,
checkCertificateRevocation: false); 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.WriteAsync(request, 0, request.Length);
await sslStream.ReadAsync(new byte[32], 0, 32); await sslStream.ReadAsync(new byte[32], 0, 32);
} }

View File

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

View File

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

View File

@ -12,18 +12,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public class MaxRequestLineSizeTests public class MaxRequestLineSizeTests
{ {
[Theory] [Theory]
[InlineData("GET / HTTP/1.1\r\n\r\n", 16)] [InlineData("GET / HTTP/1.1\r\nHost:\r\n\r\n", 16)]
[InlineData("GET / HTTP/1.1\r\n\r\n", 17)] [InlineData("GET / HTTP/1.1\r\nHost:\r\n\r\n", 17)]
[InlineData("GET / HTTP/1.1\r\n\r\n", 137)] [InlineData("GET / HTTP/1.1\r\nHost:\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\nHost:\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\nHost:\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("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\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", 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\nHost:\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("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\n\r\n", 40)] [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\n\r\n", 41)] [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\n\r\n", 1027)] [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) public async Task ServerAcceptsRequestLineWithinLimit(string request, int limit)
{ {
using (var server = CreateServer(limit)) using (var server = CreateServer(limit))

View File

@ -8,6 +8,7 @@
<PlatformTarget Condition="'$(TargetFramework)' == 'net46'">x64</PlatformTarget> <PlatformTarget Condition="'$(TargetFramework)' == 'net46'">x64</PlatformTarget>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!-- <!--
Workaround for "Explicit RID still required for .NET Framework test projects" (https://github.com/dotnet/sdk/issues/909). 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) private static string MakeHeaders(int count)
{ {
return string.Join("", Enumerable const string host = "Host:\r\n";
.Range(0, count) if (count <= 1) return host;
.Select(i => $"Header-{i}: value{i}\r\n"));
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) private TestServer CreateServer(int? maxRequestHeaderCount = null, int? maxRequestHeadersTotalSize = null)

View File

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

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET /%41%CC%8A/A/../B/%41%CC%8A HTTP/1.1", "GET /%41%CC%8A/A/../B/%41%CC%8A HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -77,6 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
$"GET {requestTarget} HTTP/1.1", $"GET {requestTarget} HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -109,8 +111,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
using (var connection = server.CreateConnection()) using (var connection = server.CreateConnection())
{ {
var host = method == HttpMethod.Connect
? requestTarget
: string.Empty;
await connection.Send( await connection.Send(
$"{HttpUtilities.MethodToString(method)} {requestTarget} HTTP/1.1", $"{HttpUtilities.MethodToString(method)} {requestTarget} HTTP/1.1",
$"Host: {host}",
"", "",
""); "");
await connection.ReceiveEnd( 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)) using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{ {
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); 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")); socket.Send(Encoding.ASCII.GetBytes("abc"));
while (socket.Receive(new byte[1024]) > 0) ; 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)) using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{ {
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); 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 // Wait until request is done being processed
await requestDone.WaitAsync(TimeSpan.FromSeconds(10)); 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)) using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{ {
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); 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 // Wait until connection is established
await requestStarted.WaitAsync(TimeSpan.FromSeconds(10)); 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.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
socket.LingerState = new LingerOption(true, 0); 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)); 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)) using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{ {
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); 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(); await appStarted.WaitAsync();
socket.Shutdown(SocketShutdown.Send); socket.Shutdown(SocketShutdown.Send);
await requestAborted.WaitAsync().TimeoutAfter(TimeSpan.FromSeconds(10)); await requestAborted.WaitAsync().TimeoutAfter(TimeSpan.FromSeconds(10));
@ -534,13 +534,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
var pathTcs = new TaskCompletionSource<PathString>(); var pathTcs = new TaskCompletionSource<PathString>();
var rawTargetTcs = new TaskCompletionSource<string>(); var rawTargetTcs = new TaskCompletionSource<string>();
var hostTcs = new TaskCompletionSource<HostString>();
var queryTcs = new TaskCompletionSource<IQueryCollection>(); var queryTcs = new TaskCompletionSource<IQueryCollection>();
using (var server = new TestServer(async context => using (var server = new TestServer(async context =>
{ {
pathTcs.TrySetResult(context.Request.Path); pathTcs.TrySetResult(context.Request.Path);
hostTcs.TrySetResult(context.Request.Host);
queryTcs.TrySetResult(context.Request.Query); queryTcs.TrySetResult(context.Request.Query);
rawTargetTcs.TrySetResult(context.Features.Get<IHttpRequestFeature>().RawTarget); rawTargetTcs.TrySetResult(context.Features.Get<IHttpRequestFeature>().RawTarget);
await context.Response.WriteAsync("Done"); await context.Response.WriteAsync("Done");
@ -548,10 +546,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
using (var connection = server.CreateConnection()) 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( await connection.Send(
$"GET {requestUrl} HTTP/1.1", $"GET {requestUrl} HTTP/1.1",
"Content-Length: 0", "Content-Length: 0",
"Host: localhost", $"Host: {host}",
"", "",
""); "");
@ -563,10 +568,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"Done") "Done")
.TimeoutAfter(TimeSpan.FromSeconds(10)); .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(new PathString(expectedPath), pathTcs.Task.Result);
Assert.Equal(requestUrl, rawTargetTcs.Task.Result); Assert.Equal(requestUrl, rawTargetTcs.Task.Result);
Assert.Equal("localhost", hostTcs.Task.Result.ToString());
if (queryValue == null) if (queryValue == null)
{ {
Assert.False(queryTcs.Task.Result.ContainsKey("q")); Assert.False(queryTcs.Task.Result.ContainsKey("q"));
@ -626,6 +630,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
for (var i = 0; i < iterations; i++) for (var i = 0; i < iterations; i++)
{ {
await connection.Send("GET / HTTP/1.1", await connection.Send("GET / HTTP/1.1",
"Host:",
"", "",
""); "");
@ -657,8 +662,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"Connection: close", "Connection: close",
"Content-Length: 7", "Content-Length: 7",
"", "",
@ -862,6 +869,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Expect: 100-continue", "Expect: 100-continue",
"Connection: close", "Connection: close",
"Content-Length: 11", "Content-Length: 11",
@ -901,6 +909,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// https://github.com/aspnet/KestrelHttpServer/issues/1104 is not regressing. // https://github.com/aspnet/KestrelHttpServer/issues/1104 is not regressing.
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"Connection: close", "Connection: close",
"", "",
""); "");
@ -917,6 +926,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.0", "GET / HTTP/1.0",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -950,6 +960,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 7"); "Content-Length: 7");
connection.Shutdown(SocketShutdown.Send); connection.Shutdown(SocketShutdown.Send);
await connection.ReceiveForcedEnd(); await connection.ReceiveForcedEnd();
@ -1008,9 +1019,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Never send the body so CopyToAsync always fails. // Never send the body so CopyToAsync always fails.
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 5", "Content-Length: 5",
"", "",
"HelloPOST / HTTP/1.1", "HelloPOST / HTTP/1.1",
"Host:",
"Content-Length: 5", "Content-Length: 5",
"", "",
""); "");
@ -1062,8 +1075,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -1107,6 +1122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"Connection: Upgrade", "Connection: Upgrade",
"", "",
message); message);
@ -1163,8 +1179,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var connection = server.CreateConnection()) using (var connection = server.CreateConnection())
{ {
var requestData = var requestData =
Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount) Enumerable.Repeat("GET / HTTP/1.1\r\nHost:\r\n", loopCount)
.Concat(new[] { "GET / HTTP/1.1\r\nContent-Length: 7\r\nConnection: close\r\n\r\nGoodbye" }); .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[] { var response = string.Join("\r\n", new string[] {
"HTTP/1.1 200 OK", "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) private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress)
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
@ -1233,5 +1267,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
Assert.NotEmpty(facts["RemotePort"].Value<string>()); 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( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"gg"); "gg");
@ -360,6 +361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"gg"); "gg");
@ -389,6 +391,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -423,6 +426,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -446,6 +450,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -478,6 +483,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -515,6 +521,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -524,7 +531,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"", "",
"hello,"); "hello,");
await connection.WaitForConnectionClose(); await connection.WaitForConnectionClose().TimeoutAfter(TimeSpan.FromSeconds(30));
} }
} }
@ -554,6 +561,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -589,6 +597,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -624,6 +633,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -664,6 +674,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
@ -718,6 +729,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -757,8 +769,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -801,6 +815,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -836,6 +851,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -872,6 +888,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -900,6 +917,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -928,6 +946,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -959,6 +978,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -990,6 +1010,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -1033,17 +1054,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
requestStarted.Wait(); requestStarted.Wait();
connection.Shutdown(SocketShutdown.Send); connection.Shutdown(SocketShutdown.Send);
await connection.WaitForConnectionClose(); await connection.WaitForConnectionClose().TimeoutAfter(TimeSpan.FromSeconds(30));
} }
connectionClosed.Set(); 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( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"gg"); "gg");
await responseWritten.WaitAsync(); await responseWritten.WaitAsync().TimeoutAfter(TimeSpan.FromSeconds(30));
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 400 Bad Request", "HTTP/1.1 400 Bad Request",
$"Date: {server.Context.DateHeaderValue}", $"Date: {server.Context.DateHeaderValue}",
@ -1104,6 +1127,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -1151,6 +1175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -1197,6 +1222,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -1209,6 +1235,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Make sure connection was kept open // Make sure connection was kept open
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -1245,6 +1272,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -1286,6 +1314,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -1327,6 +1356,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -1367,6 +1397,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.Receive( await connection.Receive(
@ -1397,6 +1428,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 1", "Content-Length: 1",
"", "",
""); "");
@ -1430,6 +1462,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"gg"); "gg");
@ -1460,6 +1493,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"Expect: 100-continue", "Expect: 100-continue",
"", "",
@ -1508,6 +1542,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 2", "Content-Length: 2",
"Expect: 100-continue", "Expect: 100-continue",
"", "",
@ -1578,6 +1613,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.0", "GET / HTTP/1.0",
"Connection: keep-alive", "Connection: keep-alive",
@ -1613,6 +1649,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"Connection: close", "Connection: close",
"", "",
""); "");
@ -1654,6 +1691,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -1687,15 +1725,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 3", "Content-Length: 3",
"", "",
"204POST / HTTP/1.1", "204POST / HTTP/1.1",
"Host:",
"Content-Length: 3", "Content-Length: 3",
"", "",
"205POST / HTTP/1.1", "205POST / HTTP/1.1",
"Host:",
"Content-Length: 3", "Content-Length: 3",
"", "",
"304POST / HTTP/1.1", "304POST / HTTP/1.1",
"Host:",
"Content-Length: 3", "Content-Length: 3",
"", "",
"200"); "200");
@ -1736,6 +1778,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -1792,8 +1835,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"Connection: close", "Connection: close",
"", "",
""); "");
@ -1850,8 +1895,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -1909,6 +1956,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -1955,6 +2003,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -1999,6 +2048,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
@ -2061,6 +2111,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Host:",
"Content-Length: 5", "Content-Length: 5",
"", "",
"Hello"); "Hello");
@ -2166,8 +2217,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -2217,6 +2270,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
@ -2268,6 +2322,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"Host:",
"", "",
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(

View File

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

View File

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