PHP curl_exec返回HTTP / 1.1 100 Continue和HTTP / 1.1 200 OK,以空格分隔

时间:2012-07-06 09:14:28

标签: php web-services curl http-headers

我正在使用cURL从PHP调用服务,如下所示:

$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个选项:

  1. 指定curl不应该期望100-continue? (如何?)
  2. 指定curl应该只发回最后一个响应的标题? (如何?)
  3. 手动检查100 Continue标题并忽略它们及其后面的空行? (在这种情况下,还有其他可能发生的类似事情,我应该手动检查吗?)
  4. 除非我遗漏了一些明显的东西,否则我相信人们偶然发现了这个问题并多次解决了!

4 个答案:

答案 0 :(得分:19)

我会选择#1。 您可以通过添加:

强制curl发送空的“Expect”标题
curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));

代码

如果你想手动检查它,你应该定义你自己的头回调,也可以写回调(在curl_setopt doc中查找CURLOPT_HEADERFUNCTION和CURLOPT_WRITEFUNCTION),它只是忽略所有“HTTP / 1.1 100 Continue”标题。

答案 1 :(得分:5)

这是另一种方法,它使用我在评论中描述的方法,通过使用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==

答案 2 :(得分:0)

我遇到了这个100s和302s等它很烦人但有时需要(gdata调用等)所以我会说让curl返回所有标题并提取身体的方式略有不同。

我这样处理(无法找到我的实际代码,但你会明白这一点):

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

我不喜欢那种漫长的hackish方法(如果卷曲以某种方式标记身体内容,我会更喜欢它)但多年来它一直运作良好。让我们知道你是怎么过的。

答案 3 :(得分:0)

我有同样的问题,但这个解决方案确实为我工作,最终我找到了这个方法,并且很好:

我们必须在发送之前准备数据发布字段:

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- post字段:( name1 = val1,name2 = val2,...) 带文件数据的2- post字段:(name_file 1,path_file1,name_file2 = path_file2,..)

并在执行curl之前最终调用此函数。 $ r = curl_custom_postfields($ curl,$ post,$ postfields_files);