Bug 17722

Summary: 413 errors not returned to browser - also fails to server errordocument if present
Product: Apache httpd-2 Reporter: Gareth Webbley <garethwebbley>
Component: CoreAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: RESOLVED DUPLICATE    
Severity: normal CC: tom
Priority: P3    
Version: 2.0.48   
Target Milestone: ---   
Hardware: PC   
OS: All   

Description Gareth Webbley 2003-03-06 14:16:51 UTC
If a 413 error is encountered no response is sent to the client
If an ErrorDocument directive for 413 is present in httpd.conf the request 
either blocks if the ErrorDocument is a URL or closes the connection if the 
errordocument is text.

I have a LimitRequestBody of 10000240.  I have created a simple file upload 
page to reproduce the problem :-

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Error</title>
</head>
<body marginheight="0" marginwidth="0" rightmargin="0" topmargin="0" 
leftmargin="0" bottommargin="0">
<form id="attachForm" name="attachForm" enctype="multipart/form-data" 
method="post" action="/html/test413.html">
	<BR><input class="browseAttach" name="file" size="25" type="file">
	<P><INPUT TYPE="submit" VALUE="Upload file" METHOD="post">
</form>
</body>
</html>

Selecting a file > 10000240 causes the problem to occur.

I have used a http sniffer to check what's being sent and when a 413 error 
occurs no response is returned from apache.

I have tried this with a 1.3 version of Apache and all works as expected.
Comment 1 Gareth Webbley 2003-03-08 16:49:48 UTC
After further testing this appears to only be a problem with IE.  All works as 
expected with Opera and Mozilla.
Comment 2 Tom Evans 2004-01-25 02:15:47 UTC
I have also encountered this bug, and in my testing it has not been attributable
to a specific browser - it has affected any and every browser I have tested. I
believe this is a similar bug to 21544 -
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=21544 - and I disagree with
the conclusion that was drawn there. I am testing over a fast network link ot
the server, and transmitting the file takes very little time - less than one
second. 

Occasionally the 413 responce IS thrown up, especially if LimitRequestBody is
small (I tested at 100kb, and the 413 error was returned for files upto 200kb),
but testing with the limit set to 1048576 bytes and uploading a 1048577 byte
file causes a null responce.

Software used:
Apache 2.0.48
Linux 2.4.20
Internet Explorer 6 SP 1
Mozilla Firebird 0.6

Expected Responce:
Server replies with 413 error message

Actual Responce:
The server closes the connection

Code to replicate:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Error</title>
</head>
<body marginheight="0" marginwidth="0" rightmargin="0" topmargin="0" 
leftmargin="0" bottommargin="0">
<form id="attachForm" name="attachForm" enctype="multipart/form-data" 
method="post" action="/html/test413.html">
	<BR><input class="browseAttach" name="file" size="25" type="file">
	<P><INPUT TYPE="submit" VALUE="Upload file" METHOD="post">
</form>
</body>
</html>

in httpd.conf:
LimitRequestBody 1048576 

Choose a file > 1048576 bytes
Comment 3 André Malo 2004-01-25 02:56:01 UTC
Sounds like a server crash. What program receives the upload? Is there anything
related in the errorlog?
Comment 4 Tom Evans 2004-01-25 22:01:05 UTC
Only what is to be expected :

[Sun Jan 25 22:05:40 2004] [error] [client 192.168.0.50] Requested
content-length of 1049317 is larger than the configured limit of 1048576,
referer: http://site/news/tst-upload.html

The error gets there, but no responce/status code is returned to the browser.
Comment 5 Jeff Trawick 2004-03-21 19:20:39 UTC
FWIW, I was doing some testing recently with an Apache 2-based server and a
plug-in which can return 413, trying to see how to get IE to display a custom
message via ErrorDocument.

My network traces showed the server always seemed to return the right data
(either 302 or 413 response based on type of ErrorDocument) but IE would not
display it.  But then I tried a large error document (8K or so IIRC) and then it
worked.

This is a slightly different flow than when Apache generates the 413.
Comment 6 Joe Orton 2004-11-02 22:41:43 UTC
I saw a similar bug to this in 2.0 in the C-L parsing error path in
ap_http_filter; it failed to set ctx->remaining = 0, and then some later filter
aborted and just closed the connection.  Perhaps the same thing is correct for
the ctx->remaining > ctx->limit case.
Comment 7 Edam 2005-07-26 14:50:34 UTC
I vote for this bug to be fixed too! It's really annoying!

I too get this in the error log:

[Tue Jul 26 12:47:35 2005] [error] [client x.x.x.x] Request content-length of
28495103 is larger than the configured limit of 5242880

