From 41f77ee3fd14df9d666efd3662803ae4294b4947 Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Fri, 26 Feb 2016 14:11:34 -0800 Subject: [PATCH] Handle uploads larger than 2GB. --- .../Http/MessageBody.cs | 13 +-- .../RequestTests.cs | 84 ++++++++++++------- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/MessageBody.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/MessageBody.cs index 381b084048..d4072572f8 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/MessageBody.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/MessageBody.cs @@ -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; diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs index 5c73e751f2..db7a6800f2 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs @@ -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 - { - { "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 { - { "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 { - { "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 { - { "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 =>