print occurred info false errors error during curl_init curl_exec additional php web-services curl http-headers

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:

  1. Especifique que el rizo no debe esperar 100-continue ? (¿Cómo?)
  2. ¿Especifique que el rizo solo debe enviar de vuelta los encabezados de la última respuesta? (¿Cómo?)
  3. 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);