And a strace of httpd -X shows the following (with comments):

open("/home/xxx/public_html/.htaccess", O_RDONLY|O_LARGEFILE) = 5
fstat64(5, {st_mode=S_IFREG|0660, st_size=818, ...}) = 0
fstat64(5, {st_mode=S_IFREG|0660, st_size=818, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x40015000
read(5, "# Error documents\r\nErrorDocument"..., 4096) = 818
read(5, "", 4096)                       = 0
close(5)                                = 0
munmap(0x40015000, 4096)                = 0
lstat64("/home/xxx/public_html/cgi-bin", {st_mode=S_IFDIR|0755, st_size=136,
...}) = 0
open("/home/xxx/public_html/cgi-bin/.htaccess", O_RDONLY|O_LARGEFILE) = -1
ENOENT (No such file or directory)
lstat64("/home/xxx/public_html/cgi-bin/foo.cgi", {st_mode=S_IFREG|0755,
st_size=4928, ...}) = 0
open("/home/xxx/public_html/cgi-bin/foo.cgi", O_RDONLY|O_LARGEFILE) = 5
fcntl64(5, F_DUPFD, 15)                 = 20
close(5)                                = 0
read(20, "#!/usr/bin/perl -w\n\n# This is a t"..., 4096) = 4096
close(20)                               = 0
time(NULL)                              = 1122378455
write(15, "[Tue Jul 26 12:47:35 2005] [erro"..., 139) = 139

^^^ This corresponds to the error_log entry

alarm(300)                              = 0
alarm(0)                                = 300
alarm(300)                              = 0
alarm(0)                                = 300
alarm(300)                              = 0
alarm(0)                                = 300
write(4, "HTTP/1.1 413 Request Entity Too "..., 685) = 685

^^^ This *looks* like it's trying to return something to the browser! And the
handle (4) is the same one that is all requests and responses are read
from/written to throughout the strace dump.

time(NULL)                              = 1122378455
write(17, "host-x-x-x-x.xxxxx.com"..., 263) = 263

^^^ This corresponds to the access_log entry

alarm(30)                               = 0
shutdown(4, 1 /* send */)               = 0
select(5, [4], NULL, NULL, {2, 0})      = 1 (in [4], left {2, 0})
read(4, "\231Hy\230\2710\217\253\20r\201t\3443)\227<\24XC,\2168"..., 512) = 512
select(5, [4], NULL, NULL, {2, 0})      = 1 (in [4], left {2, 0})
read(4, "\37\207\37qm\373\7\201^\254\215\272\273o\340]~\f\237>|"..., 512) = 512

^^^ These last 4 lines are then repeated, and repeated (although the actual data
changes). The handle is again 4, and the data corresponds to a file uploaded in
the request, so it looks like httpd is accepting the request!!!

I'm not that knowledgable at programming under *nix (yet), so I thought I'd post
this in case it helps anyone.
Comment 8 Edam 2005-07-26 23:41:38 UTC
After a day of reading source-code and running Ethereal (which I wish I'd done
first!), I think I understand what is going on. I dont think this is actually a
bug in apache at all...


This is what happens:

* The browser sends the request. In my case it is a multi-part POST which
includes a file too big for the limit set by LimitRequestBody.

* The server responds with the expected 413 error page. It then calls
lingering_close() in main/http_main.c which partially closes the socket,
stopping the server sending any more outgoing data, and also causing a TCP FIN
to be sent to the browser. The FIN packet indicates that the server is done
sending and wishes to close the connection. The server then continues to accept
incoming data from the browser in order to try and avoid a situation that
happens later anyway (read on).

* Instead of displaying the returned 413 error page, the browser ignores both
the 413 error page and the FIN packet and continues to send it's request! The
server continues to soak it up (waiting for the connection to close).

* After a designated timeout period (30 seconds), the server decides to give up
waiting for the browser to stop spewing rubbish, and closes the connection. When
the TCP mechanism at the server-end then recieves further incoming packets from
the browser, it responds with an RST packet. This is precisely the situation
that lingering_close() tries to prevent, because the browser then believes the
connection to have been unexpectedly reset. This causes the browser to discard
any previous response from the server (which is why no one ever sees the 413
error page!) and display the familliar "Net Reset Error".


I dont know the ins and outs of the HTTP protocol, and frankly I want to go to
bed, not wade through RFCs! But it seems to me that this is an issue with the
browser isn't it? Surely if a response has been sent to the browser, and that
response was an error, and it was followed by a TCP FIN packet, isn't it
reasonable to assume that the rest of the request can be discarded? Surely this
would make more sense, especially in the case where the request is large and the
error is the "413 Request Entity Too Large" error!

I've tried a couple of browsers. The following exhibited the above behavior:
	MSIE 6
	Firefox 1.0.6

However the following worked ok:
	Opera 7.53

It looks like Opera waits for a "100 Continue" response from the server before
spewing the large upload... I'll post a bug report on FireFox's Bugzilla too.
Screw IE though.  :o)

I'd very much like to hear what any of the apache team think about all this! As
I said before, I'm still learning about a lot of this stuff...


Before I go, here's an (edited) Ethereal capture of the above bahavior:

FRAME TIME        SOURCE IP            DESTINATION IP
    1 0.000000    192.168.0.1          web.se.rv.er
        TCP      2076 > http [SYN] Seq=0 Ack=0 Win=64240 Len=0 MSS=1460
    2 0.046966    web.se.rv.er         192.168.0.1  
        TCP      http > 2076 [SYN, ACK] Seq=0 Ack=1 Win=5840 Len=0 MSS=1460
    3 0.047054    192.168.0.1          web.se.rv.er
        TCP      2076 > http [ACK] Seq=1 Ack=1 Win=64240 Len=0
    4 0.050230    192.168.0.1          web.se.rv.er
        HTTP     POST /foo.html HTTP/1.1

^^^ server sends request...

    5 0.050940    192.168.0.1          web.se.rv.er
        HTTP     Continuation

^^^ ...and then immediately starts sending the file. From the packet:
    Content-Type: multipart/form-data; boundary=------[snip]\r\n
    Content-Length: 28495103\r\n
    \r\n
    Data (1345 bytes)

    6 0.114983    web.se.rv.er         192.168.0.1  
        TCP      http > 2076 [ACK] Seq=1 Ack=671 Win=6700 Len=0
    7 0.115087    192.168.0.1          web.se.rv.er
        HTTP     Continuation
    8 0.115093    192.168.0.1          web.se.rv.er
        HTTP     Continuation
    9 0.152971    web.se.rv.er         192.168.0.1  
        TCP      http > 2076 [ACK] Seq=1 Ack=2131 Win=8760 Len=0
   10 0.153055    192.168.0.1          web.se.rv.er
        HTTP     Continuation
   11 0.153063    192.168.0.1          web.se.rv.er
        HTTP     Continuation

^^^ various server acknowledgments, and the browser sendings...

   12 0.193151    web.se.rv.er         192.168.0.1   
       HTTP     HTTP/1.1 413 Request Entity Too Large

^^^ server respondes with 413 error (packet includes the HTML error page)...

   13 0.193968    web.se.rv.er         192.168.0.1  
        TCP      http > 2076 [FIN, ACK] Seq=676 Ack=2131 Win=8760 Len=0

^^^ ...followed by a FIN packet

   14 0.194024    192.168.0.1          web.se.rv.er
        HTTP     Continuation
   15 0.231948    web.se.rv.er         192.168.0.1  
        TCP      http > 2076 [ACK] Seq=677 Ack=5051 Win=14600 Len=0
   16 0.232125    192.168.0.1          web.se.rv.er
        HTTP     Continuation

^^^ browser continues to send, server continues to acknowledge.

      . . . this continues for 30 seconds until . . .

  888 31.572576   web.se.rv.er         192.168.0.1  
        TCP      [TCP ZeroWindow] http > 3510 [RST] Seq=706
  889 31.785558   web.se.rv.er         192.168.0.1  
        TCP      [TCP ZeroWindow] [TCP Dup ACK 888#1] http > 3510 [RST] Seq=706
  890 31.843627   web.se.rv.er         192.168.0.1  
        TCP      [TCP ZeroWindow] [TCP Dup ACK 888#2] http > 3510 [RST] Seq=706
  891 31.883537   web.se.rv.er         192.168.0.1  
        TCP      [TCP ZeroWindow] [TCP Dup ACK 888#3] http > 3510 [RST] Seq=706

^^^ server responds with RST packets. Browser stops sending and displays error.
Comment 9 Jeff Trawick 2005-07-27 02:09:04 UTC
Right.  In this scenario, Apache can't keep reading unlimited data from the
browser, yet doing so is the only way to get the browser to read the error response.

If the browser were to send Expect: 100-Continue and Content-Length with the
request, Apache can reject it before the browser starts sending the data. 
Apache either responds with an error such as 413, or Apache sends 100-Continue
to tell the browser to start sending data.  IE doesn't implement this.  (no
knowledge here of which browsers do)
Comment 10 Joe Orton 2005-09-25 22:23:56 UTC
The lingering close bug is a genuine cause of this problem, insofar as this
issue is fixable; so marking this as a duplicate of that bug.

*** This bug has been marked as a duplicate of 35292 ***