Bug 57198 - mod_proxy_fcgi (more) wrong behavior with 304
Summary: mod_proxy_fcgi (more) wrong behavior with 304
Status: REOPENED
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: mod_proxy_fcgi (show other bugs)
Version: 2.4.10
Hardware: PC Linux
: P2 major (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords: FixedInTrunk
Depends on:
Blocks:
 
Reported: 2014-11-10 19:44 UTC by Luke G.
Modified: 2019-11-22 07:38 UTC (History)
3 users (show)



Attachments
proposed patch (2.19 KB, patch)
2014-11-12 07:13 UTC, jkaluza
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Luke G. 2014-11-10 19:44:28 UTC
The patch r1331416 introduced a serious bug: 

In the case described in bug 52879 (a PHP script run as fastcgi through php-fpm and mod_proxy_fcgi which returns a Last-Modified header with no Status header), httpd (after fix r1331416) does indeed now return a 304 status (if a matching If-not-modified header is in the request) BUT the content of the php script is also sent. 

This breaks the http protocol as described in rfc2616 section 10.3.5: "The 304 response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields."

This invalid behavior cause a serious problem when processed by a reverse proxy placed in front of the backends. When the proxy receives this invalid 304 response (containing a body), it immediately sends a 304 to the client and ignores the rest of the packet; but the next packets sent by the backend stay in the tcp stack (the proxy does not expect further content). The reverse proxy prepends these packets in the response to the next request routed to the same backend. This, of course, seriouly breaks our applications...
Comment 1 jkaluza 2014-11-11 09:47:33 UTC
From the first quick try, I'm not able to reproduce the issue.

My PHP script looks like this:

<?php header('Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT');?>
test

At first I've tried request with If-Modified-Since before the date in PHP script. This returns 200 OK with body as expected:

# curl --header 'If-Modified-Since: Tue, 14 Nov 1995 04:58:08 GMT' http://localhost/index.php -v
> GET /index.php HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost
> Accept: */*
> If-Modified-Since: Tue, 14 Nov 1995 04:58:08 GMT
> 
< HTTP/1.1 200 OK
< Date: Tue, 11 Nov 2014 09:41:25 GMT
< Server: Apache/2.4.10 (Fedora) Phusion_Passenger/4.0.53
< X-Powered-By: PHP/5.5.18
< Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
< 
test

Then I've tried with different If-Modified-Since to trigger to 304. This returns 304, but I don't see the php script content there:

# curl --header 'If-Modified-Since: Wed, 15 Nov 1995 04:58:08 GMT' http://localhost/index.php -v
> GET /index.php HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost
> Accept: */*
> If-Modified-Since: Wed, 15 Nov 1995 04:58:08 GMT
> 
< HTTP/1.1 304 Not Modified
< Date: Tue, 11 Nov 2014 09:41:17 GMT
< Server: Apache/2.4.10 (Fedora) Phusion_Passenger/4.0.53
< 

Are you doing anything differently? How do you reproduce it?

Note that from the code, I think this should work properly, because the output brigade is cleaned-up before it's passed to output filters.
Comment 2 Luke G. 2014-11-11 16:10:51 UTC
curl seems to be following the rfc2616 correctly, and immediately close connection after receiving the 304 header, so the bug won't show with curl.

I can see the problem when using telnet to connect to the server and sending the request manually.

Also, after some testing, it seems the bug is triggered only when the php script generate some large enough content.

Here is how to reproduce:

-> index.php:

<?php header('Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT');
for( $i=0; $i<60; $i++) {
   echo( "testing testing testing testing testing testing testing testing testing testing testing\n" );
}
flush();
?>

-> send request manually via telnet:

telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /index.php HTTP/1.1
Host: localhost
Accept: */*
If-Modified-Since: Tue, 15 Nov 1995 05:58:08 GMT


HTTP/1.1 304 Not Modified
Date: Tue, 11 Nov 2014 15:54:07 GMT
Server: Apache/2.4.10 (Unix)

testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
Connection closed by foreign host.

-> Note that telnet close the connection after about 5 seconds of waiting


And this is what I get when the date triggers to send content:

telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /index.php HTTP/1.1
Host: localhost
Accept: */*
If-Modified-Since: Tue, 14 Nov 1995 05:58:08 GMT


HTTP/1.1 200 OK
Date: Tue, 11 Nov 2014 16:04:03 GMT
Server: Apache/2.4.10 (Unix)
X-Powered-By: PHP/5.5.18
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Transfer-Encoding: chunked
Content-Type: text/html

1028
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing

478
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing
testing testing testing testing testing testing testing testing testing testing testing

0

Connection closed by foreign host.
Comment 3 Luke G. 2014-11-11 19:32:27 UTC
After some more testing, it appears that when a 304 message is returned, httpd skips the rest of the first chunk (as seen when a 200 message is returned) but then sends all the following chunks. 

Maybe this will help find the problem...
Comment 4 jkaluza 2014-11-12 07:13:44 UTC
Created attachment 32204 [details]
proposed patch

Stop handling body sent by fcgi backend after httpd sent 304 to the client.
Comment 5 Luke G. 2014-11-13 21:39:32 UTC
Thanks, proposed patch 32204 solved the problem!
Comment 6 jkaluza 2014-11-19 07:23:14 UTC
Committed to trunk in r1640495.
Comment 7 jkaluza 2014-11-25 12:36:31 UTC
Proposed in 2.4.x.
Comment 8 Christophe JAILLET 2015-01-09 21:33:49 UTC
Backported in 2.4.x in r1650677

Will be part of 2.4.11
Comment 9 Valentin Gutierrez 2019-11-21 10:25:40 UTC
This isn't completely solved by patch 32204.
This small PHP PoC script triggers the same issue (tested with version 2.4.25):
<?php
http_response_code(304);
echo("test");
flush();
Comment 10 Valentin Gutierrez 2019-11-22 07:38:46 UTC
This seems to be solved by http://svn.apache.org/viewvc?view=revision&revision=1837056. I'm not able to reproduce with Apache >=2.4.35