1

Closed

Web API MimeMultipartBodyPartParser throws exception

description

If we sent request body without mime types of file parameters then request throws an exception: "Error parsing MIME multipart body part header byte 271 of data segment System.Byte[]." with a next stack:

" at System.Net.Http.Formatting.Parsers.MimeMultipartBodyPartParser.<ParseBuffer>d__0.MoveNext()
at System.Net.Http.HttpContentMultipartExtensions.MoveNextPart(MultipartAsyncContext context)
at System.Net.Http.HttpContentMultipartExtensions.MultipartWriteSegmentAsyncComplete(IAsyncResult result)
at System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(IMessage msg)
at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()"

and as a result of unhandled exception it crushes w3wp.exe. Web api controller action:

[HttpPost]
    public Task<HttpResponseMessage> Post()
    {
        // Checks content type
        if (Request.Content.IsMimeMultipartContent())
        {
            // For returning non-async stuff, use a TaskCompletionSource to avoid thread switches
            // Save file
            var provider = new MultipartFormDataStreamProvider(_uploadPath);
            var postActionHandler = new PostActionHandler();

            return Request.Content.ReadAsMultipartAsync(provider)
                .ContinueWith(contents =>
                              HttpMultipartForm.HandleMultipartBody(contents.Result, provider),
                              TaskScheduler.FromCurrentSynchronizationContext())
                .ContinueWith(bodyParts =>
                              postActionHandler.Execute(bodyParts.Result),
                              TaskScheduler.FromCurrentSynchronizationContext());
        }

        // Only multipart form-data accepted
        var completionSource = new TaskCompletionSource<HttpResponseMessage>();
        completionSource.SetResult(Request.CreateResponse(HttpStatusCode.BadRequest));

        return completionSource.Task;
    }
How we should handle such type of exception to prevent crashes of the IIS worker process?

P.S. It's important because after the few invalid requests to that action IIS Thread pool stopped with a message "Application pool 'e4a15a24-2234-4f87-b655-d82b1e734234' is being automatically disabled due to a series of failures in the process(es) serving that application pool." and on each bad request it logs a message "A process serving application pool 'e4a15a24-2234-4f87-b655-d82b1e734234' suffered a fatal communication error with the Windows Process Activation Service. The process id was '2100'. The data field contains the error number."

file attachments

Closed Jul 26, 2012 at 5:21 PM by trdai
fixed and verified.

comments

HenrikN wrote Jun 18, 2012 at 2:43 PM

What does the multipart HTTP request look like?

dtretyakov wrote Jun 18, 2012 at 3:03 PM

Request's body has been attached.

HenrikN wrote Jun 18, 2012 at 4:25 PM

The bodypart does indeed contain an invalid Content-Type header which will lead to an exception. What you need to do is to guard your continuations so that any errors are observed. Otherwise the default behavior for Tasks in .NET 4 is to take down the app domain. This behavior is somewhat draconian and has been modified in .NET 4.5 to be less catastrophic.

Something like this should do it:

return Request.Content.ReadAsMultipartAsync(provider).ContinueWith(
readTask =>
{
    if (readTask.IsCanceled)
    {
        return Request.CreateResponse(HttpStatusCode.RequestTimeout);
    }
    else if (readTask.IsFaulted)
    {
        Exception error = readTask.Exception.GetBaseException();
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);
    }
    else
    {
        HttpMultipartForm.HandleMultipartBody(contents.Result, provider);
    }
},
TaskScheduler.FromCurrentSynchronizationContext())
Hope this helps,

Henrik

HenrikN wrote Jun 18, 2012 at 11:30 PM

Just as a follow-up, we do see a problem and are evaluating a fix...

HenrikN wrote Jun 19, 2012 at 1:04 AM