使用分块编码的POST请求到PHP7未能正确返回结果

27
我正在从客户端(使用curl和自定义nodejs脚本进行测试)发送POST请求,但未能正确地收到响应。整个过程在PHP 5.6中运行良好。

环境

尽可能地减少了整个过程:

  • 所有内容都在Vagrant VM Ubuntu 14.04 LTS中运行
  • nginx 1.9.7来自http://nginx.org/packages/ubuntu/
  • PHP7 FPM从官方源编译而成,使用--disable-all --enable-fpm

我正在使用最小的nginx站点配置:

server {
  listen 80;
  server_name localhost;
  location / {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_pass  unix:/var/run/php/php7.0-fpm-api.sock;
    fastcgi_param SCRIPT_FILENAME /vagrant/index.php;
  }
}

来自/vagrant/index.php的PHP示例脚本:

<?php
echo str_repeat('.', 512);
flush(); // not necessary, only due testing

我正在使用的curl调用命令是:curl -XPOST http://localhost/ -H "Transfer-Encoding: chunked" -d ''

我正在使用的NodeJS脚本:

'use strict';

var http = require('http');
var url = require('url');

var uri = url.parse(process.env.URL);
var options = {
  method: 'POST', protocol: uri.protocol, hostname: uri.hostname,
  port: uri.port, path: uri.path,
};
var data = '';

var httpRequest = http.request(options, function(res) {
    res.on('data', function(chunk) {
      console.log('received data', chunk.length);
      data += chunk;
    });
    res.on('end', function() { console.log('final size', data.length); });
  })
  .on('error', function(err) { console.log(err); });

httpRequest.write('');
httpRequest.end();

将我的测试请求发送到PHP 5.6
$ curl http://localhost/
..........[cut off]

$ curl -XPOST http://localhost/ -H "Transfer-Encoding: chunked" -d ''
..........[cut off]

$ URL=http://localhost/ node php7test.js
received data 512
final size 512

将我的测试请求发送到PHP 7.0
$ curl http://localhost/
..........[cut off]

$ URL=http://localhost/ node php7test.js
final size 0

$ curl -XPOST http://localhost/ -H "Transfer-Encoding: chunked" -d ''
curl: (18) transfer closed with outstanding read data remaining

为什么我要使用分块编码?

没有业务原因需要这样做,但是我正在使用一个非常相似的NodeJS代码,默认情况下使用分块编码,但在切换到PHP7时突然停止工作。

我发现从nodejs方面以下方法可行:显式设置Content-Length头将删除由NodeJS发送的隐式Transfer-Encoding: chunked头,因此与两个PHP版本一起使用。

但是我想了解为什么PHP7在这里的行为不同,我是否有错误或这里真正发生了什么。

更新1:

  • 我比较了5.6和7.0之间的源代码,除了由于PHP内部更改而进行的更改外,几乎找不到任何区别
  • 内置服务器(php -S)不受影响,所有测试均通过

更新2:

我对PHP源代码进行了二分法,并能够确定行为何时更改:

期间,使用git bisect输出,我无法编译的提交:

$ git bisect skip
There are only 'skip'ped commits left to test.
The first bad commit could be any of:
ba5ecf355fe792a5a2a8e6582d5e081d02b16fbf
e383cb4493031a7cd952cfcaed3297e583149c07
fef18f4bea1980a59a9283c2197bd090aaf500cb
18cf4e0a8a574034f60f4d123407c173e57e54ec
We cannot bisect more!

我觉得这可能是一个 bug,所以写信给内部人员询问,也许他们有一些见解:https://marc.info/?l=php-internals&m=145090900217798&w=2


你正在使用最新版本的curl吗?你尝试过Apache吗?我使用PHP 7.0.0运行它(虽然7.0.1刚刚发布),curl(7.43.0)和Apache(2.4.16)。没有问题。 - Clay
我没有安装Apache,也没有相关经验,所以不,不使用Apache。我对Git源代码进行了二分查找,并成功确定了行为变化的时间点。我会尽快更新帖子。我没有尝试其他版本的curl,因为NodeJS脚本的行为也发生了变化,这让我得出结论,这不是客户端问题本身导致的。 - mark
你是否已经在 https://bugs.php.net/ 上报了一个 bug?如果没有,我建议你现在就去报。这明显是一个 bug,我在 Git 历史记录中也没有找到更多的信息。首先,请确保最新提交中仍然存在此问题。 - Will
我已发送邮件列表,但因为在那里还没有得到回复(我猜假期期间不是很好),所以最终我会在本周晚些时候完成。感谢您的时间和鼓励! - mark
这个错误在 PHP7.3 FPM 版本上仍然存在:https://stackoverflow.com/questions/54444220/request-with-chunked-encoding-not-returning-request-body-on-php-7-1 - Yahya Uddin
2个回答

2
从你的帖子中,我猜测你正在使用PHP7.0.0。如果我的猜测是(bool) TRUE,那么我建议你迁移到PHP7.0.1PHP7.0.0大约有27个错误PHP7.0.1中被修复;还有其他修复项。
来源:PHP.net变更日志
我也看了一下你上面的PHP代码(用我的谷歌眼镜),但它非常简单。我怀疑它可能与PHP7处理flush()和输出的方式有关。
此外:
注意到你的更新(特别是更新1更新2),我必须真的向你致敬!那真的很令人印象深刻。一定要检查一下这个已修复的错误#61751。如果我对你的PHP版本正确,那么这个已修复的错误可能已经解决了你的问题;你只需要迁移到PHP7.0.1
注意:我知道我应该检查已修复的错误的内部情况,但是……

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接