Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where do I catch and handle maxAllowedContentLength exceeded in IIS7?

I have an aspx page where I’m allowing a user to upload a file and I want to cap the max file upload size to be 10MB. IIS7, .NET 3.5. I have the following configured in my web.config file:

<location path="foo.aspx">
    <system.web>
        <!-- maxRequestLength: kbytes, executionTimeout:seconds -->
        <httpRuntime maxRequestLength="10240" executionTimeout="120" />
        <authorization>
            <allow roles="customRole"/>
            <!-- Deny everyone else -->
            <deny users="*"/>
        </authorization>
    </system.web>
    <system.webServer>
        <security>
            <requestFiltering>
                <!-- maxAllowedContentLength: bytes -->
                <requestLimits maxAllowedContentLength="10240000"/>
            </requestFiltering>
        </security>
        <handlers accessPolicy="Read, Script">
            <add name="foo" path="foo.aspx" verb="POST"
               type="System.Web.UI.PageHandlerFactory"
               preCondition="integratedMode" />
        </handlers>       
    </system.webServer>
</location>

I have a custom error handling module that implements IHttpModule. I’ve found that when maxRequestLength is exceeded, HttpApplication.Error does indeed get raised. However when I play with maxAllowedContentLength, the HttpApplication.Error event isn’t being raised and the user gets redirected to a 404.13 page. I've attached with Visual Studio with first chance exceptions turned on nothing is being thrown.

My first thought is to check the header content length in an earlier event – are there recommendations/best practices of where I do this? PostLogRequest? EndRequest?

like image 535
sunflowerpower Avatar asked Feb 19 '09 21:02

sunflowerpower


2 Answers

After looking at the ASP.NET Application Life Cycle Overview for IIS 7.0 and doing my own experimentation, I'm assuming that the request validation is done internally by IIS before any of the events are raised.

It looks like only LogRequest, PostLogRequest, EndRequest, PreSendRequestContent, and PreSendRequestHeaders are raised after the internal validation with this error.

I've decided to attach an event handler to the HttpApplication.EndRequest event in my custom error handler and check for the 404.13 status code on POST and handle as I need it to be handled, which in my case is to redirect to the calling page which will check Server.GetLastError() and display a friendly error to the end user.

private void application_EndRequest(object sender, EventArgs e)
{
    HttpRequest request = HttpContext.Current.Request;
    HttpResponse response = HttpContext.Current.Response;

    if ((request.HttpMethod == "POST") &&
        (response.StatusCode == 404 && response.SubStatusCode == 13))
    {
        // Clear the response header but do not clear errors and
        // transfer back to requesting page to handle error
        response.ClearHeaders();
        HttpContext.Current.Server.Transfer(
            request.AppRelativeCurrentExecutionFilePath);
    }
}

I'd welcome feedback on this approach and alternatives.

like image 178
sunflowerpower Avatar answered Nov 15 '22 00:11

sunflowerpower


The simplest method is to handle it in the OnError method of page itself.

I think this works only in .NET 4.0, since the WebEventCode property is documented as NEW in .NET 4.0.

protected override void OnError(EventArgs e)
{
    Exception err = Server.GetLastError();
    if (err is HttpException)
    {
        if ((err as HttpException).WebEventCode == 3004)
        {
            Context.Items["error"] = "File exceeded maximum allowed length.";
            Server.Transfer( Context.Request.Url.LocalPath );
            return;
        }
    }
    base.OnError(e);
}

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    if (!IsPostBack)
    {
        string error = Context.Items["error"] as string;
        if (!string.IsNullOrEmpty( error ))
            showErrorMessage( error );
    }
}

What I did was:

  • get the last error with Server.GetLastError
  • check that it's a "Maximum request length exceeded." error (WebEventCode == 3004).
  • added a value to Context.Items collections to flag the request as an error
  • transfer the request back to the page itself with Server.Transfer(Context.Request.Url.LocalPath)
  • the page's OnLoad method checks for the error flag and displays a message if present

This ensures the error is handled entirely on the requested page, and the page is capable of reporting errors.

Also note that while the browser will eventually receive a proper response, the browser may take its time uploading the entire request before it handles the server's response and displays it. This behavior is probably defined as part of the server/browser interaction in the HTTP protocol, so probably not much can be done about that.

like image 3
Triynko Avatar answered Nov 15 '22 00:11

Triynko