我正在从客户端(使用curl和自定义nodejs脚本进行测试)发送POST请求,但未能正确地收到响应。整个过程在PHP 5.6中运行良好。
将我的测试请求发送到PHP 5.6
将我的测试请求发送到PHP 7.0
环境
尽可能地减少了整个过程:
- 所有内容都在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源代码进行了二分法,并能够确定行为何时更改:
- 我能够编译成功的最后一次提交: https://github.com/php/php-src/commit/16265a59ac6bd433bfb636e4e44da1ad57cdcda9
- 我再次编译但无法工作的第一个提交: https://github.com/php/php-src/commit/86de98cabada88f4667839794c176ea37648498b
期间,使用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