mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Fix bug #76922: FastCGI terminates conn after FCGI_GET_VALUES
Closes GH-12387
This commit is contained in:
parent
7e5fb564d6
commit
e3d1beb0f1
2
NEWS
2
NEWS
@ -24,6 +24,8 @@ PHP NEWS
|
||||
- FPM:
|
||||
. Fixed bug GH-12232 (FPM: segfault dynamically loading extension without
|
||||
opcache). (Jakub Zelenka)
|
||||
. Fixed bug #76922 (FastCGI terminates conn after FCGI_GET_VALUES).
|
||||
(Jakub Zelenka)
|
||||
|
||||
- Intl:
|
||||
. Removed the BC break on IntlDateFormatter::construct which threw an
|
||||
|
@ -1202,7 +1202,7 @@ static int fcgi_read_request(fcgi_request *req)
|
||||
req->keep = 0;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -1470,7 +1470,8 @@ int fcgi_accept_request(fcgi_request *req)
|
||||
return -1;
|
||||
}
|
||||
req->hook.on_read();
|
||||
if (fcgi_read_request(req)) {
|
||||
int read_result = fcgi_read_request(req);
|
||||
if (read_result == 1) {
|
||||
#ifdef _WIN32
|
||||
if (is_impersonate && !req->tcp) {
|
||||
pipe = (HANDLE)_get_osfhandle(req->fd);
|
||||
@ -1481,7 +1482,7 @@ int fcgi_accept_request(fcgi_request *req)
|
||||
}
|
||||
#endif
|
||||
return req->fd;
|
||||
} else {
|
||||
} else if (read_result == 0) {
|
||||
fcgi_close(req, 1, 1);
|
||||
}
|
||||
}
|
||||
|
43
sapi/fpm/tests/bug76922-fcgi-get-value-conn.phpt
Normal file
43
sapi/fpm/tests/bug76922-fcgi-get-value-conn.phpt
Normal file
@ -0,0 +1,43 @@
|
||||
--TEST--
|
||||
FPM: bug76922 - FCGI conn termination after FCGI_GET_VALUES
|
||||
--SKIPIF--
|
||||
<?php include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
[unconfined]
|
||||
listen = {{ADDR}}
|
||||
pm = static
|
||||
pm.max_children = 1
|
||||
catch_workers_output = yes
|
||||
EOT;
|
||||
|
||||
$code = <<<EOT
|
||||
<?php
|
||||
echo 1;
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg, $code);
|
||||
$tester->start();
|
||||
$tester->expectLogStartNotices();
|
||||
$tester->requestValues(connKeepAlive: true)->expectValue('FCGI_MPXS_CONNS', '0');
|
||||
$tester->request(connKeepAlive: true)->expectBody('1');
|
||||
$tester->requestValues(connKeepAlive: true)->expectValue('FCGI_MPXS_CONNS', '0');
|
||||
$tester->terminate();
|
||||
$tester->close();
|
||||
|
||||
?>
|
||||
Done
|
||||
--EXPECT--
|
||||
Done
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
?>
|
||||
<?php
|
@ -60,9 +60,9 @@ class Client
|
||||
const OVERLOADED = 2;
|
||||
const UNKNOWN_ROLE = 3;
|
||||
|
||||
const MAX_CONNS = 'MAX_CONNS';
|
||||
const MAX_REQS = 'MAX_REQS';
|
||||
const MPXS_CONNS = 'MPXS_CONNS';
|
||||
const MAX_CONNS = 'FCGI_MAX_CONNS';
|
||||
const MAX_REQS = 'FCGI_MAX_REQS';
|
||||
const MPXS_CONNS = 'FCGI_MPXS_CONNS';
|
||||
|
||||
const HEADER_LEN = 8;
|
||||
|
||||
@ -454,8 +454,8 @@ class Client
|
||||
fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
|
||||
|
||||
$resp = $this->readPacket();
|
||||
if ($resp['type'] == self::GET_VALUES_RESULT) {
|
||||
return $this->readNvpair($resp['content'], $resp['length']);
|
||||
if (isset($resp['type']) && $resp['type'] == self::GET_VALUES_RESULT) {
|
||||
return $this->readNvpair($resp['content'], $resp['contentLength']);
|
||||
} else {
|
||||
throw new \Exception('Unexpected response type, expecting GET_VALUES_RESULT');
|
||||
}
|
||||
|
@ -434,3 +434,89 @@ class Response
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ValuesResponse
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private array $values;
|
||||
|
||||
/**
|
||||
* @param string|array|null $values
|
||||
*/
|
||||
public function __construct($values = null)
|
||||
{
|
||||
if ( ! is_array($values)) {
|
||||
if ( ! is_null($values) ) {
|
||||
$this->error('Invalid values supplied', true);
|
||||
}
|
||||
$this->values = [];
|
||||
} else {
|
||||
$this->values = $values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect value.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return ValuesResponse
|
||||
*/
|
||||
public function expectValue(string $name, $value = null)
|
||||
{
|
||||
if ( ! isset($this->values[$name])) {
|
||||
return $this->error("Value $name not found in values");
|
||||
}
|
||||
if ( ! is_null($value) && $value !== $this->values[$name]) {
|
||||
return $this->error("Value $name is {$this->values[$name]} but expected $value");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug output data.
|
||||
*
|
||||
* @return ValuesResponse
|
||||
*/
|
||||
public function debugOutput()
|
||||
{
|
||||
echo ">>> ValuesResponse\n";
|
||||
echo "----------------- Values -----------------\n";
|
||||
var_dump($this->values);
|
||||
echo "---------------------------------------\n\n";
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit error message
|
||||
*
|
||||
* @param string $message
|
||||
* @param bool $throw
|
||||
*
|
||||
* @return ValuesResponse
|
||||
*/
|
||||
private function error(string $message, $throw = false): bool
|
||||
{
|
||||
$errorMessage = "ERROR: $message\n";
|
||||
if ($throw) {
|
||||
throw new \Exception($errorMessage);
|
||||
}
|
||||
$this->debugOutput();
|
||||
echo $errorMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -868,6 +868,38 @@ class Tester
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute request for getting FastCGI values.
|
||||
*
|
||||
* @param string|null $address
|
||||
* @param bool $connKeepAlive
|
||||
*
|
||||
* @return ValuesResponse
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function requestValues(
|
||||
string $address = null,
|
||||
bool $connKeepAlive = false
|
||||
): ValuesResponse {
|
||||
if ($this->hasError()) {
|
||||
return new Response(null, true);
|
||||
}
|
||||
|
||||
try {
|
||||
$valueResponse = new ValuesResponse(
|
||||
$this->getClient($address, $connKeepAlive)->getValues(['FCGI_MPXS_CONNS'])
|
||||
);
|
||||
if ($this->debug) {
|
||||
$this->response->debugOutput();
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->error("Request for getting values failed", $exception);
|
||||
$valueResponse = new ValuesResponse();
|
||||
}
|
||||
|
||||
return $valueResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user