Bug on subsequent body-less POST request after a body-full POST request on reused curl handle
- Dominant language
- C++
- Stars
- 18.7k
- Forks
- 3.1k
- Avg merge
- 1h 47m
- Merged PRs (30d)
- 2
Description
Get a curl handle using `curl_init()`.
Perform a POST request with a non-empty body.
Reset the curl handle using `curl_reset()`.
Perform a POST request without setting any body.
HHVM closes the connection and gives the curl_error `"read function returned funny value"` instead of the received body.
If you either to 2 POSTs with a body or without a body, it works fine.
If the first request is a GET, it works fine too.
PHP 7.1 exhibit no issue with the same code.
### HHVM Version
```
$ hhvm --version
HipHop VM 3.21.3 (rel)
Compiler: tags/HHVM-3.21.3-0-gce34e5458e6a75931438a2d8fc41b91454a2f2b2
Repo schema: 90ed4216b98563d0585cf5c87959dc9ff7c2e1a1
```
I compared with PHP 7.1
```
$ php --version
PHP 7.1.10-1+0~20170929170631.9+jessie~1.gbp501135 (cli) (built: Sep 29 2017 17:33:58) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.1.10-1+0~20170929170631.9+jessie~1.gbp501135, Copyright (c) 1999-2017, by Zend Technologies
```
### Standalone code, or other way to reproduce the problem
```
$url = 'http://httpbin.org/post';
echo "First request\n";
$request = curl_init();
curl_setopt($request, CURLOPT_POST, true);
curl_setopt($request, CURLOPT_URL, $url);
curl_setopt($request, CURLOPT_POSTFIELDS, 'hello');
curl_setopt($request, CURLOPT_RETURNTRANSFER, true); // not necessary for bug
$resp = curl_exec($request);
$error = curl_error($request);
$info = curl_getinfo($request);
var_dump($resp, $error, $info);
curl_reset($request);
echo "\n";
echo "Second request\n";
curl_setopt($request, CURLOPT_POST, true);
curl_setopt($request, CURLOPT_URL, $url);
// Uncomment the following line to fix HHVM 3.21.3-0-gce34e5458e6a75931438a2d8fc41b91454a2f2b2
// PHP 7.1.10-1+0~20170929170631.9+jessie~1.gbp501135 works fine
// curl_setopt($request, CURLOPT_POSTFIELDS, '');
curl_setopt($request, CURLOPT_RETURNTRANSFER, true); // not necessary for bug
// curl_setopt($request, CURLOPT_HTTPHEADER, ['Expect: ']); // same bug with or without this, although server will answer an additional "HTTP/1.1 Continue"
$resp = curl_exec($request);
$error = curl_error($request);
$info = curl_getinfo($request);
var_dump($resp, $error, $info);
curl_reset($request);
```
### Expected result
The first request var_dump and the second should be roughly equivalent (httpbin would simply not set `form.hello:""`).
```
First request
string(349) "{
"args": {},
"data": "",
"files": {},
"form": {
"hello": ""
},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Content-Length": "5",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org"
},
"json": null,
"origin": "1.2.3.4",
"url": "http://httpbin.org/post"
}
"
string(0) ""
array(26) {
["url"]=>
string(23) "http://httpbin.org/post"
["content_type"]=>
string(16) "application/json"
["http_code"]=>
int(200)
["header_size"]=>
int(303)
["request_size"]=>
int(128)
["filetime"]=>
int(-1)
["ssl_verify_result"]=>
int(0)
["redirect_count"]=>
int(0)
["total_time"]=>
float(0.257955)
["namelookup_time"]=>
float(0.004184)
["connect_time"]=>
float(0.126515)
["pretransfer_time"]=>
float(0.12655)
["size_upload"]=>
float(5)
["size_download"]=>
float(349)
["speed_download"]=>
float(1352)
["speed_upload"]=>
float(19)
["download_content_length"]=>
float(349)
["upload_content_length"]=>
float(5)
["starttransfer_time"]=>
float(0.257927)
["redirect_time"]=>
float(0)
["redirect_url"]=>
NULL
["primary_ip"]=>
string(14) "54.243.115.172"
["certinfo"]=>
array(0) {
}
["primary_port"]=>
int(80)
["local_ip"]=>
string(12) "1.2.3.4"
["local_port"]=>
int(54500)
}
Second request
string(330) "{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Content-Length": "0",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org"
},
"json": null,
"origin": "1.2.3.4",
"url": "http://httpbin.org/post"
}
"
string(0) ""
array(26) {
["url"]=>
string(23) "http://httpbin.org/post"
["content_type"]=>
string(16) "application/json"
["http_code"]=>
int(200)
["header_size"]=>
int(303)
["request_size"]=>
int(123)
["filetime"]=>
int(-1)
["ssl_verify_result"]=>
int(0)
["redirect_count"]=>
int(0)
["total_time"]=>
float(0.135898)
["namelookup_time"]=>
float(1.7E-5)
["connect_time"]=>
float(1.8E-5)
["pretransfer_time"]=>
float(4.2E-5)
["size_upload"]=>
float(0)
["size_download"]=>
float(330)
["speed_download"]=>
float(2428)
["speed_upload"]=>
float(0)
["download_content_length"]=>
float(330)
["upload_content_length"]=>
float(0)
["starttransfer_time"]=>
float(0.13587)
["redirect_time"]=>
float(0)
["redirect_url"]=>
NULL
["primary_ip"]=>
string(14) "54.243.115.172"
["certinfo"]=>
array(0) {
}
["primary_port"]=>
int(80)
["local_ip"]=>
string(12) "1.2.3.4"
["local_port"]=>
int(54500)
}
```
### Actual result
`curl_exec` returns `false` on the second call, and `curl_error` gives `"read function returned funny value"`.
```
First request
string(349) "{
"args": {},
"data": "",
"files": {},
"form": {
"hello": ""
},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Content-Length": "5",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org"
},
"json": null,
"origin": "1.2.3.4",
"url": "http://httpbin.org/post"
}
"
string(0) ""
array(26) {
["url"]=>
string(23) "http://httpbin.org/post"
["content_type"]=>
string(16) "application/json"
["http_code"]=>
int(200)
["header_size"]=>
int(303)
["request_size"]=>
int(128)
["filetime"]=>
int(-1)
["ssl_verify_result"]=>
int(0)
["redirect_count"]=>
int(0)
["total_time"]=>
float(0.273448)
["namelookup_time"]=>
float(0.004243)
["connect_time"]=>
float(0.135017)
["pretransfer_time"]=>
float(0.135139)
["size_upload"]=>
float(5)
["size_download"]=>
float(349)
["speed_download"]=>
float(1276)
["speed_upload"]=>
float(18)
["download_content_length"]=>
float(349)
["upload_content_length"]=>
float(5)
["starttransfer_time"]=>
float(0.273421)
["redirect_time"]=>
float(0)
["redirect_url"]=>
NULL
["primary_ip"]=>
string(13) "54.243.73.226"
["certinfo"]=>
array(0) {
}
["primary_port"]=>
int(80)
["local_ip"]=>
string(12) "1.2.3.4"
["local_port"]=>
int(51334)
}
Second request
bool(false)
string(34) "read function returned funny value"
array(26) {
["url"]=>
string(23) "http://httpbin.org/post"
["content_type"]=>
NULL
["http_code"]=>
int(100)
["header_size"]=>
int(25)
["request_size"]=>
int(126)
["filetime"]=>
int(-1)
["ssl_verify_result"]=>
int(0)
["redirect_count"]=>
int(0)
["total_time"]=>
float(0.136466)
["namelookup_time"]=>
float(1.7E-5)
["connect_time"]=>
float(1.7E-5)
["pretransfer_time"]=>
float(4.0E-5)
["size_upload"]=>
float(0)
["size_download"]=>
float(0)
["speed_download"]=>
float(0)
["speed_upload"]=>
float(0)
["download_content_length"]=>
float(-1)
["upload_content_length"]=>
float(-1)
["starttransfer_time"]=>
float(0.136471)
["redirect_time"]=>
float(0)
["redirect_url"]=>
NULL
["primary_ip"]=>
string(13) "54.243.73.226"
["certinfo"]=>
array(0) {
}
["primary_port"]=>
int(80)
["local_ip"]=>
string(12) "1.2.3.4"
["local_port"]=>
int(51334)
}
```
### Workaround
Simply ensure to set `CURLOPT_POSTFIELDS`, even to `""`, `[]` or `null`.
```
curl_setopt($request, CURLOPT_POSTFIELDS, '');
```
Or do not reuse curl handles, but you would loose kept alive connections.
Contributor guide
Assessment
This issue has not been assessed yet.