-
Bug
-
Resolution: Not a bug
-
Major
-
None
-
2.6.1
-
None
-
MOODLE_26_STABLE
There are several issues relating to various PHP files sending, or not sending, content-length headers. Hopefully I found them all and linked them to this one.
When image.php serves an image, it sets the content-length header to the uncompressed size. mod_deflate can reduce the size, but because it doesn't change the value of this header, the client will sit on the connection waiting for more data.
If keepalives are not enabled in Apache, this is not a problem, because Apache will drop the connection and the client will try to decode the data that it has (which is actually complete) and render the response OK.
However if keepalives are enabled, then the connection is blocked until the keepalive time expires (typically 15 seconds in Chrome, much longer with wget). Since the browser limits the number of such open connections, loading the page may stall.
You can reproduce this quite easily against a server with mod_deflate and keepalive: on enabled:
wget http://moodle.inasp.info/theme/image.php/clean/core/1401869915/t/switch_plus -O /dev/null
- gzip disabled, works fine
wget --header 'Accept-Encoding: gzip' http://moodle.inasp.info/theme/image.php/clean/core/1401869915/t/switch_plus -O /dev/null
- gzip enabled, after 15 seconds notices that connection closed early and retries:
-2014-06-06 17:14:32- http://moodle.inasp.info/theme/image.php/clean/core/1401869915/t/switch_plus
Resolving moodle.inasp.info (moodle.inasp.info)... 88.80.187.126
Connecting to moodle.inasp.info (moodle.inasp.info)|88.80.187.126|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1036 (1.0K) [image/svg+xml]
Saving to: `/dev/null'
61% [=======================================================> ] 638 --.-K/s in 14s
2014-06-06 17:14:47 (44.7 B/s) - Connection closed at byte 638. Retrying.
-2014-06-06 17:14:48- (try: 2) http://moodle.inasp.info/theme/image.php/clean/core/1401869915/t/switch_plus
Connecting to moodle.inasp.info (moodle.inasp.info)|88.80.187.126|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1036 (1.0K) [image/svg+xml]
Saving to: `/dev/null'
61% [=======================================================> ] 638 --.-K/s in 14s
2014-06-06 17:15:03 (44.7 B/s) - Connection closed at byte 638. Retrying.
You can also load this same URL in a browser and you'll see that it keeps loading (spinner spinning) for 15 seconds until the server drops the connection.
You can see that Apache sends the same Content-Length with gzip enabled:
0x01b0: 733a 206e 6f6e 650d 0a43 6f6e 7465 6e74 s:.none..Content
0x01c0: 2d4c 656e 6774 683a 2031 3033 360d 0a56 -Length:.1036..V
0x01d0: 6172 793a 2041 6363 6570 742d 456e 636f ary:.Accept-Enco
0x01e0: 6469 6e67 2c55 7365 722d 4167 656e 740d ding,User-Agent.
0x01f0: 0a43 6f6e 7465 6e74 2d45 6e63 6f64 696e .Content-Encodin
0x0200: 673a 2067 7a69 700d 0a4b 6565 702d 416c g:.gzip..Keep-Al
0x0210: 6976 653a 2074 696d 656f 7574 3d31 352c ive:.timeout=15,
0x0220: 206d 6178 3d31 3030 0d0a 436f 6e6e 6563 .max=100..Connec
0x0230: 7469 6f6e 3a20 4b65 6570 2d41 6c69 7665 tion:.Keep-Alive
Or with it disabled:
0x01b0: 733a 206e 6f6e 650d 0a43 6f6e 7465 6e74 s:.none..Content
0x01c0: 2d4c 656e 6774 683a 2031 3033 360d 0a56 -Length:.1036..V
0x01d0: 6172 793a 2041 6363 6570 742d 456e 636f ary:.Accept-Enco
0x01e0: 6469 6e67 2c55 7365 722d 4167 656e 740d ding,User-Agent.
0x01f0: 0a4b 6565 702d 416c 6976 653a 2074 696d .Keep-Alive:.tim
0x0200: 656f 7574 3d31 352c 206d 6178 3d31 3030 eout=15,.max=100
0x0210: 0d0a 436f 6e6e 6563 7469 6f6e 3a20 4b65 ..Connection:.Ke
0x0220: 6570 2d41 6c69 7665 0d0a 436f 6e74 656e ep-Alive..Conten
0x0230: 742d 5479 7065 3a20 696d 6167 652f 7376 t-Type:.image/sv
I'm not sure what the correct fix is. Not sending a content-length breaks keepalive IF mod_deflate is not enabled AND chunked encoding is not used, because the only way for the server to inform the client that the response is finished is to drop the connection, defeating keepalive. But defeating keepalive at least doesn't break the HTTP protocol, while sending less data than the content-length does.
You can see that this is not limited to Moodle, from the first response to this question:
http://help.pagodabox.com/customer/portal/questions/437628-disabling-mod-deflate-for-php-output#increment-437668
"We have noticed that some php frameworks report a content length, and apache doesn’t correct it when it compresses the processed page."
According to comments on that page, it may also be related to using FastCGI to serve PHP (which we do, to enable php-fpm). Also according to this page:
https://bugs.launchpad.net/ubuntu/+source/libapache-mod-fastcgi/+bug/381384
Perhaps the real solution is to link directly to images (not via a PHP script) in all cases where that's possible (to take PHP/CGI out of the loop) and in other cases use the X-SendFile header to tell Apache to serve the static content itself, avoiding any need to set a Content-Lenght header.