Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Commit ce408a9

Browse files
committed
#578 Do not buffer the request body by default when reading forms.
1 parent 5da3673 commit ce408a9

File tree

6 files changed

+261
-46
lines changed

6 files changed

+261
-46
lines changed

src/Microsoft.AspNetCore.Http/BufferingHelper.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static string TempDirectory
2222
if (_tempDirectory == null)
2323
{
2424
// Look for folders in the following order.
25-
var temp = Environment.GetEnvironmentVariable("ASPNET_TEMP") ?? // ASPNET_TEMP - User set temporary location.
25+
var temp = Environment.GetEnvironmentVariable("ASPNETCORE_TEMP") ?? // ASPNETCORE_TEMP - User set temporary location.
2626
Path.GetTempPath(); // Fall back.
2727

2828
if (!Directory.Exists(temp))
@@ -54,5 +54,26 @@ public static HttpRequest EnableRewind(this HttpRequest request, int bufferThres
5454
}
5555
return request;
5656
}
57+
58+
public static MultipartSection EnableRewind(this MultipartSection section, Action<IDisposable> registerForDispose, int bufferThreshold = DefaultBufferThreshold)
59+
{
60+
if (section == null)
61+
{
62+
throw new ArgumentNullException(nameof(section));
63+
}
64+
if (registerForDispose == null)
65+
{
66+
throw new ArgumentNullException(nameof(registerForDispose));
67+
}
68+
69+
var body = section.Body;
70+
if (!body.CanSeek)
71+
{
72+
var fileStream = new FileBufferingReadStream(body, bufferThreshold, _getTempDirectory);
73+
section.Body = fileStream;
74+
registerForDispose(fileStream);
75+
}
76+
return section;
77+
}
5778
}
5879
}

src/Microsoft.AspNetCore.Http/Features/FormFeature.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,6 @@ private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancell
118118

119119
cancellationToken.ThrowIfCancellationRequested();
120120

121-
_request.EnableRewind();
122-
123121
FormCollection formFields = null;
124122
FormFileCollection files = null;
125123

@@ -146,16 +144,27 @@ private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancell
146144
ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);
147145
if (HasFileContentDisposition(contentDisposition))
148146
{
147+
// Enable buffering for the file if not already done for the full body
148+
section.EnableRewind(_request.HttpContext.Response.RegisterForDispose);
149149
// Find the end
150150
await section.Body.DrainAsync(cancellationToken);
151151

152152
var name = HeaderUtilities.RemoveQuotes(contentDisposition.Name) ?? string.Empty;
153153
var fileName = HeaderUtilities.RemoveQuotes(contentDisposition.FileName) ?? string.Empty;
154154

155-
var file = new FormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length, name, fileName)
155+
FormFile file;
156+
if (section.BaseStreamOffset.HasValue)
157+
{
158+
// Relative reference to buffered request body
159+
file = new FormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length, name, fileName);
160+
}
161+
else
156162
{
157-
Headers = new HeaderDictionary(section.Headers),
158-
};
163+
// Individually buffered file body
164+
file = new FormFile(section.Body, 0, section.Body.Length, name, fileName);
165+
}
166+
file.Headers = new HeaderDictionary(section.Headers);
167+
159168
if (files == null)
160169
{
161170
files = new FormFileCollection();
@@ -194,7 +203,10 @@ private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancell
194203
}
195204

196205
// Rewind so later readers don't have to.
197-
_request.Body.Seek(0, SeekOrigin.Begin);
206+
if (_request.Body.CanSeek)
207+
{
208+
_request.Body.Seek(0, SeekOrigin.Begin);
209+
}
198210

199211
if (formFields != null)
200212
{

src/Microsoft.AspNetCore.Http/Features/HttpResponseFeature.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ public HttpResponseFeature()
2525

2626
public Stream Body { get; set; }
2727

28-
public bool HasStarted
28+
public virtual bool HasStarted
2929
{
3030
get { return false; }
3131
}
3232

33-
public void OnStarting(Func<object, Task> callback, object state)
33+
public virtual void OnStarting(Func<object, Task> callback, object state)
3434
{
3535
throw new NotImplementedException();
3636
}
3737

38-
public void OnCompleted(Func<object, Task> callback, object state)
38+
public virtual void OnCompleted(Func<object, Task> callback, object state)
3939
{
4040
throw new NotImplementedException();
4141
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Http.Features.Internal;
8+
9+
namespace Microsoft.AspNetCore.Http.Features.Internal
10+
{
11+
public class FakeResponseFeature : HttpResponseFeature
12+
{
13+
List<Tuple<Func<object, Task>, object>> _onCompletedCallbacks = new List<Tuple<Func<object, Task>, object>>();
14+
15+
public override void OnCompleted(Func<object, Task> callback, object state)
16+
{
17+
_onCompletedCallbacks.Add(new Tuple<Func<object, Task>, object>(callback, state));
18+
}
19+
20+
public async Task CompleteAsync()
21+
{
22+
var callbacks = _onCompletedCallbacks;
23+
_onCompletedCallbacks = null;
24+
foreach (var callback in callbacks)
25+
{
26+
await callback.Item1(callback.Item2);
27+
}
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)