8

I have a PHP script that serves portions of a PDF file by byte ranges.

If an HTTP HEAD request is received, it should send back headers (including the PDF file size) but not the actual file contents. I have tried this:

header('HTTP/1.1 200 OK');
header('Content-Type: application/pdf');
header('Accept-Ranges: bytes');
header('Content-Length: '.filesize($Pathname));
die;

The problem is that something (I assume the web server == LiteSpeed) replaces the Content-Length header with Content-Length: 0 - which defeats the whole purpose.

Can anyone suggest what I should be doing? Thanks

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
oomp
  • 99
  • 1
  • 5
  • Have you checked if the `$Pathname` is correct? – Smort Jan 04 '17 at 11:16
  • How do you know it is replaced? – Bart Friederichs Jan 04 '17 at 11:21
  • @Paul yes the $Pathname variable is definitely correct. I've also written to a log file that confirms that filesize($Pathname) is the actual file size. – oomp Jan 04 '17 at 11:23
  • @BartFriederichs - by viewing network activity in Firefox (F12). – oomp Jan 04 '17 at 11:25
  • try to add header('Dummy-Length: ' . filesize($Pathname)); and header('Dummy-Exists: ' . file_exists($Pathname)); – Iurii Drozdov Jan 04 '17 at 11:26
  • Tried that @IuriiDrozdov - Firefox toolbox reports `Dummy-Length: 1545019` and `Dummy-Exists: 1` (but also `Content-Length: 0`) – oomp Jan 04 '17 at 11:49
  • please, specify in your question which webserver are you using, and also the head request made – leoap Jan 04 '17 at 11:54
  • @leo_ap The server is LiteSpeed. There is no actual HEAD request sent; for testing, I've coded the PHP to assume it's a HEAD request if a byte-range is not requested. – oomp Jan 04 '17 at 12:10
  • I have no idea of LiteSpeed but `Content-Length` tends to be mangled when you have some sort of transparent compression. You should also test a real HEAD request because otherwise you're trying to send incorrect data and the unexpected can happen. – Álvaro González Jan 04 '17 at 12:16

3 Answers3

6

From w3c Hypertext Transfer Protocol -- HTTP/1.1:

When a Content-Length is given in a message where a message-body is allowed, its field value MUST exactly match the number of OCTETs in the message-body. HTTP/1.1 user agents MUST notify the user when an invalid length is received and detected.

And:

The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of the entity-body that would have been sent had the request been a GET.

So, I suppose, your code will properly work if you send real HEAD request to your server.

Iurii Drozdov
  • 1,685
  • 1
  • 12
  • 23
  • I can't see that a real HEAD request would make any difference, since the PHP response is independent of the inital request isn't it? – oomp Jan 04 '17 at 12:34
  • 2
    "I can't see that a real HEAD request would make any difference" - I can. That firmly places the blame at the door of the webserver rather than on PHP or your code (assuming you get the same behaviour in response to a HEAD request). – symcbean Jan 04 '17 at 12:42
  • @oomp just give it a try. You can use curl -i -D -I -X HEAD http:// yourserver.com (without space between http:// and domain name) or Postman – Iurii Drozdov Jan 04 '17 at 13:05
  • 1
    Thanks - and to @symcbean - you are right. Who would have believed that in order to respond to a HEAD request, you need a HEAD request? I feel a bit silly now. – oomp Jan 04 '17 at 22:47
2

It's the webserver job, not yours.

In my case I left everything to the Apache webserver and nothing changed in my php code except of how the requests is being parsed

For example things like

if($_SERVER['REQUEST_METHOD'] === "GET"){
     //ok
}else{
     //send 400 Bad Request
}

are changed to

if($_SERVER['REQUEST_METHOD'] === "GET" || $_SERVER['REQUEST_METHOD'] === "HEAD"){
     //ok
}else{
     //send 400 Bad Request
}

and Apache did all the heavy lifting (striped the response body).

(don't try to ob_clean() or die("") or things like this).

related resources:

http://hc.apache.org/httpclient-3.x/methods/head.html

https://security.stackexchange.com/questions/62811/should-i-disable-http-head-requests

Apache 2.2.2 response on HEAD requests

Accountant م
  • 6,975
  • 3
  • 41
  • 61
0

As Lurii mentioned, the content length is affected by your request type.

With GET requests, a non-matching content length may result in a hanging client, so LiteSpeed will verify the content length before sending the header to the client.

Using a HEAD request should return the content length as expected.