Revert "Add MaxRequestBodySize limit (#478)."
This reverts commit e53a87be9c.
This commit is contained in:
parent
859816bb53
commit
defcbdb907
|
|
@ -78,9 +78,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
case RequestRejectionReason.RequestTimeout:
|
case RequestRejectionReason.RequestTimeout:
|
||||||
ex = new BadHttpRequestException("Request timed out.", 408);
|
ex = new BadHttpRequestException("Request timed out.", 408);
|
||||||
break;
|
break;
|
||||||
case RequestRejectionReason.PayloadTooLarge:
|
|
||||||
ex = new BadHttpRequestException("Payload too large.", 413);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
ex = new BadHttpRequestException("Bad request.", 400);
|
ex = new BadHttpRequestException("Bad request.", 400);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
private DateHeaderValueManager DateHeaderValueManager => ConnectionContext.ListenerContext.ServiceContext.DateHeaderValueManager;
|
private DateHeaderValueManager DateHeaderValueManager => ConnectionContext.ListenerContext.ServiceContext.DateHeaderValueManager;
|
||||||
private ServerAddress ServerAddress => ConnectionContext.ListenerContext.ServerAddress;
|
private ServerAddress ServerAddress => ConnectionContext.ListenerContext.ServerAddress;
|
||||||
// Hold direct reference to ServerOptions since this is used very often in the request processing path
|
// Hold direct reference to ServerOptions since this is used very often in the request processing path
|
||||||
public KestrelServerOptions ServerOptions { get; }
|
private KestrelServerOptions ServerOptions { get; }
|
||||||
private IPEndPoint LocalEndPoint => ConnectionContext.LocalEndPoint;
|
private IPEndPoint LocalEndPoint => ConnectionContext.LocalEndPoint;
|
||||||
private IPEndPoint RemoteEndPoint => ConnectionContext.RemoteEndPoint;
|
private IPEndPoint RemoteEndPoint => ConnectionContext.RemoteEndPoint;
|
||||||
protected string ConnectionId => ConnectionContext.ConnectionId;
|
protected string ConnectionId => ConnectionContext.ConnectionId;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
|
|
@ -271,12 +272,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var contentLength = FrameHeaders.ParseContentLength(unparsedContentLength);
|
var contentLength = FrameHeaders.ParseContentLength(unparsedContentLength);
|
||||||
|
|
||||||
if (contentLength > context.ServerOptions.Limits.MaxRequestBodySize)
|
|
||||||
{
|
|
||||||
context.RejectRequest(RequestRejectionReason.PayloadTooLarge);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ForContentLength(keepAlive, contentLength, context);
|
return new ForContentLength(keepAlive, contentLength, context);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
|
|
@ -286,7 +281,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid slowing down most common case
|
// Avoid slowing down most common case
|
||||||
if (!ReferenceEquals(context.Method, HttpMethods.Get))
|
if (!object.ReferenceEquals(context.Method, HttpMethods.Get))
|
||||||
{
|
{
|
||||||
// If we got here, request contains no Content-Length or Transfer-Encoding header.
|
// If we got here, request contains no Content-Length or Transfer-Encoding header.
|
||||||
// Reject with 411 Length Required.
|
// Reject with 411 Length Required.
|
||||||
|
|
@ -399,7 +394,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
private readonly SocketInput _input;
|
private readonly SocketInput _input;
|
||||||
private readonly FrameRequestHeaders _requestHeaders;
|
private readonly FrameRequestHeaders _requestHeaders;
|
||||||
private long _inputBytesRead;
|
|
||||||
private int _inputLength;
|
private int _inputLength;
|
||||||
|
|
||||||
private Mode _mode = Mode.Prefix;
|
private Mode _mode = Mode.Prefix;
|
||||||
|
|
@ -420,12 +414,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
protected override void OnConsumedBytes(int count)
|
protected override void OnConsumedBytes(int count)
|
||||||
{
|
{
|
||||||
_inputLength -= count;
|
_inputLength -= count;
|
||||||
_inputBytesRead += count;
|
|
||||||
|
|
||||||
if (_inputBytesRead > _context.ServerOptions.Limits.MaxRequestBodySize)
|
|
||||||
{
|
|
||||||
_context.RejectRequest(RequestRejectionReason.PayloadTooLarge);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ArraySegment<byte>> PeekStateMachineAsync()
|
private async Task<ArraySegment<byte>> PeekStateMachineAsync()
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
FinalTransferCodingNotChunked,
|
FinalTransferCodingNotChunked,
|
||||||
LengthRequired,
|
LengthRequired,
|
||||||
LengthRequiredHttp10,
|
LengthRequiredHttp10,
|
||||||
PayloadTooLarge
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
// Matches the default LimitRequestFields in Apache httpd.
|
// Matches the default LimitRequestFields in Apache httpd.
|
||||||
private int _maxRequestHeaderCount = 100;
|
private int _maxRequestHeaderCount = 100;
|
||||||
|
|
||||||
// Matches default HttpRuntimeSection.MaxRequestLength.
|
|
||||||
// https://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.maxrequestlength%28v=vs.100%29.aspx
|
|
||||||
private long _maxRequestBodySize = 4 * 1024 * 1024;
|
|
||||||
|
|
||||||
// Matches the default http.sys connectionTimeout.
|
// Matches the default http.sys connectionTimeout.
|
||||||
private TimeSpan _keepAliveTimeout = TimeSpan.FromMinutes(2);
|
private TimeSpan _keepAliveTimeout = TimeSpan.FromMinutes(2);
|
||||||
|
|
||||||
|
|
@ -148,28 +144,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the maximum request body size, in bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Defaults to 4MB.
|
|
||||||
/// </remarks>
|
|
||||||
public long MaxRequestBodySize
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _maxRequestBodySize;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value <= 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(value), "Value must a positive integer.");
|
|
||||||
}
|
|
||||||
_maxRequestBodySize = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the keep-alive timeout.
|
/// Gets or sets the keep-alive timeout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -178,10 +178,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
AddServerHeader = false,
|
AddServerHeader = false,
|
||||||
Limits =
|
Limits =
|
||||||
{
|
{
|
||||||
KeepAliveTimeout = KeepAliveTimeout,
|
KeepAliveTimeout = KeepAliveTimeout
|
||||||
// Prevent request rejection if ConnectionNotTimedOutWhileRequestBeingSent
|
|
||||||
// sends more bytes than the default body size limit.
|
|
||||||
MaxRequestBodySize = long.MaxValue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
using Microsoft.AspNetCore.Testing;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|
||||||
{
|
|
||||||
public class MaxRequestBodySizeTests
|
|
||||||
{
|
|
||||||
private const int MaxRequestBodySize = 128;
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(MaxRequestBodySize - 1, 0)]
|
|
||||||
[InlineData(MaxRequestBodySize, 0)]
|
|
||||||
[InlineData(MaxRequestBodySize - 1, 1)]
|
|
||||||
[InlineData(MaxRequestBodySize, 1)]
|
|
||||||
[InlineData(MaxRequestBodySize - 1, 2)]
|
|
||||||
[InlineData(MaxRequestBodySize, 2)]
|
|
||||||
public async Task ServerAcceptsRequestBodyWithinLimit(int requestBodySize, int chunks)
|
|
||||||
{
|
|
||||||
using (var server = CreateServer(MaxRequestBodySize, async httpContext => await httpContext.Response.WriteAsync("hello, world")))
|
|
||||||
{
|
|
||||||
using (var connection = new TestConnection(server.Port))
|
|
||||||
{
|
|
||||||
await connection.SendAll(BuildRequest(connection, requestBodySize, chunks));
|
|
||||||
|
|
||||||
await connection.Receive(
|
|
||||||
"HTTP/1.1 200 OK",
|
|
||||||
$"Date: {server.Context.DateHeaderValue}",
|
|
||||||
"Transfer-Encoding: chunked",
|
|
||||||
"",
|
|
||||||
"c",
|
|
||||||
"hello, world",
|
|
||||||
"0",
|
|
||||||
"",
|
|
||||||
"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(MaxRequestBodySize + 1, 0)]
|
|
||||||
[InlineData(MaxRequestBodySize + 1, 1)]
|
|
||||||
[InlineData(MaxRequestBodySize + 1, 2)]
|
|
||||||
public async Task ServerRejectsRequestBodyExceedingLimit(int requestBodySize, int chunks)
|
|
||||||
{
|
|
||||||
using (var server = CreateServer(MaxRequestBodySize, async httpContext =>
|
|
||||||
{
|
|
||||||
var received = 0;
|
|
||||||
while (received < requestBodySize)
|
|
||||||
{
|
|
||||||
received += await httpContext.Request.Body.ReadAsync(new byte[1024], 0, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should never get here
|
|
||||||
await httpContext.Response.WriteAsync("hello, world");
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
using (var connection = new TestConnection(server.Port))
|
|
||||||
{
|
|
||||||
await connection.SendAll(BuildRequest(connection, requestBodySize, chunks));
|
|
||||||
|
|
||||||
await connection.ReceiveForcedEnd(
|
|
||||||
"HTTP/1.1 413 Payload Too Large",
|
|
||||||
"Connection: close",
|
|
||||||
$"Date: {server.Context.DateHeaderValue}",
|
|
||||||
"Content-Length: 0",
|
|
||||||
"",
|
|
||||||
"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MaxRequestBodySizeNotEnforcedOnUpgradedConnection()
|
|
||||||
{
|
|
||||||
var sendBytes = MaxRequestBodySize + 1;
|
|
||||||
|
|
||||||
using (var server = CreateServer(MaxRequestBodySize, async httpContext =>
|
|
||||||
{
|
|
||||||
var stream = await httpContext.Features.Get<IHttpUpgradeFeature>().UpgradeAsync();
|
|
||||||
|
|
||||||
var received = 0;
|
|
||||||
while (received < sendBytes)
|
|
||||||
{
|
|
||||||
received += await stream.ReadAsync(new byte[1024], 0, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = Encoding.ASCII.GetBytes($"{received}");
|
|
||||||
await stream.WriteAsync(response, 0, response.Length);
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
using (var connection = new TestConnection(server.Port))
|
|
||||||
{
|
|
||||||
await connection.Send(
|
|
||||||
"GET / HTTP/1.1",
|
|
||||||
"Connection: upgrade",
|
|
||||||
"",
|
|
||||||
new string('a', sendBytes));
|
|
||||||
|
|
||||||
await connection.Receive(
|
|
||||||
"HTTP/1.1 101 Switching Protocols",
|
|
||||||
"Connection: Upgrade",
|
|
||||||
$"Date: {server.Context.DateHeaderValue}",
|
|
||||||
"",
|
|
||||||
$"{sendBytes}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildRequest(TestConnection connection, int requestBodySize, int chunks)
|
|
||||||
{
|
|
||||||
var request = new StringBuilder();
|
|
||||||
|
|
||||||
request.Append("POST / HTTP/1.1\r\n");
|
|
||||||
|
|
||||||
if (chunks == 0)
|
|
||||||
{
|
|
||||||
request.Append($"Content-Length: {requestBodySize}\r\n\r\n");
|
|
||||||
request.Append(new string('a', requestBodySize));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
request.Append("Transfer-Encoding: chunked\r\n\r\n");
|
|
||||||
|
|
||||||
var bytesSent = 0;
|
|
||||||
while (bytesSent < requestBodySize)
|
|
||||||
{
|
|
||||||
var chunkSize = Math.Min(requestBodySize / chunks, requestBodySize - bytesSent);
|
|
||||||
|
|
||||||
request.Append($"{chunkSize:X}\r\n");
|
|
||||||
request.Append(new string('a', chunkSize));
|
|
||||||
request.Append("\r\n");
|
|
||||||
|
|
||||||
bytesSent += chunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we sent the right amount of data
|
|
||||||
Assert.Equal(requestBodySize, bytesSent);
|
|
||||||
|
|
||||||
request.Append("0\r\n\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TestServer CreateServer(int maxRequestBodySize, RequestDelegate app)
|
|
||||||
{
|
|
||||||
return new TestServer(app, new TestServiceContext
|
|
||||||
{
|
|
||||||
ServerOptions = new KestrelServerOptions
|
|
||||||
{
|
|
||||||
AddServerHeader = false,
|
|
||||||
Limits =
|
|
||||||
{
|
|
||||||
MaxRequestBodySize = maxRequestBodySize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -169,7 +169,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
.UseKestrel(options =>
|
.UseKestrel(options =>
|
||||||
{
|
{
|
||||||
options.Limits.MaxRequestBufferSize = maxRequestBufferSize;
|
options.Limits.MaxRequestBufferSize = maxRequestBufferSize;
|
||||||
options.Limits.MaxRequestBodySize = _dataLength;
|
|
||||||
options.UseHttps(@"TestResources/testCert.pfx", "testPassword");
|
options.UseHttps(@"TestResources/testCert.pfx", "testPassword");
|
||||||
|
|
||||||
if (maxRequestBufferSize.HasValue &&
|
if (maxRequestBufferSize.HasValue &&
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
Assert.True(bufferLength % 256 == 0, $"{nameof(bufferLength)} must be evenly divisible by 256");
|
Assert.True(bufferLength % 256 == 0, $"{nameof(bufferLength)} must be evenly divisible by 256");
|
||||||
|
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.UseKestrel(options =>
|
.UseKestrel()
|
||||||
{
|
|
||||||
options.Limits.MaxRequestBodySize = contentLength;
|
|
||||||
})
|
|
||||||
.UseUrls("http://127.0.0.1:0/")
|
.UseUrls("http://127.0.0.1:0/")
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -149,34 +149,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Assert.Equal(value, o.MaxRequestHeaderCount);
|
Assert.Equal(value, o.MaxRequestHeaderCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MaxRequestBodySizeDefault()
|
|
||||||
{
|
|
||||||
Assert.Equal(4 * 1024 * 1024, (new KestrelServerLimits()).MaxRequestBodySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(long.MinValue)]
|
|
||||||
[InlineData(-1)]
|
|
||||||
[InlineData(0)]
|
|
||||||
public void MaxRequestBodySizeInvalid(long value)
|
|
||||||
{
|
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
|
||||||
{
|
|
||||||
(new KestrelServerLimits()).MaxRequestBodySize = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(long.MaxValue)]
|
|
||||||
public void MaxRequestBodySizeValid(long value)
|
|
||||||
{
|
|
||||||
var o = new KestrelServerLimits();
|
|
||||||
o.MaxRequestBodySize = value;
|
|
||||||
Assert.Equal(value, o.MaxRequestBodySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void KeepAliveTimeoutDefault()
|
public void KeepAliveTimeoutDefault()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue