【问题标题】:PHP file_get_contents Returning Inconsistent Partial Data for HTTP RequestsPHP file_get_contents 为 HTTP 请求返回不一致的部分数据
【发布时间】:2020-05-08 14:48:59
【问题描述】:

我正在尝试使用 PHP SoapClient 来执行对第三方应用程序的请求。当我创建 SoapClient 对象时,我收到有关 WSDL 数据过早结束的错误。在尝试诊断错误时,我发现 WSDL URI 的 file_get_contents() 不会返回整个 XML。事实上,它经常返回不同数量的 WSDL。这是我的测试程序:

$xml = file_get_contents('https://webservices3.autotask.net/atservices/1.6/atws.wsdl');
echo $xml . "\n";
echo strlen($xml). "\n";

每次我得到大约 57k 字节(195628 是正确的值),有时我得到整个 XML 的次数越来越多,而且很少。我相信这是一个 PHP 问题,因为为这个 URI 调用 curl 或 wget 100 次的 shell 循环将 100% 的时间返回整个文件。我使用的是 PHP 5.4.16,我知道它很旧(2013 年),但这个过程工作了大约一个月,然后就完全停止了。

我尝试过更改超时、HTTP 协议版本、PHP 内存设置,但我不明白为什么 file_get_contents 会这样。任何建议表示赞赏。

卷曲测试:

for a in $( seq 1 100 ); do curl -o wsdl.$a https://webservices3.autotask.net/atservices/1.6/atws.wsdl; done

Wget 测试:

for a in $( seq 1 100 ); do wget -O wsdl.$a https://webservices3.autotask.net/atservices/1.6/atws.wsdl; done

更新 1:

将 maxlen 设置为一些愚蠢的大数字不会影响行为:

$xml = file_get_contents('https://webservices3.autotask.net/atservices/1.6/atws.wsdl', false, null, 0, 999999);
echo $xml . "\n";
echo strlen($xml). "\n";

更新 2:

$ curl -s -D /dev/stderr -- https://webservices3.autotask.net/atservices/1.6/atws.wsdl > /dev/null
HTTP/1.1 200 OK
Content-Type: text/xml
Last-Modified: Wed, 29 Apr 2020 14:38:25 GMT
Accept-Ranges: bytes
ETag: "39163cd7331ed61:0"
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Content-Security-Policy: default-src 'self' https: *;script-src 'self' 'unsafe-inline' 'unsafe-eval' https: *;style-src 'self' 'unsafe-inline';img-src 'self' https://walkme.psa.datto.com/Images/ data: https://www.datto.com/img/
Date: Fri, 08 May 2020 15:22:28 GMT
Content-Length: 195628

以下是 PHP 报告的响应标头:

$xml = file_get_contents('https://webservices3.autotask.net/atservices/1.6/atws.wsdl');
echo $xml . "\n";
echo strlen($xml). "\n";
echo var_dump($http_response_header);

array(11) {
  [0]=> string(15) "HTTP/1.1 200 OK"
  [1]=> string(22) "Content-Type: text/xml"
  [2]=> string(44) "Last-Modified: Wed, 29 Apr 2020 14:38:25 GMT"
  [3]=> string(20) "Accept-Ranges: bytes"
  [4]=> string(25) "ETag: "39163cd7331ed61:0""
  [5]=> string(25) "Server: Microsoft-IIS/8.5"
  [6]=> string(21) "X-Powered-By: ASP.NET"
  [7]=> string(228) "Content-Security-Policy: default-src 'self' https: *;script-src 'self' 'unsafe-inline' 'unsafe-eval' https: *;style-src 'self' 'unsafe-inline';img-src 'self' https://walkme.psa.datto.com/Images/ data: https://www.datto.com/img/ "
  [8]=> string(35) "Date: Fri, 08 May 2020 15:26:54 GMT"
  [9]=> string(22) "Connection: keep-alive"
  [10]=> string(22) "Content-Length: 195628"
}

更新 3:

使用 gzip 破坏 PHP 的 Content-Length 标头:

$ctx = stream_context_create(array(
    'http' => array(
        'header' => "Accept-Encoding: gzip\r\n"
     )
));
$xml = file_get_contents('https://webservices3.autotask.net/atservices/1.6/atws.wsdl', false, $ctx);
echo var_dump($http_response_header);

