occurred - php curl get http status
PHP curl_exec devuelve tanto HTTP/1.1 100 Continuar como HTTP/1.1 200 Aceptar separados por espacio (4)
Estoy llamando a un servicio de PHP usando cURL, así:
$response = curl_exec($ch);
y los encabezados de solicitud / respuesta se ven algo así:
Solicitud:
POST /item/save HTTP/1.1
Host: services.mydomain.com
Accept: */*
Content-Length: 429
Expect: 100-continue
Content-Type: multipart/form-data
Respuesta:
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Fri, 06 Jul 2012 08:37:01 GMT
Server: Apache
Vary: Accept-Encoding,User-Agent
Content-Length: 256
Content-Type: application/json; charset=utf-8
seguido por el cuerpo (datos codificados json).
El problema es que lo común es dividir los encabezados y el cuerpo en la respuesta por la primera línea vacía encontrada, excepto en este caso, la línea vacía está después de 100 Continue
y, por lo tanto, todo lo demás se introduce en el cuerpo, y eso no es json válido más :-)
Entonces mi pregunta es esta: ¿Cuál es la forma común de lidiar con esto? Tengo 3 opciones alineadas:
- Especifique que el rizo no debe esperar
100-continue
? (¿Cómo?) - ¿Especifique que el rizo solo debe enviar de vuelta los encabezados de la última respuesta? (¿Cómo?)
- Compruebe manualmente si hay
100 Continue
encabezados y descartarlos y su siguiente línea vacía? (En ese caso, ¿hay otras cosas similares que podrían suceder, que debería verificar manualmente?)
¡A menos que me esté perdiendo algo obvio, estoy seguro de que la gente se ha topado con esto y lo ha resuelto muchas veces!
Aquí hay otro método que utiliza el enfoque que describí en el comentario al analizar la respuesta en el encabezado frente al cuerpo utilizando CURLINFO_HEADER_SIZE
:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://test/curl_test.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
// sets multipart/form-data content-type
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
''field1'' => ''foo'',
''field2'' => ''bar''
));
$data = curl_exec($ch);
// if you want the headers sent by CURL
$sentHeaders = curl_getinfo($ch, CURLINFO_HEADER_OUT);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
$header = substr($data, 0, $headerSize);
$body = substr($data, $headerSize);
echo "==Sent Headers==/n$sentHeaders/n==End Sent Headers==/n";
echo "==Response Headers==/n$headers/n==End Response Headers==/n";
echo "==Response Body==/n$body/n==End Body==";
He probado esto y da como resultado el siguiente resultado:
==Sent Headers== POST /curl_test.php HTTP/1.1 Host: test Accept: */* Content-Length: 242 Expect: 100-continue Content-Type: multipart/form-data; boundary=---------------------------- d86ac263ce1b ==End Sent Headers== ==Response Headers== HTTP/1.1 100 Continue HTTP/1.1 200 OK Date: Fri, 06 Jul 2012 14:21:53 GMT Server: Apache/2.4.2 (Win32) PHP/5.4.4 X-Powered-By: PHP/5.4.4 Content-Length: 112 Content-Type: text/plain ==End Response Headers== ==Response Body== **FORM DATA** array(2) { ["field1"]=> string(3) "foo" ["field2"]=> string(3) "bar" } **END FORM DATA** ==End Body==
Me he topado con esto con 100 y 302, etc. Es molesto pero a veces es necesario (llamadas de Gdata, etc.) así que diría que dejar el rizo devolviendo todos los encabezados y extraer el cuerpo de forma un poco diferente.
Lo manejo de esta manera (no puedo encontrar mi código real pero obtendrás la idea):
$response = curl_exec($ch);
$headers = array();
$body = array();
foreach(explode("/n/n", $response) as $frag){
if(preg_match(''/^HTTP//[0-9/.]+ [0-9]+/'', $frag)){
$headers[] = $frag;
}else{
$body[] = $frag;
}
}
echo implode("/n/n", $headers);
echo implode("/n/n", $body);
Rechazo el método hackish de larga duración (preferiría que el rizo marcara el contenido del cuerpo) pero ha funcionado bien a lo largo de los años. Háganos saber cómo le va.
Optaré por el # 1. Puede forzar que el rizo envíe un encabezado "Expect" vacío, agregando:
curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));
a tu código
Si desea verificarlo manualmente, debe definir su propia devolución de llamada de encabezado y tal vez escribir devolución de llamada (busque CURLOPT_HEADERFUNCTION y CURLOPT_WRITEFUNCTION en curl_setopt doc ), que simplemente tiene que ignorar todos los encabezados "HTTP / 1.1 100 Continue".
Tuve el mismo problema pero esta solución no me funciona, al final he encontrado este método y todo está bien:
Tenemos que preparar los campos de publicación de datos antes de enviarlos:
function curl_custom_postfields($curl, array $assoc = array(), array $files = array()) {
/**
* For safe multipart POST request for PHP5.3 ~ PHP 5.4.
* @param resource $ch cURL resource
* @param array $assoc "name => value"
* @param array $files "name => path"
* @return bool
*/
// invalid characters for "name" and "filename"
static $disallow = array("/0", "/"", "/r", "/n");
// build normal parameters
foreach ($assoc as $key => $value) {
$key = str_replace($disallow, "_", $key);
$body[] = implode("/r/n", array(
"Content-Disposition: form-data; name=/"{$key}/"",
"",
filter_var($value),
));
}
// build file parameters
foreach ($files as $key => $value) {
switch (true) {
case false === $value = realpath(filter_var($value)):
case !is_file($value):
case !is_readable($value):
continue; // or return false, throw new InvalidArgumentException
}
$data = file_get_contents($value);
$value = call_user_func("end", explode(DIRECTORY_SEPARATOR, $value));
$key = str_replace($disallow, "_", $key);
$value = str_replace($disallow, "_", $value);
$body[] = implode("/r/n", array(
"Content-Disposition: form-data; name=/"{$key}/"; filename=/"{$value}/"",
"Content-Type: application/octet-stream",
"",
$data,
));
}
// generate safe boundary
do {
$boundary = "---------------------" . md5(mt_rand() . microtime());
} while (preg_grep("/{$boundary}/", $body));
// add boundary for each parameters
array_walk($body, function (&$part) use ($boundary) {
$part = "--{$boundary}/r/n{$part}";
});
// add final boundary
$body[] = "--{$boundary}--";
$body[] = "";
// set options
return @curl_setopt_array($curl, array(
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => implode("/r/n", $body),
CURLOPT_HTTPHEADER => array(
"Expect: 100-continue",
"Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
),
));}
tienes que preparar dos matrices: 1- campo de publicación con datos normales: (nombre1 = val1, nombre2 = val2, ...) 2- campo de publicación con datos de archivo: (nombre_archivo 1, ruta_archivo1, nombre_archivo2 = ruta_archivo2, ..)
y finalmente llamar a esta función antes de ejecutar curl como este. $ r = curl_custom_postfields ($ curl, $ post, $ postfields_files);