Handle uploads larger than 2GB.

This commit is contained in:
Cesar Blum Silveira 2016-02-26 14:11:34 -08:00
parent 4e5920fd09
commit 41f77ee3fd
2 changed files with 63 additions and 34 deletions

View File

@ -122,8 +122,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
var unparsedContentLength = headers.HeaderContentLength.ToString();
if (unparsedContentLength.Length > 0)
{
int contentLength;
if (!int.TryParse(unparsedContentLength, out contentLength) || contentLength < 0)
long contentLength;
if (!long.TryParse(unparsedContentLength, out contentLength) || contentLength < 0)
{
context.ReportCorruptedHttpRequest(new BadHttpRequestException("Invalid content length."));
return new ForContentLength(keepAlive, 0, context);
@ -166,10 +166,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private class ForContentLength : MessageBody
{
private readonly int _contentLength;
private int _inputLength;
private readonly long _contentLength;
private long _inputLength;
public ForContentLength(bool keepAlive, int contentLength, Frame context)
public ForContentLength(bool keepAlive, long contentLength, Frame context)
: base(context)
{
RequestKeepAlive = keepAlive;
@ -181,7 +181,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
var input = _context.SocketInput;
var limit = buffer.Array == null ? _inputLength : Math.Min(buffer.Count, _inputLength);
var inputLengthLimit = (int)Math.Min(_inputLength, int.MaxValue);
var limit = buffer.Array == null ? inputLengthLimit : Math.Min(buffer.Count, inputLengthLimit);
if (limit == 0)
{
return 0;

View File

@ -1,7 +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.Collections.Generic;
using System;
using System.Globalization;
using System.Net.Http;
using System.Net.Sockets;
@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Xunit;
@ -24,16 +23,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public async Task LargeUpload()
{
var port = PortManager.GetPort();
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "server.urls", $"http://localhost:{port}/" }
})
.Build();
var builder = new WebHostBuilder()
.UseConfiguration(config)
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseUrls($"http://localhost:{port}/")
.Configure(app =>
{
app.Run(async context =>
@ -76,6 +68,57 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
}
[ConditionalFact]
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Fails on Mono on Mac because it is not 64-bit.")]
public async Task LargeMultipartUpload()
{
var port = PortManager.GetPort();
var builder = new WebHostBuilder()
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseUrls($"http://localhost:{port}/")
.Configure(app =>
{
app.Run(async context =>
{
long total = 0;
var bytes = new byte[1024];
var count = await context.Request.Body.ReadAsync(bytes, 0, bytes.Length);
while (count > 0)
{
total += count;
count = await context.Request.Body.ReadAsync(bytes, 0, bytes.Length);
}
await context.Response.WriteAsync(total.ToString(CultureInfo.InvariantCulture));
});
});
using (var host = builder.Build())
{
host.Start();
using (var client = new HttpClient())
{
using (var form = new MultipartFormDataContent())
{
const int oneMegabyte = 1024 * 1024;
const int files = 2048;
var bytes = new byte[oneMegabyte];
for (int i = 0; i < files; i++)
{
var fileName = Guid.NewGuid().ToString();
form.Add(new ByteArrayContent(bytes), "file", fileName);
}
var length = form.Headers.ContentLength.Value;
var response = await client.PostAsync($"http://localhost:{port}/", form);
response.EnsureSuccessStatusCode();
Assert.Equal(length.ToString(CultureInfo.InvariantCulture), await response.Content.ReadAsStringAsync());
}
}
}
}
[Theory]
[InlineData("127.0.0.1", "127.0.0.1")]
[InlineData("localhost", "127.0.0.1")]
@ -95,14 +138,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public async Task DoesNotHangOnConnectionCloseRequest()
{
var port = PortManager.GetPort();
var config = new ConfigurationBuilder().AddInMemoryCollection(
new Dictionary<string, string> {
{ "server.urls", $"http://localhost:{port}" }
}).Build();
var builder = new WebHostBuilder()
.UseConfiguration(config)
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseUrls($"http://localhost:{port}")
.Configure(app =>
{
app.Run(async context =>
@ -129,14 +167,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public void RequestPathIsNormalized()
{
var port = PortManager.GetPort();
var config = new ConfigurationBuilder().AddInMemoryCollection(
new Dictionary<string, string> {
{ "server.urls", $"http://localhost:{port}/\u0041\u030A" }
}).Build();
var builder = new WebHostBuilder()
.UseConfiguration(config)
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseUrls($"http://localhost:{port}/\u0041\u030A")
.Configure(app =>
{
app.Run(async context =>
@ -178,14 +211,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress)
{
var port = PortManager.GetPort();
var config = new ConfigurationBuilder().AddInMemoryCollection(
new Dictionary<string, string> {
{ "server.urls", $"http://{registerAddress}:{port}" }
}).Build();
var builder = new WebHostBuilder()
.UseConfiguration(config)
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseUrls($"http://{registerAddress}:{port}")
.Configure(app =>
{
app.Run(async context =>