-
Notifications
You must be signed in to change notification settings - Fork 191
Mime multipart request parsing. #146
Conversation
@Tratcher : Just FYI...in Web API, some users had concerns about upload files to a directory on the web server and wanted the ability to upload files directly to Amazon or Azure storage services...if its small files then one can read the multipart content in-memory and upload them to these external storage services, but obviously this is not a recommended approach for large files...so checking to see if we have/plan the extensibility points for achieving this scenario... |
@kichalla That should be easy enough. See |
👍 Thanks... |
while (await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken) > 0) | ||
{ | ||
// Not all streams support cancellation directly. | ||
cancellationToken.ThrowIfCancellationRequested(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not check the passive way too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (cancellationToken.IsCancellationRequested)
?
Then what? I can't return a cancelled task from an async
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes true.
@Tratcher this is one of the topics that we should discuss in next week's mtg to go over the HTTP abstractions. This is a pretty big feature area that we want to have a broad discussion on before committing any codez. |
{ | ||
#if ASPNETCORE50 | ||
Task<int> t = (Task<int>)asyncResult; | ||
t.Wait(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 171 - 177 can be replaced by try { return t.GetAwaiter().GetResult(); } catch (Exception ex) { /* wrap and rethrow ex */ }
. This has the added benefit of not exposing AggregateException up the stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's even worse, actually 😄
Given that Wait
always throws an AggregateException
when the task doesn't run to completion, the if
statement is either not reached - when an exception is thrown - or necessarily evaluates to true
💥
Updated. |
Updated. Samples: aspnet/Entropy#32 |
I need to move IFormCollection from Http to Extensions or WebUtilities. |
/// <param name="enableRewind">Indicates that the entire body should be buffered so it can be rewound afterwards.</param> | ||
/// <param name="cancellationToken"></param> | ||
/// <returns></returns> | ||
public static async Task<IEnumerable<IFileUpload>> ReadFileUploadsAsync([NotNull] this HttpRequest request, bool enableRewind = true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be an IFileCollection : IDictionary<string, IFileUpload[]>
, indexed by the CD name parameter?
Updated. |
var ignored = new ArraySegment<byte>(buffer, offset, count); | ||
if (count == 0) | ||
{ | ||
throw new ArgumentOutOfRangeException("count", "The value must be greater than zero."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nameof
(might want to check other instance in this PR)
Updated. |
The changes in response to my comments look good. |
Also fixes: #84 |
var element = elements.Where(entry => entry.StartsWith("boundary=")).First(); | ||
var boundary = element.Substring("boundary=".Length); | ||
// Remove quotes | ||
if (boundary.Length >=2 && boundary[0] == '"' && boundary[boundary.Length - 1] == '"') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fomatting busted here
|
Redone, with updated samples: aspnet/Entropy#32 |
|
||
namespace Microsoft.AspNet.WebUtilities | ||
{ | ||
public class BufferedReadStream : Stream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Internal?
The
Here |
@ajaybhargavb Fixed. |
|
|
||
if (_bodyStream == null || _bodyStream != body) | ||
// Read the form async and block, but don't block indefinitely, this could be a 2gb file upload. | ||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a really difficult timeout to predict, reproduce, etc in production. How about adding an optional cancellation token argument to ReadForm() but have the default behavior assume a timeout for synchronous-request-body-reading is provided in a different way.
#139
This implementation includes several layers of support for reading multipart request bodies.
Here is a functional sample:
aspnet/Entropy@4e083df
None of this work is prescriptive about how you want to interpret the content. If we want to interpret it as files then we would add one additional layer that would read the content-disposition header to get the file details. However, when we get strongly typed headers that information should be more readily available anyways.