12

注:解决方案在最后

如果我尝试执行超过 1024 个字符的 HTTP POST,它会失败。为什么?这是一个最小的例子:

收件人.php:

<?php
if (strlen(file_get_contents('php://input')) > 1000
    || strlen($HTTP_RAW_POST_DATA) > 1000) {
 echo "This was a triumph.";
}
?>

发件人.php:

<?php
function try_to_post($char_count) {
 $url = 'http://gpx3quaa.joyent.us/test/recipient.php';
 $post_data = str_repeat('x', $char_count);
 $c = curl_init();
 curl_setopt_array($c,
                    array(  CURLOPT_URL => $url,
                            CURLOPT_HEADER => false,
                            CURLOPT_CONNECTTIMEOUT => 999,
                            CURLOPT_RETURNTRANSFER => true,
                            CURLOPT_POST => 1,
                            CURLOPT_POSTFIELDS => $post_data
                    )
 );
 $result = curl_exec($c);
 echo "{$result}\n";
 curl_close($c);
}

for ($i=1020;$i<1030;$i++) {
 echo "Trying {$i} - ";
 try_to_post($i);
}
?>

输出:

Trying 1020 - This was a triumph.
Trying 1021 - This was a triumph.
Trying 1022 - This was a triumph.
Trying 1023 - This was a triumph.
Trying 1024 - This was a triumph.
Trying 1025 - 
Trying 1026 - 
Trying 1027 - 
Trying 1028 - 
Trying 1029 - 

配置:

PHP Version 5.2.6
libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3 libidn/1.8
lighttpd-1.4.19

解决方案

为 cURL 添加以下选项:

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

原因似乎是任何超过 1024 个字符的 POST 都会导致发送“Expect: 100-continue”HTTP 标头,而 Lighttpd 1.4.* 不支持它。我找到了一张票:http ://redmine.lighttpd.net/issues/show/1017

他们说它适用于1.5。

4

4 回答 4

23

您可以通过设置显式请求标头来说服 PHP 的 curl 后端停止执行 100-continue-thing:

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

这样,您可以发布请求,无论您想要多长时间,并且 curl 不会执行双阶段发布。

大约两年前,我在博客上写过这个。

于 2009-01-20T21:48:55.687 回答
3

最初的想法...

curl_setopt的手册页说 CURLOPT_POSTFIELDS

“要在 HTTP“POST”操作中发布的完整数据。要发布文件,请在文件名前面加上 @ 并使用完整路径。这可以作为 urlencoded 字符串传递,例如 'para1=val1¶2=val2&...'或以字段名称为键,字段数据为值的数组。”

可能是您的值被视为已被 urlencoded 处理,因此看起来像一个没有价值的大长名称。某处决定截断该名称。

也许你可以把它改成类似

$post_data = "data=".str_repeat('x', $char_count);

事实证明这太容易了,而且问题更深一些。那么,如何调试呢?

准确找出 CURL 发送到服务器的内容

另一种调试策略可能是制定一个 curl 命令行来实现相同的目标,并让它在生成 HTTP 请求时输出它们的详细信息。

手动测试服务器

您可以通过手动执行请求来消除等式中的服务器,例如远程登录到服务器上的端口 80 并向其发送 >1024 个字符的请求

POST /test/recipient.php HTTP/1.0
Host: gpx3quaa.joyent.us
Content-Length:1028

xxxxx(I put 1028 chars here, no point copying them all here!)

我收到了这个回复

HTTP/1.0 200 OK
Connection: close
Content-type: text/html; charset=UTF-8
Content-Length: 19
Date: Tue, 20 Jan 2009 21:35:16 GMT
Server: lighttpd/1.4.19

This was a triumph.Connection closed by foreign host.

所以至少你现在知道这一切都在客户端,可能是某个 CURL 选项或配置设置:(

最终答案!

这个问题引起了我的兴趣,所以我挖得更深

如果您使用CURLOPT_VERBOSE=>true,您会看到 CURL 在较大的帖子上发送一个额外的标题:Expect: 100-Continue. 您的 lighttpd 服务器似乎不喜欢这样。

CURLOPT_HTTP_VERSION=>CURL_HTTP_VERSION_1_0您可以通过在 curl_setopt 选项数组中强制 CURL 使用 HTTP/1.0 来阻止 CURL 执行此操作。

于 2009-01-20T21:27:19.707 回答
0

我在使用 SSL v3 的 IIS 服务器上遇到了类似的问题。

当 CURLOPT_POSTFIELDS 长于 1024 时,我不断收到以下 cURL 错误:

52 - SSL read: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number, errno 0

添加 CURLOPT_HTTPHEADER : "Expect:" 为我解决了这个问题。

非常感谢这个话题!

于 2009-05-05T17:18:43.390 回答
0

检查您是否启用了 Suhosin 补丁。默认情况下,它会在一定数量的索引后切断 POST 数据。您可以在 Suhosin 配置中绕过它。

于 2011-03-04T14:45:50.287 回答