facebook / facebook/hhvm

Bug on subsequent body-less POST request after a body-full POST request on reused curl handle

Open
#8,036 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.