Thursday 23 June 2011

System.Web.HttpException - Maximum request length exceeded

If you're using ASP.NET WebForms and you want to allow a user to upload files to your web server, then I'm guessing you've used a FileUpload server control. The problem with the whole concept of "uploading files" is that if a user decides they want to be a pain, they could upload gigabytes worth of files which eat up your server's hard drive, finally causing it to crash in a big heap.

Well, Microsoft aren't stupid, they realise this is a pretty big security implication and as such have put safe guards to prevent this. By default, the maximum upload of a file can be 4MB, any bigger and your application will throw the following exception:

System.Web.HttpException: Maximum request length exceeded.

Now that's all fair and good but there's a couple of problems with this which I'll address now.

Note: This is for use with Internet Information Service 6 (IIS 6). In IIS 7, Microsoft have changed how you set the maximum upload size. You can use the rest of this article but, if you're using IIS 7, remember to change the relevant tags within the web.config file as defined in this article.

Firstly, and most obviously, what do you do if you want the user to be able to upload more than 4MB? Well, that's pretty simple, you can override the default! 
Within the web.config file, you can add/find the httpRuntime tag as follows...


<system.web>
        ...
        <httpRuntime
             maxRequestLength="4096" />
        ...
</system.web>


The maxRequestLength is the maximum upload file size, in kilobytes. So, if you wanted to up it to 6MB, you'll enter the value 6144. If you are going to increase the file size, be careful. Do not increase it to any very large numbers, if you do, you'll be leaving your website vulnerable. It'll only take one careless (or malicious) user to upload a few massive files and your web server will come crashing down.

Ok, well, so far so good. We've increased our maximum file upload size to 6MB, but, what happens if a user does, accidentally or unknowingly, try to upload a file greater than 6MB? Well currently, you'll get a 404 error (I know, weird eh?). The HTTP Runtime will throw an exception, this will prevent the server from sending a response. The browser, expecting a response, won't receive one so will assume the page has magically vanished and hence the 404. So, how can we get around this?

There's a few ways, I'm only going to discuss two, one server side solution, one client side. Ideally, they should be used together.

1. You can catch the error and show a custom made error page. To do this, within the Application_Error method in your global.asax, you can have something that looks like this:


protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    if (ex is HttpUnhandledException)
    {
        ex = ex.InnerException;
        if (ex != null && ex.Message.Contains("Maximum request length exceeded."))
        {
            this.Server.ClearError();
            this.Server.Transfer("~/MaxUploadError.aspx");
        }
    }
}

Where MaxUploadError.aspx is an error page you've set up describing the problem. 

Note: This doesn't work for the development server found in Visual Studio so, when testing, you'll still get your 404 error. It will work when you deploy to IIS, or, if you have Visual Studio hooked into IIS.

2. You can use HTML 5! Unfortunately, JavaScript before HTML 5 was unable to interrogate files on the users computer, for obvious security reasons. With HTML 5, you can now query information from a file, given that the user has selected that file for upload. Ok, so this is only going to work with the latest and greatest browsers that support the File API within the HTML 5 specification but, where available, it should be used. It'll save your server having to deal with an extra, possibly time consuming request and it'll give your user an immediate response. They won't be directed to some error page where they'll then have to go back to re-submit a file, they can just change the file there and then and re-submit.

Ok, so to demo this, I'm going to assume you have a FileUpload control defined within your aspx as so:


<asp:FileUpload runat="server" ID="fileUpload" />


Now, within your Page_Load method within your code behind, you can add the following:


protected void Page_Load(object sender, EventArgs e)
{
    HttpRuntimeSection section = ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection;

    string errorMessage = "Sorry, you cannot select this file for upload.\\r\\n\\r\\nMaximum file size " + section.MaxRequestLength + "Kb, your file is ' + (evt.target.files[0].size/1024) + 'Kb.";

    string script = "if(typeof(evt) != 'undefined'){ if(evt.target.files.length == 1 && (evt.target.files[0].size/1024) > " + section.MaxRequestLength + "){ alert('"+ errorMessage + "'); evt.target.value = ''; } }";

    this.fileUpload.Attributes.Add("onchange", script);
}


And that's it, basic HTML 5 support with minimum fuss. Maybe a small, but hopefully effective method of validating the size of your user uploads! We simply check to make sure the File API is available, grab the file size in kilobytes, compare it to the known maximum value and if it's more, show an alert to the user and reset the upload control.

If you want to view the source of all this, then I've set up a simple project that you can download here

Just as a warning, there is a maximum upload size that you will not be able to override but it'll depend on your set up. Essentially, when uploading, IIS will put the file your uploading into memory before writing to the hard disk, this means you can only use the amount of memory that the IIS worker process has available (usually about 1GB). For more information regarding this, have a look at this knowledge base article provided by Microsoft.

Happy Coding!