array(12) {
  [0]=> string(15) "HTTP/1.1 200 OK"
  [1]=> string(22) "Content-Type: text/xml"
  [2]=> string(44) "Last-Modified: Wed, 29 Apr 2020 14:35:51 GMT"
  [3]=> string(20) "Accept-Ranges: bytes"
  [4]=> string(24) "ETag: "b376e7b331ed61:0""
  [5]=> string(25) "Server: Microsoft-IIS/8.5"
  [6]=> string(21) "X-Powered-By: ASP.NET"
  [7]=> string(228) "Content-Security-Policy: default-src 'self' https: *;script-src 'self' 'unsafe-inline' 'unsafe-eval' https: *;style-src 'self' 'unsafe-inline';img-src 'self' https://walkme.psa.datto.com/Images/ data: https://www.datto.com/img/ "
  [8]=> string(35) "Date: Fri, 08 May 2020 15:44:12 GMT"
  [9]=> string(22) "Connection: keep-alive"
  [10]=> string(22) "ntCoent-Length: 195628"
  [11]=> string(22) "Content-Encoding: gzip"

}

更新 4:

使用 gzip 来自 curl 的标题(注意它们看起来正确):

$ curl --compressed -s -D /dev/stderr -- https://webservices3.autotask.net/atservices/1.6/atws.wsdl > /dev/null
HTTP/1.1 200 OK
Content-Type: text/xml
Content-Encoding: gzip
Last-Modified: Wed, 29 Apr 2020 14:35:51 GMT
Accept-Ranges: bytes
ETag: "807d37b331ed61:0"
Vary: Accept-Encoding
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Content-Security-Policy: default-src 'self' https: *;script-src 'self' 'unsafe-inline' 'unsafe-eval' https: *;style-src 'self' 'unsafe-inline';img-src 'self' https://walkme.psa.datto.com/Images/ data: https://www.datto.com/img/
Date: Fri, 08 May 2020 16:12:13 GMT
Content-Length: 13192

我能够强制 SoapClient 不使用 gzip,这确实解决了问题,尽管效率低下。我们仍然没有 PHP 破坏标头的根本原因。

// Autotask Client options
$auth_opts = array(
    'login'    => $username,
    'password' => $password,
    'trace'    => 1,
    'http'     => array(
        'header' => array(
            'Accept-Encoding' => 'identity' // here be dragons
        )
    )
);

更新 5:

我们确认这在 PHP 7.2 中仍然可以重现。我已经与 PHP 团队建立了bug

【问题讨论】:

  • 感谢您的建议,但根据不需要的文档,PHP 应该返回整个文件。我更新了我的问题以表明设置 maxlen 不会影响行为。
  • Kevin,你说“这个过程工作了大约一个月,然后就完全停止了。”。什么时候发生的?...Last-Modified: Wed, 29 Apr 2020 14:35:51 GMT.也许它是一个代理?尝试添加一些获取参数到 url file_get_contents('https://webservices3.autotask.net/atservices/1.6/atws.wsdl'.'?t='.time());
  • 很难说:该操作是由用户发起的,因此我们可能会在一天内多次发出请求,或者在几天内不发出请求。我看到的第一个失败是在 5 月 4 日。

标签: php file-get-contents


【解决方案1】:

webservices3.autotask.net 响应中的标头错误

HTTP/1.1 200 OK
Content-Type: text/xml
Accept-Ranges: bytes
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Cteonnt-Length: 195628
Cache-Control: private
Content-Encoding: gzip
Transfer-Encoding: chunked

注意:Cteonnt-Length: 195628 应该是 Content-Length: 195628

这就是file_get_contents 无法正确处理请求的原因。

所以,修复响应或设置maxlen

更新: 这是混乱的标题。 这应该有效https://stackoverflow.com/a/8582042/3849743

【讨论】:

  • 标题对我来说看起来不错。我会将它们添加到问题中。
  • 嗯,你能提供标题输出吗?
  • 是的,对不起。我正在努力按回车键添加一个空行和评论最大长度:-)
  • 哦,我明白了。用户浏览器 > 开发者工具 > 网络验证标头
  • 是的,我没有提到,但我已经验证了标题。我发布了两个选项来验证它们是否健康。
【解决方案2】:

这似乎是 PHP 中的一个错误。问题末尾有一个bug 报告链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-16
    • 2011-07-19
    • 2016-05-12
    • 1970-01-01
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多