Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-45906

image.php sends content-length when it shouldn't, loading stalls with mod_deflate

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not a bug
    • Icon: Major Major
    • None
    • 2.6.1
    • Caching, Files API, Installation
    • 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

      1. 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

      1. 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.

            Unassigned Unassigned
            gcc Chris Wilson (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved:

                Error rendering 'clockify-timesheets-time-tracking-reports:timer-sidebar'. Please contact your Jira administrators.