More specific response status codes for errors (#653).
- 414 when request line exceeds size limit - 431 when request headers exceed size or count limits - 505 when request line contains unsupported HTTP version
This commit is contained in:
parent
63509b9e10
commit
49ff98f8cb
|
|
@ -8,100 +8,102 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
{
|
||||
public sealed class BadHttpRequestException : IOException
|
||||
{
|
||||
private BadHttpRequestException(string message)
|
||||
private BadHttpRequestException(string message, int statusCode)
|
||||
: base(message)
|
||||
{
|
||||
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
|
||||
internal int StatusCode { get; }
|
||||
|
||||
internal static BadHttpRequestException GetException(RequestRejectionReason reason)
|
||||
{
|
||||
BadHttpRequestException ex;
|
||||
switch (reason)
|
||||
{
|
||||
case RequestRejectionReason.MissingMethod:
|
||||
ex = new BadHttpRequestException("Missing method.");
|
||||
ex = new BadHttpRequestException("Missing method.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.InvalidMethod:
|
||||
ex = new BadHttpRequestException("Invalid method.");
|
||||
ex = new BadHttpRequestException("Invalid method.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.MissingRequestTarget:
|
||||
ex = new BadHttpRequestException("Missing request target.");
|
||||
ex = new BadHttpRequestException("Missing request target.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.MissingHTTPVersion:
|
||||
ex = new BadHttpRequestException("Missing HTTP version.");
|
||||
ex = new BadHttpRequestException("Missing HTTP version.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.UnrecognizedHTTPVersion:
|
||||
ex = new BadHttpRequestException("Unrecognized HTTP version.");
|
||||
ex = new BadHttpRequestException("Unrecognized HTTP version.", 505);
|
||||
break;
|
||||
case RequestRejectionReason.MissingLFInRequestLine:
|
||||
ex = new BadHttpRequestException("Missing LF in request line.");
|
||||
ex = new BadHttpRequestException("Missing LF in request line.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.HeadersCorruptedInvalidHeaderSequence:
|
||||
ex = new BadHttpRequestException("Headers corrupted, invalid header sequence.");
|
||||
ex = new BadHttpRequestException("Headers corrupted, invalid header sequence.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.HeaderLineMustNotStartWithWhitespace:
|
||||
ex = new BadHttpRequestException("Header line must not start with whitespace.");
|
||||
ex = new BadHttpRequestException("Header line must not start with whitespace.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.NoColonCharacterFoundInHeaderLine:
|
||||
ex = new BadHttpRequestException("No ':' character found in header line.");
|
||||
ex = new BadHttpRequestException("No ':' character found in header line.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName:
|
||||
ex = new BadHttpRequestException("Whitespace is not allowed in header name.");
|
||||
ex = new BadHttpRequestException("Whitespace is not allowed in header name.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.HeaderValueMustNotContainCR:
|
||||
ex = new BadHttpRequestException("Header value must not contain CR characters.");
|
||||
ex = new BadHttpRequestException("Header value must not contain CR characters.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.HeaderValueLineFoldingNotSupported:
|
||||
ex = new BadHttpRequestException("Header value line folding not supported.");
|
||||
ex = new BadHttpRequestException("Header value line folding not supported.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.MalformedRequestInvalidHeaders:
|
||||
ex = new BadHttpRequestException("Malformed request: invalid headers.");
|
||||
ex = new BadHttpRequestException("Malformed request: invalid headers.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.UnexpectedEndOfRequestContent:
|
||||
ex = new BadHttpRequestException("Unexpected end of request content.");
|
||||
ex = new BadHttpRequestException("Unexpected end of request content.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.BadChunkSuffix:
|
||||
ex = new BadHttpRequestException("Bad chunk suffix.");
|
||||
ex = new BadHttpRequestException("Bad chunk suffix.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.BadChunkSizeData:
|
||||
ex = new BadHttpRequestException("Bad chunk size data.");
|
||||
ex = new BadHttpRequestException("Bad chunk size data.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.ChunkedRequestIncomplete:
|
||||
ex = new BadHttpRequestException("Chunked request incomplete.");
|
||||
ex = new BadHttpRequestException("Chunked request incomplete.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.PathContainsNullCharacters:
|
||||
ex = new BadHttpRequestException("The path contains null characters.");
|
||||
ex = new BadHttpRequestException("The path contains null characters.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.InvalidCharactersInHeaderName:
|
||||
ex = new BadHttpRequestException("Invalid characters in header name.");
|
||||
ex = new BadHttpRequestException("Invalid characters in header name.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.NonAsciiOrNullCharactersInInputString:
|
||||
ex = new BadHttpRequestException("The input string contains non-ASCII or null characters.");
|
||||
ex = new BadHttpRequestException("The input string contains non-ASCII or null characters.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.RequestLineTooLong:
|
||||
ex = new BadHttpRequestException("Request line too long.");
|
||||
ex = new BadHttpRequestException("Request line too long.", 414);
|
||||
break;
|
||||
case RequestRejectionReason.MissingSpaceAfterMethod:
|
||||
ex = new BadHttpRequestException("No space character found after method in request line.");
|
||||
ex = new BadHttpRequestException("No space character found after method in request line.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.MissingSpaceAfterTarget:
|
||||
ex = new BadHttpRequestException("No space character found after target in request line.");
|
||||
ex = new BadHttpRequestException("No space character found after target in request line.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.MissingCrAfterVersion:
|
||||
ex = new BadHttpRequestException("Missing CR in request line.");
|
||||
ex = new BadHttpRequestException("Missing CR in request line.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.HeadersExceedMaxTotalSize:
|
||||
ex = new BadHttpRequestException("Request headers too long.");
|
||||
ex = new BadHttpRequestException("Request headers too long.", 431);
|
||||
break;
|
||||
case RequestRejectionReason.MissingCRInHeaderLine:
|
||||
ex = new BadHttpRequestException("No CR character found in header line.");
|
||||
ex = new BadHttpRequestException("No CR character found in header line.", 400);
|
||||
break;
|
||||
case RequestRejectionReason.TooManyHeaders:
|
||||
ex = new BadHttpRequestException("Request contains too many headers.");
|
||||
ex = new BadHttpRequestException("Request contains too many headers.", 431);
|
||||
break;
|
||||
default:
|
||||
ex = new BadHttpRequestException("Bad request.");
|
||||
ex = new BadHttpRequestException("Bad request.", 400);
|
||||
break;
|
||||
}
|
||||
return ex;
|
||||
|
|
@ -113,13 +115,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
switch (reason)
|
||||
{
|
||||
case RequestRejectionReason.MalformedRequestLineStatus:
|
||||
ex = new BadHttpRequestException($"Invalid request line: {value}");
|
||||
ex = new BadHttpRequestException($"Invalid request line: {value}", 400);
|
||||
break;
|
||||
case RequestRejectionReason.InvalidContentLength:
|
||||
ex = new BadHttpRequestException($"Invalid content length: {value}");
|
||||
ex = new BadHttpRequestException($"Invalid content length: {value}", 400);
|
||||
break;
|
||||
default:
|
||||
ex = new BadHttpRequestException("Bad request.");
|
||||
ex = new BadHttpRequestException("Bad request.", 400);
|
||||
break;
|
||||
}
|
||||
return ex;
|
||||
|
|
|
|||
|
|
@ -640,13 +640,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
if (_requestRejected)
|
||||
{
|
||||
// 400 Bad Request
|
||||
StatusCode = 400;
|
||||
_keepAlive = false;
|
||||
}
|
||||
else
|
||||
// If the request was rejected, StatusCode has already been set by SetBadRequestState
|
||||
if (!_requestRejected)
|
||||
{
|
||||
// 500 Internal Server Error
|
||||
StatusCode = 500;
|
||||
|
|
@ -1249,6 +1244,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
public void SetBadRequestState(BadHttpRequestException ex)
|
||||
{
|
||||
StatusCode = ex.StatusCode;
|
||||
_keepAlive = false;
|
||||
_requestProcessingStopping = true;
|
||||
_requestRejected = true;
|
||||
Log.ConnectionBadRequest(ConnectionId, ex);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
private static readonly byte[] _bytesStatus423 = Encoding.ASCII.GetBytes("423 Locked");
|
||||
private static readonly byte[] _bytesStatus424 = Encoding.ASCII.GetBytes("424 Failed Dependency");
|
||||
private static readonly byte[] _bytesStatus426 = Encoding.ASCII.GetBytes("426 Upgrade Required");
|
||||
private static readonly byte[] _bytesStatus431 = Encoding.ASCII.GetBytes("431 Request Header Fields Too Large");
|
||||
private static readonly byte[] _bytesStatus451 = Encoding.ASCII.GetBytes("451 Unavailable For Legal Reasons");
|
||||
private static readonly byte[] _bytesStatus500 = Encoding.ASCII.GetBytes("500 Internal Server Error");
|
||||
private static readonly byte[] _bytesStatus501 = Encoding.ASCII.GetBytes("501 Not Implemented");
|
||||
|
|
@ -157,6 +158,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return _bytesStatus424;
|
||||
case 426:
|
||||
return _bytesStatus426;
|
||||
case 431:
|
||||
return _bytesStatus431;
|
||||
case 451:
|
||||
return _bytesStatus451;
|
||||
case 500:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
|
@ -30,17 +27,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
var maxRequestLineSize = limit;
|
||||
|
||||
using (var host = BuildWebHost(options =>
|
||||
using (var server = CreateServer(limit))
|
||||
{
|
||||
options.Limits.MaxRequestLineSize = maxRequestLineSize;
|
||||
}))
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
using (var connection = new TestConnection(server.Port))
|
||||
{
|
||||
await connection.SendEnd($"{requestLine}\r\n");
|
||||
await connection.Receive($"HTTP/1.1 200 OK\r\n");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Transfer-Encoding: chunked",
|
||||
"",
|
||||
"c",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,33 +53,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
[InlineData("DELETE /a%20b%20c/d%20e?f=ghi HTTP/1.1\r\n")]
|
||||
public async Task ServerRejectsRequestLineExceedingLimit(string requestLine)
|
||||
{
|
||||
using (var host = BuildWebHost(options =>
|
||||
using (var server = CreateServer(requestLine.Length - 1))
|
||||
{
|
||||
options.Limits.MaxRequestLineSize = requestLine.Length - 1; // stop short of the '\n'
|
||||
}))
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
using (var connection = new TestConnection(server.Port))
|
||||
{
|
||||
await connection.SendAllTryEnd($"{requestLine}\r\n");
|
||||
await connection.Receive($"HTTP/1.1 400 Bad Request\r\n");
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 414 URI Too Long",
|
||||
"Connection: close",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IWebHost BuildWebHost(Action<KestrelServerOptions> options)
|
||||
private TestServer CreateServer(int maxRequestLineSize)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel(options)
|
||||
.UseUrls("http://127.0.0.1:0/")
|
||||
.Configure(app => app.Run(async context =>
|
||||
return new TestServer(async httpContext => await httpContext.Response.WriteAsync("hello, world"), new TestServiceContext
|
||||
{
|
||||
ServerOptions = new KestrelServerOptions
|
||||
{
|
||||
await context.Response.WriteAsync("hello, world");
|
||||
}))
|
||||
.Build();
|
||||
|
||||
return host;
|
||||
AddServerHeader = false,
|
||||
Limits =
|
||||
{
|
||||
MaxRequestLineSize = maxRequestLineSize
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
|
@ -28,17 +24,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
var headers = MakeHeaders(headerCount);
|
||||
|
||||
using (var host = BuildWebHost(options =>
|
||||
using (var server = CreateServer(maxRequestHeadersTotalSize: headers.Length + extraLimit))
|
||||
{
|
||||
options.Limits.MaxRequestHeadersTotalSize = headers.Length + extraLimit;
|
||||
}))
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
using (var connection = new TestConnection(server.Port))
|
||||
{
|
||||
await connection.SendEnd($"GET / HTTP/1.1\r\n{headers}\r\n");
|
||||
await connection.Receive($"HTTP/1.1 200 OK\r\n");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Transfer-Encoding: chunked",
|
||||
"",
|
||||
"c",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,17 +56,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
var headers = MakeHeaders(headerCount);
|
||||
|
||||
using (var host = BuildWebHost(options =>
|
||||
using (var server = CreateServer(maxRequestHeaderCount: maxHeaderCount))
|
||||
{
|
||||
options.Limits.MaxRequestHeaderCount = maxHeaderCount;
|
||||
}))
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
using (var connection = new TestConnection(server.Port))
|
||||
{
|
||||
await connection.SendEnd($"GET / HTTP/1.1\r\n{headers}\r\n");
|
||||
await connection.Receive($"HTTP/1.1 200 OK\r\n");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Transfer-Encoding: chunked",
|
||||
"",
|
||||
"c",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -78,17 +82,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
var headers = MakeHeaders(headerCount);
|
||||
|
||||
using (var host = BuildWebHost(options =>
|
||||
using (var server = CreateServer(maxRequestHeadersTotalSize: headers.Length - 1))
|
||||
{
|
||||
options.Limits.MaxRequestHeadersTotalSize = headers.Length - 1;
|
||||
}))
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
using (var connection = new TestConnection(server.Port))
|
||||
{
|
||||
await connection.SendAllTryEnd($"GET / HTTP/1.1\r\n{headers}\r\n");
|
||||
await connection.Receive($"HTTP/1.1 400 Bad Request\r\n");
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 431 Request Header Fields Too Large",
|
||||
"Connection: close",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,17 +106,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
var headers = MakeHeaders(headerCount);
|
||||
|
||||
using (var host = BuildWebHost(options =>
|
||||
using (var server = CreateServer(maxRequestHeaderCount: maxHeaderCount))
|
||||
{
|
||||
options.Limits.MaxRequestHeaderCount = maxHeaderCount;
|
||||
}))
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
using (var connection = new TestConnection(server.Port))
|
||||
{
|
||||
await connection.SendAllTryEnd($"GET / HTTP/1.1\r\n{headers}\r\n");
|
||||
await connection.Receive($"HTTP/1.1 400 Bad Request\r\n");
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 431 Request Header Fields Too Large",
|
||||
"Connection: close",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,18 +129,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
.Select(i => $"Header-{i}: value{i}\r\n"));
|
||||
}
|
||||
|
||||
private static IWebHost BuildWebHost(Action<KestrelServerOptions> options)
|
||||
private TestServer CreateServer(int? maxRequestHeaderCount = null, int? maxRequestHeadersTotalSize = null)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel(options)
|
||||
.UseUrls("http://127.0.0.1:0/")
|
||||
.Configure(app => app.Run(async context =>
|
||||
{
|
||||
await context.Response.WriteAsync("hello, world");
|
||||
}))
|
||||
.Build();
|
||||
var options = new KestrelServerOptions { AddServerHeader = false };
|
||||
|
||||
return host;
|
||||
if (maxRequestHeaderCount.HasValue)
|
||||
{
|
||||
options.Limits.MaxRequestHeaderCount = maxRequestHeaderCount.Value;
|
||||
}
|
||||
|
||||
if (maxRequestHeadersTotalSize.HasValue)
|
||||
{
|
||||
options.Limits.MaxRequestHeadersTotalSize = maxRequestHeadersTotalSize.Value;
|
||||
}
|
||||
|
||||
return new TestServer(async httpContext => await httpContext.Response.WriteAsync("hello, world"), new TestServiceContext
|
||||
{
|
||||
ServerOptions = options
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,13 +19,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("GET \r\n")]
|
||||
[InlineData("GET /\r\n")]
|
||||
[InlineData("GET / \r\n")]
|
||||
[InlineData("GET / H\r\n")]
|
||||
[InlineData("GET / HT\r\n")]
|
||||
[InlineData("GET / HTT\r\n")]
|
||||
[InlineData("GET / HTTP\r\n")]
|
||||
[InlineData("GET / HTTP/\r\n")]
|
||||
[InlineData("GET / HTTP/1\r\n")]
|
||||
[InlineData("GET / HTTP/1.\r\n")]
|
||||
// Missing method
|
||||
[InlineData(" \r\n")]
|
||||
// Missing second space
|
||||
|
|
@ -37,18 +30,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("GET / \r\n")]
|
||||
// Missing CR
|
||||
[InlineData("GET / \n")]
|
||||
// Unrecognized HTTP version
|
||||
[InlineData("GET / http/1.0\r\n")]
|
||||
[InlineData("GET / http/1.1\r\n")]
|
||||
[InlineData("GET / HTTP/1.1 \r\n")]
|
||||
[InlineData("GET / HTTP/1.1a\r\n")]
|
||||
[InlineData("GET / HTTP/1.0\n\r\n")]
|
||||
[InlineData("GET / HTTP/1.2\r\n")]
|
||||
[InlineData("GET / HTTP/3.0\r\n")]
|
||||
[InlineData("GET / H\r\n")]
|
||||
[InlineData("GET / HTTP/1.\r\n")]
|
||||
[InlineData("GET / hello\r\n")]
|
||||
[InlineData("GET / 8charact\r\n")]
|
||||
// Missing LF after CR
|
||||
[InlineData("GET / HTTP/1.0\rA\n")]
|
||||
// Bad HTTP Methods (invalid according to RFC)
|
||||
|
|
@ -78,7 +59,37 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAllTryEnd(request);
|
||||
await ReceiveBadRequestResponse(connection, server.Context.DateHeaderValue);
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET / H\r\n")]
|
||||
[InlineData("GET / HT\r\n")]
|
||||
[InlineData("GET / HTT\r\n")]
|
||||
[InlineData("GET / HTTP\r\n")]
|
||||
[InlineData("GET / HTTP/\r\n")]
|
||||
[InlineData("GET / HTTP/1\r\n")]
|
||||
[InlineData("GET / HTTP/1.\r\n")]
|
||||
[InlineData("GET / http/1.0\r\n")]
|
||||
[InlineData("GET / http/1.1\r\n")]
|
||||
[InlineData("GET / HTTP/1.1 \r\n")]
|
||||
[InlineData("GET / HTTP/1.1a\r\n")]
|
||||
[InlineData("GET / HTTP/1.2\r\n")]
|
||||
[InlineData("GET / HTTP/3.0\r\n")]
|
||||
[InlineData("GET / H\r\n")]
|
||||
[InlineData("GET / HTTP/1.\r\n")]
|
||||
[InlineData("GET / hello\r\n")]
|
||||
[InlineData("GET / 8charact\r\n")]
|
||||
public async Task TestInvalidRequestLinesWithUnsupportedVersion(string request)
|
||||
{
|
||||
using (var server = new TestServer(context => TaskCache.CompletedTask))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAllTryEnd(request);
|
||||
await ReceiveBadRequestResponse(connection, "505 HTTP Version Not Supported", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,7 +125,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAllTryEnd($"GET / HTTP/1.1\r\n{rawHeaders}");
|
||||
await ReceiveBadRequestResponse(connection, server.Context.DateHeaderValue);
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,7 +142,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
"H\u00eb\u00e4d\u00ebr: value",
|
||||
"",
|
||||
"");
|
||||
await ReceiveBadRequestResponse(connection, server.Context.DateHeaderValue);
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -158,15 +169,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAllTryEnd($"GET {path} HTTP/1.1\r\n");
|
||||
await ReceiveBadRequestResponse(connection, server.Context.DateHeaderValue);
|
||||
await ReceiveBadRequestResponse(connection, "400 Bad Request", server.Context.DateHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveBadRequestResponse(TestConnection connection, string expectedDateHeaderValue)
|
||||
private async Task ReceiveBadRequestResponse(TestConnection connection, string expectedResponseStatusCode, string expectedDateHeaderValue)
|
||||
{
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
$"HTTP/1.1 {expectedResponseStatusCode}",
|
||||
"Connection: close",
|
||||
$"Date: {expectedDateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
"",
|
||||
"");
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
"HTTP/1.1 431 Request Header Fields Too Large",
|
||||
"Connection: close",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
|
|
@ -331,7 +331,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
"",
|
||||
"");
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
"HTTP/1.1 431 Request Header Fields Too Large",
|
||||
"Connection: close",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
|
@ -243,8 +246,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
const string key = "\u00141ód\017c";
|
||||
|
||||
var encoding = Encoding.GetEncoding("iso-8859-1");
|
||||
Assert.Throws<BadHttpRequestException>(
|
||||
var exception = Assert.Throws<BadHttpRequestException>(
|
||||
() => headers.Append(encoding.GetBytes(key), 0, encoding.GetByteCount(key), key));
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("Header: line1\r\n\t\tline2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n \t\t line2\r\n\r\n")]
|
||||
[InlineData("Header: line1\r\n \t \t line2\r\n\r\n")]
|
||||
public void ThrowsOnHeaderValueWithLineFolding(string rawHeaders)
|
||||
public void TakeMessageHeadersThrowsOnHeaderValueWithLineFolding(string rawHeaders)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -210,11 +210,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Header value line folding not supported.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThrowsOnHeaderValueWithLineFolding_CharacterNotAvailableOnFirstAttempt()
|
||||
public void TakeMessageHeadersThrowsOnHeaderValueWithLineFolding_CharacterNotAvailableOnFirstAttempt()
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -241,6 +242,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Header value line folding not supported.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +252,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("Header-1: value1\rHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: v\ralue2\r\n")]
|
||||
public void ThrowsOnHeaderValueContainingCR(string rawHeaders)
|
||||
public void TakeMessageHeadersThrowsOnHeaderValueContainingCR(string rawHeaders)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -273,6 +275,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Header value must not contain CR characters.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +283,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("Header-1 value1\r\n\r\n")]
|
||||
[InlineData("Header-1 value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2 value2\r\n\r\n")]
|
||||
public void ThrowsOnHeaderLineMissingColon(string rawHeaders)
|
||||
public void TakeMessageHeadersThrowsOnHeaderLineMissingColon(string rawHeaders)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -303,6 +306,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("No ':' character found in header line.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -311,7 +315,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("\tHeader: value\r\n\r\n")]
|
||||
[InlineData(" Header-1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
[InlineData("\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n")]
|
||||
public void ThrowsOnHeaderLineStartingWithWhitespace(string rawHeaders)
|
||||
public void TakeMessageHeadersThrowsOnHeaderLineStartingWithWhitespace(string rawHeaders)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -334,6 +338,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Header line must not start with whitespace.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -346,7 +351,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("Header-1: value1\r\nHeader 2: value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2 : value2\r\n\r\n")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2\t: value2\r\n\r\n")]
|
||||
public void ThrowsOnWhitespaceInHeaderName(string rawHeaders)
|
||||
public void TakeMessageHeadersThrowsOnWhitespaceInHeaderName(string rawHeaders)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -369,6 +374,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Whitespace is not allowed in header name.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +382,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r\r")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r ")]
|
||||
[InlineData("Header-1: value1\r\nHeader-2: value2\r\n\r \n")]
|
||||
public void ThrowsOnHeadersNotEndingInCRLFLine(string rawHeaders)
|
||||
public void TakeMessageHeadersThrowsOnHeadersNotEndingInCRLFLine(string rawHeaders)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -399,11 +405,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Headers corrupted, invalid header sequence.", exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThrowsWhenHeadersExceedTotalSizeLimit()
|
||||
public void TakeMessageHeadersThrowsWhenHeadersExceedTotalSizeLimit()
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -432,11 +439,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Request headers too long.", exception.Message);
|
||||
Assert.Equal(431, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThrowsWhenHeadersExceedCountLimit()
|
||||
public void TakeMessageHeadersThrowsWhenHeadersExceedCountLimit()
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
|
|
@ -465,6 +473,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders));
|
||||
Assert.Equal("Request contains too many headers.", exception.Message);
|
||||
Assert.Equal(431, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -842,6 +851,107 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TakeStartLineThrowsWhenTooLong()
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
using (var pool = new MemoryPool())
|
||||
using (var socketInput = new SocketInput(pool, ltp))
|
||||
{
|
||||
var connectionContext = new ConnectionContext()
|
||||
{
|
||||
ConnectionControl = Mock.Of<IConnectionControl>(),
|
||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
|
||||
ServerOptions = new KestrelServerOptions()
|
||||
{
|
||||
Limits =
|
||||
{
|
||||
MaxRequestLineSize = "GET / HTTP/1.1\r\n".Length
|
||||
}
|
||||
},
|
||||
Log = trace
|
||||
};
|
||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||
frame.Reset();
|
||||
|
||||
var requestLineBytes = Encoding.ASCII.GetBytes("GET /a HTTP/1.1\r\n");
|
||||
socketInput.IncomingData(requestLineBytes, 0, requestLineBytes.Length);
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeStartLine(socketInput));
|
||||
Assert.Equal("Request line too long.", exception.Message);
|
||||
Assert.Equal(414, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET/HTTP/1.1\r\n", "No space character found after method in request line.")]
|
||||
[InlineData(" / HTTP/1.1\r\n", "Missing method.")]
|
||||
[InlineData("GET? / HTTP/1.1\r\n", "Invalid method.")]
|
||||
[InlineData("GET /HTTP/1.1\r\n", "No space character found after target in request line.")]
|
||||
[InlineData("GET /a?b=cHTTP/1.1\r\n", "No space character found after target in request line.")]
|
||||
[InlineData("GET /a%20bHTTP/1.1\r\n", "No space character found after target in request line.")]
|
||||
[InlineData("GET /a%20b?c=dHTTP/1.1\r\n", "No space character found after target in request line.")]
|
||||
[InlineData("GET HTTP/1.1\r\n", "Missing request target.")]
|
||||
[InlineData("GET / HTTP/1.1\n", "Missing CR in request line.")]
|
||||
[InlineData("GET / \r\n", "Missing HTTP version.")]
|
||||
[InlineData("GET / HTTP/1.1\ra\n", "Missing LF in request line.")]
|
||||
public void TakeStartLineThrowsWhenInvalid(string requestLine, string expectedExceptionMessage)
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
using (var pool = new MemoryPool())
|
||||
using (var socketInput = new SocketInput(pool, ltp))
|
||||
{
|
||||
var connectionContext = new ConnectionContext()
|
||||
{
|
||||
ConnectionControl = Mock.Of<IConnectionControl>(),
|
||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
|
||||
ServerOptions = new KestrelServerOptions(),
|
||||
Log = trace
|
||||
};
|
||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||
frame.Reset();
|
||||
|
||||
var requestLineBytes = Encoding.ASCII.GetBytes(requestLine);
|
||||
socketInput.IncomingData(requestLineBytes, 0, requestLineBytes.Length);
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeStartLine(socketInput));
|
||||
Assert.Equal(expectedExceptionMessage, exception.Message);
|
||||
Assert.Equal(400, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TakeStartLineThrowsOnUnsupportedHttpVersion()
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
using (var pool = new MemoryPool())
|
||||
using (var socketInput = new SocketInput(pool, ltp))
|
||||
{
|
||||
var connectionContext = new ConnectionContext()
|
||||
{
|
||||
ConnectionControl = Mock.Of<IConnectionControl>(),
|
||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
|
||||
ServerOptions = new KestrelServerOptions(),
|
||||
Log = trace
|
||||
};
|
||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||
frame.Reset();
|
||||
|
||||
var requestLineBytes = Encoding.ASCII.GetBytes("GET / HTTP/1.2\r\n");
|
||||
socketInput.IncomingData(requestLineBytes, 0, requestLineBytes.Length);
|
||||
|
||||
var exception = Assert.Throws<BadHttpRequestException>(() => frame.TakeStartLine(socketInput));
|
||||
Assert.Equal("Unrecognized HTTP version.", exception.Message);
|
||||
Assert.Equal(505, exception.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TakeMessageHeadersCallsConsumingCompleteWithFurthestExamined()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue