I'm using PHP code to retrieve a resource from an HTTP server via PHP HTTP wrapper , like:
file_get_contents("http://...");
While the PHP sends HTTP/1.0 request, the server responds with HTTP/1.1 response with Connection: Keep-Alive
header:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Length: ...
Although the PHP has no way to use the persistent connection, it seems to pointlessly wait for the HTTP connection to close. The server closes it only after 60 seconds of inactivity.
Is there any way to prevent PHP HTTP wrapper from waiting for the server to close the connection, but close it itself after receiving the data announced by Content-Length
header?
Or is it at least safe to use the fopen
and the fread
loop, reading until I get the Content-Length
bytes?
The only workaround I've found is to set the timeout
HTTP context option to reduce the wait time. But obviously I need to set the timeout high enough to be sure it does not interrupt reading of the response, so it's still far from ideal.
Other approaches I've tried without success:
protocol_version
context option), hoping that it will make PHP be aware of persisted connection Connection: close
header (the server ignores it) $context = stream_context_create(array('http' => array('header'=>"Connection: close\r\n")));
file_get_contents("http://...",false,$context);
You say you've done this in which case a better answer may be to manually write/read the response using fsockopen until fgets returns false (don't use feof it'll hang until the connection is closed).
Reference: http://php.net/manual/en/function.fsockopen.php & http://php.net/manual/en/function.fgets.php
EDIT: As stated previously and after further testing I've confirmed this indeed hangs as well so here's a revised solution that works correctly
<?php
$handle = fsockopen("google.com", 80);
if ($handle) {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: google.com\r\n";
$out .= "Connection: Keep-Alive\r\n\r\n";
fwrite($handle, $out);
$bytesread = 0;
while (($buffer = fgets($handle, 4096)) !== false) {
if(strpos($buffer, "Content-Length:") !== false) {
list(, $len) = explode("Content-Length: ", $buffer);
}
if($buffer === "\r\n") {
break;
}
}
$data = '';
while($bytesread != intval($len)) {
$buf = fgets($handle, 1024);
$bytesread += strlen($buf);
$data .= $buf;
}
fclose($handle);
print $data;
}
?>
This is clearly very simple and missing quite a bit of error checking but provides the intended behavior.
As there seems to be no way to make get_file_contents
follow Content-Length
header, I ended up using own implementation:
// parse $http_response_header auto variable to associative array
function get_http_response_headers($http_response_header)
{
$response_headers = array();
if (isset($http_response_header))
{
foreach ($http_response_header as $header)
{
$i = strpos($header, ":");
if ($i !== false)
{
$name = trim(substr($header, 0, $i));
$value = trim(substr($header, $i+1));
}
else
{
$name = trim($header);
$value = NULL;
}
$name = strtolower($name);
$response_headers[$name] = $value;
}
}
else
{
$response_headers = array();
}
return $response_headers;
}
// Replacement for file_get_contents that follows Content-Length header
function http_get_contents($url, $context)
{
$h = fopen($url, "r", false, $context);
$result = ($h !== false);
if ($result)
{
$response_headers = get_http_response_headers($http_response_header);
$len = null;
// If it is not persistent connection, just read to the end,
// as file_get_contents does
if (isset($response_headers["connection"]) &&
(strcasecmp($response_headers["connection"], "close") == 0))
{
$len = false;
}
// If it is not persistent connection, follow Content-Length
else if (isset($response_headers["content-length"]))
{
$len = intval($response_headers["content-length"]);
}
else
{
trigger_error("No Content-Length or Connection:close header");
$result = false;
}
if ($result)
{
$result = null;
$total = 0;
while (true)
{
$toread = ($len === false) ? 16384 : ($len - $total);
$buf = fread($h, $toread);
if ($buf === false)
{
trigger_error("Reading HTTP failed");
$result = false;
break;
}
else
{
$read = strlen($buf);
$total += $read;
$result .= $buf;
if ($len !== false)
{
if ($read > $len)
{
trigger_error("Read too much data $read > $len");
break;
}
else if ($read == $len)
{
// done
break;
}
}
else
{
if ($read == 0)
{
// done
break;
}
}
}
}
}
fclose($h);
}
return $result;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.