1

I have to distribute a huge file to some people (pictures of a prom) via my Apache2/PHP server which is giving me some headaches: Chrome and Firefox both show a filesize of 2GB but the file is actually >4GB, so I started to track things down. I am doing the following thing in my php script:

header("Content-Length: ".filesize_large($fn));
header("Actual-File-Size: ".filesize_large($fn)); //Debug
readfile($fn);

filesize_large() is returning the correct filesize for >4gb files as a string (yes, even on 32-bit PHP). Now the interesting part; the actual HTTP header:

Content-Length: 2147483647
Actual-File-Size: 4236525700

So the filesize_large() method is working totally fine, but PHP or Apache somehow limit the value of Content-Length?! Why that?

Apache/2.2.22 x86, PHP 5.3.10 x86, I am using SSL over https

Just so you guys believe me when I say filesize_large() is correct:

function filesize_large($filename)
{
    return trim(shell_exec('stat -c %s '.escapeshellarg($filename)));
}

Edit:

Seems like PHP casts the content length to an integer when communicating with apache2 over the sapi interface on 32-bit systems. No workaround sadly except not including the Content-Size in case of files >2GB

Workaround (and actually a far better solution in the first place): Use mod_xsendfile

Marius
  • 2,234
  • 16
  • 18
  • Can you please post your complete code? especially. the code of `filesize_large()` – Keep Coding Mar 25 '15 at 17:56
  • "even on 32-bit PHP" well there's your issue. It's still going to be a problem. Did you try simply not sending the header? – rjdown Mar 25 '15 at 17:56
  • 1
    When I do not send the header Chrome and Firefox have no information on the filesize and they just start downloading happily. – Marius Mar 25 '15 at 17:57
  • Yep, I think that's your only real solution here, unless you can change to 64-bit or split the file up. – rjdown Mar 25 '15 at 17:59
  • Found this, seems similar http://stackoverflow.com/q/6748471/1301076 – rjdown Mar 25 '15 at 18:00

1 Answers1

1

You have to use 64-bit operation system in order to support long integers for Content-length header. I would recommend to use Vagrant for development.

Header based on strings, but content length based on int. If take a look here https://books.google.com/books?id=HTo_AmTpQPMC&pg=PA130&lpg=PA130&dq=ap_set_content_length%28r,+r-%3Efinfo.size%29;&source=bl&ots=uNqmcTbKYy&sig=-Wth33sukeEiSnUUwVJPtyHSpXU&hl=en&sa=X&ei=GP0SVdSlFM_jsATWvoGwBQ&ved=0CDEQ6AEwAw#v=onepage&q=ap_set_content_length%28r%2C%20r-%3Efinfo.size%29%3B&f=false you will see example of ap_set_content_length(); function which was used to serve content-length response. It accepts file length from system function. Try to call php filesize() and you'll probably see the same result.

If you take a look into ap_set_content_length declaration http://ci.apache.org/projects/httpd/trunk/doxygen/group__APACHE__CORE__PROTO.html#ga7ab393c56cf073ce7aadc3b7ca3db7b2 you will see that length declared as apr_off_t.

And here http://svn.haxx.se/dev/archive-2004-01/0871.shtml you can read, that this type depends from compiler options which is 32bit in your case.

I would recommend you to read source code of Apache and PHP projects.

zoonman
  • 1,116
  • 1
  • 12
  • 30
  • But shouldn't be `header()` be based on strings? – Marius Mar 25 '15 at 18:09
  • header is based on strings, but content length based on `int`, please, see updated answer. – zoonman Mar 25 '15 at 19:22
  • Thank you for digging into the apache source, I really appreciate so much effort. Accepted answer since it answer my inital question ("why is the header modified"), although I now use mod_xsendfile as a solution (see updated question). – Marius Mar 25 '15 at 19:33