Depende de la respuesta, si el transfer-encoding
de la respuesta es , entonces lee hasta que encuentre el "último trozo" (\r\n0\r\n
).
Si el content-encoding
es gzip
, mira el encabezado de respuesta content-length
y lee esa cantidad de datos y luego inflarlo. Si el transfer-encoding
también está configurado para fragmentarse, debe descifrar la respuesta decodificada.
Lo más fácil es construir una máquina de estado simple para leer la respuesta desde el socket mientras aún quedan datos para la respuesta.
Al leer datos fragmentados, debe leer la primera longitud de fragmento (y cualquier extensión fragmentada) y luego leer tantos datos como el tamaño de fragmento, y hacerlo hasta el último fragmento.
Dicho de otra manera:
- leer las cabeceras de respuesta HTTP (leer pequeños fragmentos de datos hasta que se encuentra con
\r\n\r\n
)
- analizar los encabezados de respuesta en una matriz
- Si está fragmentada del
transfer-encoding
, leer y dechunk la información pieza por pieza.
- Si la cabecera
content-length
se establece, se puede leer que muchos datos de la toma de
- Si el
content-encoding
es gzip, descomprimir los datos de lectura
Una vez que haya realizado los pasos anteriores, debe haber leído toda la respuesta y ahora puede enviar otra solicitud HTTP en el mismo socket y repetir el proceso.
Por otro lado, a menos que tenga la necesidad absoluta de una conexión para mantener vivo, simplemente configure Connection: close
en la solicitud y puede leer con seguridad while (!feof($f))
.
No tengo ningún código PHP para leer y analizar las respuestas HTTP en este momento (solo uso cURL) pero si desea ver el código real, hágamelo saber y puedo resolver algo. También podría remitirte a algún código de C# que haya hecho que haga todo lo anterior.
EDITAR: Aquí está el código de trabajo que usa fsockopen
para emitir una solicitud HTTP y demostrar la lectura de las conexiones keep-alive con la posibilidad de codificación fragmentada y compresión gzip. Probado, pero no torturado, ¡utilícelo bajo su propio riesgo!
<?php
/**
* PHP HTTP request demo
* Makes HTTP requests using PHP and fsockopen
* Supports chunked transfer encoding, gzip compression, and keep-alive
*
* @author drew010 <http://stackoverflow.com/questions/11125463/if-connection-is-keep-alive-how-to-read-until-end-of-stream-php/11812536#11812536>
* @date 2012-08-05
* Public domain
*
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
$host = 'www.kernel.org';
$sock = fsockopen($host, 80, $errno, $errstr, 30);
if (!$sock) {
die("Connection failed. $errno: $errstr\n");
}
request($sock, $host, 'GET', '/');
$headers = readResponseHeaders($sock, $resp, $msg);
$body = readResponseBody($sock, $headers);
echo "Response status: $resp - $msg\n\n";
echo '<pre>' . var_export($headers, true) . '</pre>';
echo "\n\n";
echo $body;
// if the connection is keep-alive, you can make another request here
// as demonstrated below
request($sock, $host, 'GET', '/kernel.css');
$headers = readResponseHeaders($sock, $resp, $msg);
$body = readResponseBody($sock, $headers);
echo "Response status: $resp - $msg\n\n";
echo '<pre>' . var_export($headers, true) . '</pre>';
echo "\n\n";
echo $body;
exit;
function request($sock, $host, $method = 'GET', $uri = '/', $params = null)
{
$method = strtoupper($method);
if ($method != 'GET' && $method != 'POST') $method = 'GET';
$request = "$method $uri HTTP/1.1\r\n"
."Host: $host\r\n"
."Connection: keep-alive\r\n"
."Accept-encoding: gzip, deflate\r\n"
."\r\n";
fwrite($sock, $request);
}
function readResponseHeaders($sock, &$response_code, &$response_status)
{
$headers = '';
$read = 0;
while (true) {
$headers .= fread($sock, 1);
$read += 1;
if ($read >= 4 && $headers[$read - 1] == "\n" && substr($headers, -4) == "\r\n\r\n") {
break;
}
}
$headers = parseHeaders($headers, $resp, $msg);
$response_code = $resp;
$response_status = $msg;
return $headers;
}
function readResponseBody($sock, array $headers)
{
$responseIsChunked = (isset($headers['transfer-encoding']) && stripos($headers['transfer-encoding'], 'chunked') !== false);
$contentLength = (isset($headers['content-length'])) ? $headers['content-length'] : -1;
$isGzip = (isset($headers['content-encoding']) && $headers['content-encoding'] == 'gzip') ? true : false;
$close = (isset($headers['connection']) && stripos($headers['connection'], 'close') !== false) ? true : false;
$body = '';
if ($contentLength >= 0) {
$read = 0;
do {
$buf = fread($sock, $contentLength - $read);
$read += strlen($buf);
$body .= $buf;
} while ($read < $contentLength);
} else if ($responseIsChunked) {
$body = readChunked($sock);
} else if ($close) {
while (!feof($sock)) {
$body .= fgets($sock, 1024);
}
}
if ($isGzip) {
$body = gzinflate(substr($body, 10));
}
return $body;
}
function readChunked($sock)
{
$body = '';
while (true) {
$data = '';
do {
$data .= fread($sock, 1);
} while (strpos($data, "\r\n") === false);
if (strpos($data, ' ') !== false) {
list($chunksize, $chunkext) = explode(' ', $data, 2);
} else {
$chunksize = $data;
$chunkext = '';
}
$chunksize = (int)base_convert($chunksize, 16, 10);
if ($chunksize === 0) {
fread($sock, 2); // read trailing "\r\n"
return $body;
} else {
$data = '';
$datalen = 0;
while ($datalen < $chunksize + 2) {
$data .= fread($sock, $chunksize - $datalen + 2);
$datalen = strlen($data);
}
$body .= substr($data, 0, -2); // -2 to remove the "\r\n" before the next chunk
}
} // while (true)
}
function parseHeaders($headers, &$response_code = null, &$response_message = null)
{
$lines = explode("\r\n", $headers);
$return = array();
$response = array_shift($lines);
if (func_num_args() > 1) {
list($proto, $code, $message) = explode(' ', $response, 3);
$response_code = $code;
if (func_num_args() > 2) {
$response_message = $message;
}
}
foreach($lines as $header) {
if (trim($header) == '') continue;
list($name, $value) = explode(':', $header, 2);
$return[strtolower(trim($name))] = trim($value);
}
return $return;
}
Como un lado, te das cuenta de que tienes un error de cotización, ¿no? – Daedalus
@Daedalus Puedo verificar que el error de cotización no es el problema. Puse la recompensa porque tengo un problema similar y ya he perdido demasiado tiempo tratando de arreglarlo :) – rdlowrey
Buena captura, @Daedalus, no entendí eso. Sin embargo, estoy con Rdlowrey, ese tipo de error habría sido diferente. –