PHP curl_exec gibt sowohl HTTP/1.1 100 Continue als auch HTTP/1.1 200 OK getrennt durch Leerzeichen zurück

Lesezeit: 6 Minuten

Ich rufe einen Dienst von PHP mit cURL wie folgt auf:

$response = curl_exec($ch);

und die Request/Response-Header sehen in etwa so aus:

Anfrage:

POST /item/save HTTP/1.1
Host: services.mydomain.com
Accept: */*
Content-Length: 429
Expect: 100-continue
Content-Type: multipart/form-data

Antwort:

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

gefolgt vom Körper (json-codierte Daten).

Das Problem besteht darin, dass Header und Body in der Antwort normalerweise durch die erste gefundene leere Zeile getrennt werden, außer dass in diesem Fall die leere Zeile nach dem steht 100 Continue und daher wird alles andere in den Körper geschoben – und das ist kein gültiger json mehr 🙂

Meine Frage ist also: Wie geht man üblich damit um? Ich habe 3 Optionen in der Reihe:

  1. Geben Sie an, dass curl nicht erwartet werden soll 100-continue? (Wie?)
  2. Festlegen, dass curl nur die Header der letzten Antwort zurücksenden soll? (Wie?)
  3. Suchen Sie manuell nach 100 Continue Kopfzeilen und ignorieren sie und ihre folgende leere Zeile? (Gibt es in diesem Fall andere ähnliche Dinge, die passieren könnten, die ich manuell überprüfen sollte?)

Wenn ich nicht etwas Offensichtliches übersehe, bin ich sicher, dass die Leute darüber gestolpert sind und es viele Male gelöst haben!

  • Stellst du ein curl_setopt($ch, CURLOPT_HEADER, 0) und Curl den Antworttext von den Headern für Sie trennen lassen?

    – Lèse Majesté

    6. Juli 2012 um 9:41 Uhr


  • @Lèsemajesté Nein, ich trenne sie manuell. Wenn ich curl die Header vom Körper trennen lasse, gibt es dann noch eine Möglichkeit, die Header zu inspizieren?

    – jgivoni

    6. Juli 2012 um 10:09 Uhr

  • Sie können die Header erhalten gesendet verwenden CURLINFO_HEADER_OUT; Sie könnten jedoch die empfangenen Header nicht abrufen, abgesehen von bestimmten Headern wie dem letzten Antwortcode, dem Inhaltstyp, der Inhaltslänge und vielleicht einigen anderen. Wenn Sie also alle Antwortheader benötigen, ist die Verwendung wahrscheinlich die beste Option curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD) oder CURLINFO_HEADER_SIZE und trennen Sie den Körper selbst von den Kopfzeilen.

    – Lèse Majesté

    6. Juli 2012 um 11:52 Uhr

  • @Lèsemajesté Der Content-Type wäre schön zu haben. Mal sehen, ob es jemandem gelingt, mit CURLINFO_HEADER_SIZE eine Antwort mit Code zu erstellen.

    – jgivoni

    6. Juli 2012 um 13:00 Uhr

Benutzer-Avatar
ts.

Ich entscheide mich für #1. Sie können curl zwingen, einen leeren „Expect“-Header zu senden, indem Sie Folgendes hinzufügen:

curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));

zu deinem Code

Wenn Sie es manuell überprüfen möchten, sollten Sie Ihren eigenen Header-Callback definieren und möglicherweise einen Callback schreiben (suchen Sie nach CURLOPT_HEADERFUNCTION und CURLOPT_WRITEFUNCTION in curl_setopt doc), der einfach alle “HTTP/1.1 100 Continue”-Header ignorieren muss.

  • Könnte das entfernen Expect Header übrigens irgendwelche anderen Auswirkungen auf den Anfrage/Antwort-HTTP-Dialog?

    – jgivoni

    6. Juli 2012 um 9:45 Uhr

  • @jgivoni Wenn die Möglichkeit besteht, die Anfrage abzulehnen (z. B. per Webservice), besteht ein Risiko. Der Zweck von „100 – Fortfahren“ besteht darin, die POST-Anforderung vorab zu validieren, bevor der gesamte Text gesendet wird (w3.org/Protocols/rfc2616/rfc2616-sec8.html). Wenn kein Risiko des Rückgangs besteht (oder es relativ gering ist), sollten Sie keine Probleme haben.

    – ts.

    6. Juli 2012 um 12:24 Uhr


  • Ich wähle diese Antwort aus, weil sie zu meinen speziellen Bedürfnissen passt. Wenn ich einen Webservice anrufe, über den ich selbst die Kontrolle habe, besteht kein Risiko, die Anfrage abzulehnen.

    – jgivoni

    6. Juli 2012 um 13:46 Uhr

Hier ist eine andere Methode, die den Ansatz verwendet, den ich im Kommentar beschrieben habe, indem die Antwort mithilfe von Header vs. Body analysiert wird 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==";

Ich habe dies getestet und es führt zu folgender Ausgabe:

==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==

Ich hatte das gleiche Problem, aber diese Lösung funktioniert bei mir nicht. Schließlich habe ich diese Methode gefunden und alles ist in Ordnung:

Wir müssen Datenpostfelder vorbereiten, bevor wir sie senden:

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
    ),
));}

Sie müssen zwei Arrays vorbereiten: 1- Post-Feld mit normalen Daten: (name1 = val1, name2 = val2, …) 2- post-Feld mit Dateidaten: (name_file 1, path_file1, name_file2 = path_file2, ..)

und rufen Sie schließlich diese Funktion auf, bevor Sie curl so ausführen. $r = curl_custom_postfields($curl, $post, $postfields_files);

Ich bin bei 100s und 302s usw. darauf gestoßen, es ist ärgerlich, aber manchmal erforderlich (gdata-Aufrufe usw.), also würde ich sagen, dass curl alle Header zurückgibt und der Körper etwas anders extrahiert wird.

Ich handhabe es so (kann meinen eigentlichen Code nicht finden, aber Sie werden die Idee bekommen):

$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);

Ich lehne die langatmige Hack-Methode ab (ich würde es vorziehen, wenn Curl den Körperinhalt irgendwie markieren würde), aber es hat im Laufe der Jahre gut funktioniert. lass uns wissen wie es dir geht.

1055280cookie-checkPHP curl_exec gibt sowohl HTTP/1.1 100 Continue als auch HTTP/1.1 200 OK getrennt durch Leerzeichen zurück

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy