Merge branch 'PHP-8.1'

This commit is contained in:
Jakub Zelenka 2022-08-29 22:34:48 +01:00
commit f3c357c446
No known key found for this signature in database
GPG Key ID: 1C0779DC5C0A9DE4
7 changed files with 122 additions and 16 deletions

View File

@ -489,7 +489,7 @@ static void sapi_send_headers_free(void)
} }
} }
SAPI_API void sapi_deactivate(void) SAPI_API void sapi_deactivate_module(void)
{ {
zend_llist_destroy(&SG(sapi_headers).headers); zend_llist_destroy(&SG(sapi_headers).headers);
if (SG(request_info).request_body) { if (SG(request_info).request_body) {
@ -523,6 +523,10 @@ SAPI_API void sapi_deactivate(void)
if (sapi_module.deactivate) { if (sapi_module.deactivate) {
sapi_module.deactivate(); sapi_module.deactivate();
} }
}
SAPI_API void sapi_deactivate_destroy(void)
{
if (SG(rfc1867_uploaded_files)) { if (SG(rfc1867_uploaded_files)) {
destroy_uploaded_files_hash(); destroy_uploaded_files_hash();
} }
@ -537,6 +541,12 @@ SAPI_API void sapi_deactivate(void)
SG(global_request_time) = 0; SG(global_request_time) = 0;
} }
SAPI_API void sapi_deactivate(void)
{
sapi_deactivate_module();
sapi_deactivate_destroy();
}
SAPI_API void sapi_initialize_empty_request(void) SAPI_API void sapi_initialize_empty_request(void)
{ {

View File

@ -143,6 +143,8 @@ extern SAPI_API sapi_globals_struct sapi_globals;
SAPI_API void sapi_startup(sapi_module_struct *sf); SAPI_API void sapi_startup(sapi_module_struct *sf);
SAPI_API void sapi_shutdown(void); SAPI_API void sapi_shutdown(void);
SAPI_API void sapi_activate(void); SAPI_API void sapi_activate(void);
SAPI_API void sapi_deactivate_module(void);
SAPI_API void sapi_deactivate_destroy(void);
SAPI_API void sapi_deactivate(void); SAPI_API void sapi_deactivate(void);
SAPI_API void sapi_initialize_empty_request(void); SAPI_API void sapi_initialize_empty_request(void);
SAPI_API void sapi_add_request_header(const char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg); SAPI_API void sapi_add_request_header(const char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg);

View File

@ -1865,10 +1865,12 @@ void php_request_shutdown(void *dummy)
zend_post_deactivate_modules(); zend_post_deactivate_modules();
} zend_end_try(); } zend_end_try();
/* 12. SAPI related shutdown (free stuff) */ /* 12. SAPI related shutdown*/
zend_try { zend_try {
sapi_deactivate(); sapi_deactivate_module();
} zend_end_try(); } zend_end_try();
/* free SAPI stuff */
sapi_deactivate_destroy();
/* 13. free virtual CWD memory */ /* 13. free virtual CWD memory */
virtual_cwd_deactivate(); virtual_cwd_deactivate();

View File

@ -0,0 +1,54 @@
--TEST--
FPM: bug77780 - Headers already sent error incorrectly emitted
--SKIPIF--
<?php include "skipif.inc"; ?>
--EXTENSIONS--
session
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = static
pm.max_children = 1
EOT;
$code = <<<EOT
<?php
echo str_repeat('asdfghjkl', 150000) . "\n";
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester
->request(
headers: [
'PHP_VALUE' => "session.cookie_secure=1",
],
readLimit: 10,
expectError: true
);
$tester->request(
headers: [
'PHP_VALUE' => "session.cookie_secure=1",
]
)
->expectNoError();
$tester->terminate();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -26,6 +26,7 @@ namespace Adoy\FastCGI;
class TimedOutException extends \Exception {} class TimedOutException extends \Exception {}
class ForbiddenException extends \Exception {} class ForbiddenException extends \Exception {}
class ReadLimitExceeded extends \Exception {}
/** /**
* Handles communication with a FastCGI application * Handles communication with a FastCGI application
@ -404,16 +405,24 @@ class Client
/** /**
* Read a FastCGI Packet * Read a FastCGI Packet
* *
* @param int $readLimit max content size
* @return array * @return array
* @throws ReadLimitExceeded
*/ */
private function readPacket() private function readPacket($readLimit = -1)
{ {
if ($packet = fread($this->_sock, self::HEADER_LEN)) { if ($packet = fread($this->_sock, self::HEADER_LEN)) {
$resp = $this->decodePacketHeader($packet); $resp = $this->decodePacketHeader($packet);
$resp['content'] = ''; $resp['content'] = '';
if ($resp['contentLength']) { if ($resp['contentLength']) {
$len = $resp['contentLength']; $len = $resp['contentLength'];
while ($len && $buf=fread($this->_sock, $len)) { if ($readLimit >= 0 && $len > $readLimit) {
// close connection so it can be re-set reset and throw an error
fclose($this->_sock);
$this->_sock = null;
throw new ReadLimitExceeded("Content has $len bytes but the limit is $readLimit bytes");
}
while ($len && $buf = fread($this->_sock, $len)) {
$len -= strlen($buf); $len -= strlen($buf);
$resp['content'] .= $buf; $resp['content'] .= $buf;
} }
@ -473,15 +482,16 @@ class Client
* *
* @param array $params Array of parameters * @param array $params Array of parameters
* @param string $stdin Content * @param string $stdin Content
* @param int $readLimit [optional] the number of bytes to accept in a single packet or -1 if unlimited
* @return array * @return array
* @throws ForbiddenException * @throws ForbiddenException
* @throws TimedOutException * @throws TimedOutException
* @throws \Exception * @throws \Exception
*/ */
public function request_data(array $params, $stdin) public function request_data(array $params, $stdin, $readLimit = -1)
{ {
$id = $this->async_request($params, $stdin); $id = $this->async_request($params, $stdin);
return $this->wait_for_response_data($id); return $this->wait_for_response_data($id, 0, $readLimit);
} }
/** /**
@ -579,12 +589,13 @@ class Client
* *
* @param int $requestId * @param int $requestId
* @param int $timeoutMs [optional] the number of milliseconds to wait. * @param int $timeoutMs [optional] the number of milliseconds to wait.
* @param int $readLimit [optional] the number of bytes to accept in a single packet or -1 if unlimited
* @return array response data * @return array response data
* @throws ForbiddenException * @throws ForbiddenException
* @throws TimedOutException * @throws TimedOutException
* @throws \Exception * @throws \Exception
*/ */
public function wait_for_response_data($requestId, $timeoutMs = 0) public function wait_for_response_data($requestId, $timeoutMs = 0, $readLimit = -1)
{ {
if (!isset($this->_requests[$requestId])) { if (!isset($this->_requests[$requestId])) {
throw new \Exception('Invalid request id given'); throw new \Exception('Invalid request id given');
@ -608,7 +619,7 @@ class Client
// but still not get the response requested // but still not get the response requested
$startTime = microtime(true); $startTime = microtime(true);
while ($resp = $this->readPacket()) { while ($resp = $this->readPacket($readLimit)) {
if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) { if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
if ($resp['type'] == self::STDERR) { if ($resp['type'] == self::STDERR) {
$this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_ERR; $this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_ERR;

View File

@ -111,21 +111,31 @@ class Response
} }
/** /**
* @param string $errorMessage * @param string|null $errorMessage
* @return Response * @return Response
*/ */
public function expectError($errorMessage) public function expectError($errorMessage)
{ {
$errorData = $this->getErrorData(); $errorData = $this->getErrorData();
if ($errorData !== $errorMessage) { if ($errorData !== $errorMessage) {
$this->error( $expectedErrorMessage = $errorMessage !== null
"The expected error message '$errorMessage' is not equal to returned error '$errorData'" ? "The expected error message '$errorMessage' is not equal to returned error '$errorData'"
); : "No error message expected but received '$errorData'";
$this->error($expectedErrorMessage);
} }
return $this; return $this;
} }
/**
* @param string $errorMessage
* @return Response
*/
public function expectNoError()
{
return $this->expectError(null);
}
/** /**
* @param string $contentType * @param string $contentType
* @return string|null * @return string|null

View File

@ -606,6 +606,8 @@ class Tester
* @param string|null $errorMessage * @param string|null $errorMessage
* @param bool $connKeepAlive * @param bool $connKeepAlive
* @param string|null $scriptFilename = null * @param string|null $scriptFilename = null
* @param bool $expectError
* @param int $readLimit
* @return Response * @return Response
*/ */
public function request( public function request(
@ -617,7 +619,12 @@ class Tester
string $errorMessage = null, string $errorMessage = null,
bool $connKeepAlive = false, bool $connKeepAlive = false,
string $scriptFilename = null, string $scriptFilename = null,
<<<<<<< HEAD
string $stdin = null string $stdin = null
=======
bool $expectError = false,
int $readLimit = -1,
>>>>>>> PHP-8.1
) { ) {
if ($this->hasError()) { if ($this->hasError()) {
return new Response(null, true); return new Response(null, true);
@ -627,11 +634,21 @@ class Tester
try { try {
$this->response = new Response( $this->response = new Response(
<<<<<<< HEAD
$this->getClient($address, $connKeepAlive)->request_data($params, $stdin) $this->getClient($address, $connKeepAlive)->request_data($params, $stdin)
=======
$this->getClient($address, $connKeepAlive)->request_data($params, false, $readLimit)
>>>>>>> PHP-8.1
); );
$this->message($successMessage); if ($expectError) {
$this->error('Expected request error but the request was successful');
} else {
$this->message($successMessage);
}
} catch (\Exception $exception) { } catch (\Exception $exception) {
if ($errorMessage === null) { if ($expectError) {
$this->message($successMessage);
} elseif ($errorMessage === null) {
$this->error("Request failed", $exception); $this->error("Request failed", $exception);
} else { } else {
$this->message($errorMessage); $this->message($errorMessage);