Details
Description
To reproduce, try to download a file when running Apache http/2. Not that downloads seem to hang randomly.
Summary
The code at components/download/download.php:241 uses readfile() to stream files through PHP, which is incompatible with Apache's mod_http2 due to its response buffering behavior.
Root Cause Analysis
Looking at the output() method (lines 232-248):
private function output($file, $type) { switch ($type) { case 'data': echo $file; break; case 'file': readfile($file); // <-- This is the problem break; case 'xsendfile': $this->setHeader('X-Sendfile', $file); break; } }
Why this fails with HTTP/2:
- mod_http2 buffers the entire PHP response before transmitting to properly frame HTTP/2 streams
- readfile() sends data through PHP's output buffer, which mod_http2 waits to complete
- Browsers using HTTP/2 see the download stall; wget/curl work because they default to HTTP/1.1
Good News
The code already supports X-Sendfile (xsendfile mode on line 244-245), which completely bypasses this issue by having Apache serve the file directly instead of PHP.
Recommended Fixes
Option 1: Output buffer management (partial fix)
Add before readfile():
while (ob_get_level()) { ob_end_clean(); } if (function_exists('apache_setenv')) { apache_setenv('no-gzip', '1'); } @ini_set('zlib.output_compression', 'Off'); flush();
This helps but may not fully resolve mod_http2 buffering.
Option 2: Promote X-Sendfile usage (recommended)
Document and encourage enabling mod_xsendfile for production environments with HTTP/2. This is the cleanest solution since:
- Apache handles the file transfer directly
- Bypasses PHP output buffering entirely
- More memory-efficient for large files
Option 3: Chunked reading with flushing
Replace readfile($file) with:
$handle = fopen($file, 'rb');
while (!feof($handle)) {
echo fread($handle, 8192);
ob_flush();
flush();
}
fclose($handle);
This is still susceptible to mod_http2 buffering but may help in some configurations.