15

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?

sharptooth
  • 167,383
  • 100
  • 513
  • 979
sunflowerpower
  • 1,305
  • 2
  • 10
  • 14

2 Answers2

15

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.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
sunflowerpower
  • 1,305
  • 2
  • 10
  • 14
  • Please not that sunflowerpower's answer only works if the Managed Pipeline Mode is set to Integrated. At least that's why I experienced – Marvin Oct 27 '10 at 12:34
  • I'm noticing that this solution doesn't handle Authentication properly. When I call Server.Transfer, the target page's authentication-checking routines fail because the HttpContext.Current.User property is NULL. It doesn't seem to be caused by clearing the headers, as taking that line out still results in the problem. Is there any way around this? It's not a solution otherwise. – Triynko Apr 14 '11 at 22:00
  • I put @Triynko 's error code in your event handler. I'm using Umbraco - I'm not sure if that is significant but this is the only place I've been able to catch 'Maximum request length exceeded' exceptions as yet. I also had to call 'Response.ClearContent();' to remove the YSOD. 'Application_Error' doesn't fire for this exception (although it does for RequestValidation) and I couldn't get 'OnError' to fire in a UserControl or a Masterpage. Couldn't find an actual page. ;) – Ian Warburton Jan 23 '13 at 14:09
  • In which event of the page lifecycle should we check the `Server.GetLastError()` ? I always have a `null` return. I have tried event such as `PreRender` with no luck. For now, i am using a `response.Cookies["maxSizeError"].Value = "ERROR";` and check the cookie on the page. – Matthieu Charbonnier Oct 31 '14 at 12:21
3

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.

Philipp M
  • 1,877
  • 7
  • 27
  • 38
Triynko
  • 18,766
  • 21
  • 107
  • 173