Подтвердить что ты не робот

PHP curl_exec возвращает как HTTP/1.1 100 Continue, так и HTTP/1.1 200 OK, разделенные пробелом

Я вызываю службу из PHP с помощью cURL, например:

$response = curl_exec($ch);

а заголовки запроса/ответа выглядят примерно так:

Запрос:

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

Ответ:

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

за которым следует тело (json закодированные данные).

Проблема состоит в том, что общая задача состоит в том, чтобы разделить заголовки и тело в ответе на первую пустую строку, за исключением того, что в этом случае пустая строка находится после 100 Continue, и поэтому все остальное попадает в тело - и это недействительно json больше: -)

Итак, мой вопрос таков: какой общий способ справиться с этим? У меня есть 3 варианта:

  • Укажите, что завиток не должен ожидать 100-continue? (Как?)
  • Укажите, что завиток должен отправлять только заголовки последнего ответа? (Как?)
  • Вручную проверять заголовки 100 Continue и игнорировать их и следующую пустую строку? (В этом случае, есть ли другие подобные вещи, которые могут произойти, что я должен проверить вручную?)

Если мне не хватает чего-то очевидного, я уверен, что люди наткнулись на это и решили его много раз!

4b9b3361

Ответ 1

Я выберу # 1. Вы можете заставить curl посылать пустой заголовок "Expect", добавив:

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

к вашему коду

Если вы хотите проверить его вручную, вы должны определить свой собственный обратный вызов заголовка и, возможно, написать обратный вызов (посмотрите CURLOPT_HEADERFUNCTION и CURLOPT_WRITEFUNCTION в curl_setopt doc), который просто игнорирует все заголовки "HTTP/1.1 100 Continue".

Ответ 2

Здесь другой метод, который использует описанный в комментарии подход, анализируя ответ в заголовке vs. body с помощью 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==";

Я тестировал это, и это приводит к следующему выводу:

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

Ответ 3

Я столкнулся с этим с 100 и 302 и т.д., это раздражает, но иногда необходимо (gdata calls и т.д.), поэтому я бы сказал, оставлять завиток, возвращая все заголовки и немного вытягивая тело.

Я обрабатываю это так (не могу найти мой фактический код, но вы получите идею):

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

Я жалуюсь на долговязый хакерский метод (предпочел бы, если бы скручивание означало содержание тела каким-то образом), но с годами он работал хорошо. сообщите нам, как вы это делаете.

Ответ 4

У меня была та же проблема, но это решение действительно замечает работу для меня, окончательно я нашел этот метод и все его прекрасное:

мы должны подготовить поля данных перед отправкой:

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

вам нужно подготовить два массива: 1- почтовое поле с нормальными данными: (name1 = val1, name2 = val2,...) 2- почтовое поле с файловыми данными: (name_file 1, path_file1, name_file2 = path_file2,..)

и окончательно вызовите эту функцию перед выполнением завитка как это. $ r = curl_custom_postfields ($ curl, $post, $postfields_files);