Merge branch 'PHP-7.1' into PHP-7.2

This commit is contained in:
Jakub Zelenka 2018-06-12 18:03:01 +01:00
commit bc58ba750f
52 changed files with 3379 additions and 1684 deletions

View File

@ -1,51 +0,0 @@
--TEST--
FPM: Startup and connect
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = 127.0.0.1:$port
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
$i = 0;
while (($i++ < 60) && !($fp = @fsockopen('127.0.0.1', $port))) {
usleep(50000);
}
if ($fp) {
echo "Done\n";
fclose($fp);
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,54 +0,0 @@
--TEST--
FPM: Test IPv6 support
--SKIPIF--
<?php include "skipif.inc";
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) die('skip IPv6 not supported.');
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = [::1]:$port
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
$i = 0;
while (($i++ < 60) && !($fp = fsockopen('[::1]', $port))) {
usleep(50000);
}
if ($fp) {
echo "Done\n";
fclose($fp);
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,62 +0,0 @@
--TEST--
FPM: Test IPv4/IPv6 support
--SKIPIF--
<?php include "skipif.inc";
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) die('skip IPv6 not supported.');
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = [::]:$port
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
$i = 0;
while (($i++ < 60) && !($fp = @fsockopen('127.0.0.1', $port))) {
usleep(50000);
}
if ($fp) {
echo "Done IPv4\n";
fclose($fp);
}
while (($i++ < 60) && !($fp = @fsockopen('[::1]', $port))) {
usleep(50000);
}
if ($fp) {
echo "Done IPv6\n";
fclose($fp);
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
Done IPv4
Done IPv6
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,60 +0,0 @@
--TEST--
FPM: Test IPv4 allowed clients
--SKIPIF--
<?php include "skipif.inc";
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) die('skip IPv6 not supported.');
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = [::]:$port
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
run_request('127.0.0.1', $port);
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
try {
run_request('[::1]', $port);
echo "IPv6 ok\n";
} catch (Exception $e) {
echo "IPv6 error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
IPv4 ok
IPv6 error
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,60 +0,0 @@
--TEST--
FPM: Test IPv6 allowed clients (bug #68428)
--SKIPIF--
<?php include "skipif.inc";
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) die('skip IPv6 not supported.');
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = [::]:$port
listen.allowed_clients = ::1
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
run_request('127.0.0.1', $port);
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
try {
run_request('[::1]', $port);
echo "IPv6 ok\n";
} catch (Exception $e) {
echo "IPv6 error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
IPv4 error
IPv6 ok
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,71 +0,0 @@
--TEST--
FPM: Test IPv6 all addresses and access_log (bug #68421)
--SKIPIF--
<?php include "skipif.inc";
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) die('skip IPv6 not supported.');
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$accfile = dirname(__FILE__).'/php-fpm.acc.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = [::]:$port
access.log = $accfile
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
var_dump(strpos(run_request('127.0.0.1', $port), 'pong'));
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
try {
var_dump(strpos(run_request('[::1]', $port), 'pong'));
echo "IPv6 ok\n";
} catch (Exception $e) {
echo "IPv6 error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
echo file_get_contents($accfile);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
int(%d)
IPv4 ok
int(%d)
IPv6 ok
127.0.0.1 %s "GET /ping" 200
::1 %s "GET /ping" 200
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
$accfile = dirname(__FILE__).'/php-fpm.acc.tmp';
@unlink($accfile);
?>

View File

@ -1,100 +0,0 @@
--TEST--
FPM: Test multi pool (dynamic + ondemand + static) (bug #68423)
--SKIPIF--
<?php
include "skipif.inc";
$cfg = <<<EOT
[global]
error_log = /dev/null
[poold_ondemand]
listen=127.0.0.1:9000
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 10
EOT;
if (test_fpm_conf($cfg, $msg) == false) {
die("skip " . $msg);
}
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port1 = 9000+PHP_INT_SIZE;
$port2 = 9001+PHP_INT_SIZE;
$port3 = 9002+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[pool_dynamic]
listen = 127.0.0.1:$port1
ping.path = /ping
ping.response = pong-dynamic
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
[poold_ondemand]
listen = 127.0.0.1:$port2
ping.path = /ping
ping.response = pong-on-demand
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 10
[pool_static]
listen = 127.0.0.1:$port3
ping.path = /ping
ping.response = pong-static
pm = static
pm.max_children = 2
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
var_dump(strpos(run_request('127.0.0.1', $port1), 'pong-dynamic'));
echo "Dynamic ok\n";
} catch (Exception $e) {
echo "Dynamic error\n";
}
try {
var_dump(strpos(run_request('127.0.0.1', $port2), 'pong-on-demand'));
echo "OnDemand ok\n";
} catch (Exception $e) {
echo "OnDemand error\n";
}
try {
var_dump(strpos(run_request('127.0.0.1', $port3), 'pong-static'));
echo "Static ok\n";
} catch (Exception $e) {
echo "Static error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
int(%d)
Dynamic ok
int(%d)
OnDemand ok
int(%d)
Static ok
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,53 +0,0 @@
--TEST--
FPM: Test Unix Domain Socket
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$socket = dirname(__FILE__).'/php-fpm.sock';
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = $socket
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
var_dump(strpos(run_request('unix://'.$socket, -1), 'pong'));
echo "UDS ok\n";
} catch (Exception $e) {
echo "UDS error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
int(%d)
UDS ok
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,87 +0,0 @@
--TEST--
FPM: Test status page
--SKIPIF--
<?php include "skipif.inc"; ?>
--XFAIL--
randomly intermittently failing all the time in CI, with diff:
017+ active processes: 0
018+ total processes: 1
017- active processes: 1
018- total processes: 2
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = 127.0.0.1:$port
pm.status_path = /status
pm = static
pm.max_children = 1
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
echo run_request('127.0.0.1', $port, '/status');
$html = run_request('127.0.0.1', $port, '/status', 'html');
var_dump(strpos($html, 'text/html') && strpos($html, 'DOCTYPE') && strpos($html, 'PHP-FPM Status Page'));
$json = run_request('127.0.0.1', $port, '/status', 'json');
var_dump(strpos($json, 'application/json') && strpos($json, '"pool":"unconfined"'));
$xml = run_request('127.0.0.1', $port, '/status', 'xml');
var_dump(strpos($xml, 'text/xml') && strpos($xml, '<?xml'));
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
X-Powered-By: PHP/%s
Expires: %s
Cache-Control: %s
Content-type: text/plain%s
pool: unconfined
process manager: static
start time: %s
start since: %d
accepted conn: 1
listen queue: 0
max listen queue: 0
listen queue len: %d
idle processes: 0
active processes: 1
total processes: 1
max active processes: 1
max children reached: 0
slow requests: 0
bool(true)
bool(true)
bool(true)
IPv4 ok
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,53 +0,0 @@
--TEST--
FPM: Test IPv4 all addresses (bug #68420)
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = $port
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
var_dump(strpos(run_request('127.0.0.1', $port), 'pong'));
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
int(%d)
IPv4 ok
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,79 +0,0 @@
--TEST--
FPM: Test reload configuration (bug #68442)
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$pidfile = dirname(__FILE__).'/php-fpm.pid';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
pid = $pidfile
[unconfined]
listen = 127.0.0.1:$port
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
var_dump(strpos(run_request('127.0.0.1', $port), 'pong'));
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
$pid=file_get_contents($pidfile);
if ($pid) {
exec("kill -USR2 $pid");
} else {
die("PID not found\n");
}
fpm_display_log($tail, 5);
try {
var_dump(strpos(run_request('127.0.0.1', $port), 'pong'));
echo "IPv4 ok\n";
} catch (Exception $e) {
echo "IPv4 error\n";
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
int(%d)
IPv4 ok
[%d-%s-%d %d:%d:%d] NOTICE: Reloading in progress ...
[%d-%s-%d %d:%d:%d] NOTICE: reloading: %s
[%d-%s-%d %d:%d:%d] NOTICE: using inherited socket fd=%d, "127.0.0.1:%d"
[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
int(%d)
IPv4 ok
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
$pidfile = dirname(__FILE__).'/php-fpm.pid';
@unlink($pidfile);
?>

View File

@ -1,53 +0,0 @@
--TEST--
FPM: Test for log_level in fpm_unix_init_main #68381
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
log_level = warning
[unconfined]
listen = 127.0.0.1:$port
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
$i = 0;
while (($i++ < 60) && !($fp = @fsockopen('127.0.0.1', $port))) {
usleep(50000);
}
if ($fp) {
echo "Started\n";
fclose($fp);
}
proc_terminate($fpm);
if (!feof($tail)) {
echo stream_get_contents($tail);
}
fclose($tail);
proc_close($fpm);
}
?>
Done
--EXPECTF--
Started
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,53 +0,0 @@
--TEST--
FPM: Test for pm.start_servers default calculation message being a notice and not a warning #68458
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
log_level = warning
[unconfined]
listen = 127.0.0.1:$port
pm = dynamic
pm.max_children = 5
;pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
$i = 0;
while (($i++ < 60) && !($fp = @fsockopen('127.0.0.1', $port))) {
usleep(50000);
}
if ($fp) {
echo "Started\n";
fclose($fp);
}
proc_terminate($fpm);
if (!feof($tail)) {
echo stream_get_contents($tail);
}
fclose($tail);
proc_close($fpm);
}
?>
Done
--EXPECTF--
Started
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,89 +0,0 @@
--TEST--
FPM: Test various messages on start, from master and childs
--SKIPIF--
<?php include "skipif.inc"; ?>
--XFAIL--
randomly intermittently failing all the time in CI,
ERROR: unable to read what child say: Bad file descriptor (9)
catch_workers_output = yes seems not reliable
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$port1 = 9000+PHP_INT_SIZE;
$port2 = 9001+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
log_level = notice
[pool1]
listen = 127.0.0.1:$port1
listen.allowed_clients=127.0.0.1
user = foo
pm = dynamic
pm.max_children = 5
pm.min_spare_servers = 1
pm.max_spare_servers = 3
catch_workers_output = yes
[pool2]
listen = 127.0.0.1:$port2
listen.allowed_clients=xxx
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
catch_workers_output = yes
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
$i = 0;
while (($i++ < 60) && !($fp = @fsockopen('127.0.0.1', $port1))) {
usleep(50000);
}
if ($fp) {
echo "Started\n";
fclose($fp);
}
for ($i=0 ; $i<10 ; $i++) {
try {
run_request('127.0.0.1', $port1);
} catch (Exception $e) {
echo "Error 1\n";
}
}
try {
run_request('127.0.0.1', $port2);
} catch (Exception $e) {
echo "Error 2\n";
}
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
}
?>
Done
--EXPECTF--
Started
Error 2
[%s] NOTICE: [pool pool1] pm.start_servers is not set. It's been set to 2.
[%s] NOTICE: [pool pool1] 'user' directive is ignored when FPM is not running as root
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
[%s] WARNING: [pool pool2] child %d said into stderr: "ERROR: Wrong IP address 'xxx' in listen.allowed_clients"
[%s] WARNING: [pool pool2] child %d said into stderr: "ERROR: There are no allowed %s"
[%s] WARNING: [pool pool2] child %d said into stderr: "ERROR: Connection disallowed: IP address '127.0.0.1' has been dropped."
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,101 +0,0 @@
--TEST--
FPM: Test splited configuration and load order #68391
--SKIPIF--
<?php
include "skipif.inc";
$cfg = <<<EOT
[global]
error_log = /dev/null
[poold_ondemand]
listen=127.0.0.1:9000
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 10
EOT;
if (test_fpm_conf($cfg, $msg) == false) {
die("skip " . $msg);
}
?>
--FILE--
<?php
include "include.inc";
$logfile = __DIR__.'/php-fpm.log.tmp';
$logdir = __DIR__.'/conf.d';
$port = 9000+PHP_INT_SIZE;
// Main configuration
$cfg = <<<EOT
[global]
error_log = $logfile
log_level = notice
include = $logdir/*.conf
EOT;
// Splited configuration
@mkdir($logdir);
$i=$port;
$names = ['cccc', 'aaaa', 'eeee', 'dddd', 'bbbb'];
foreach($names as $name) {
$poolcfg = <<<EOT
[$name]
listen = 127.0.0.1:$i
listen.allowed_clients=127.0.0.1
user = foo
pm = ondemand
pm.max_children = 5
EOT;
file_put_contents("$logdir/$name.conf", $poolcfg);
$i++;
}
// Test
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, count($names)+2);
$i=$port;
foreach($names as $name) {
try {
run_request('127.0.0.1', $i++);
echo "OK $name\n";
} catch (Exception $e) {
echo "Error 1\n";
}
}
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
}
?>
Done
--EXPECTF--
[%s] NOTICE: [pool aaaa] 'user' directive is ignored when FPM is not running as root
[%s] NOTICE: [pool bbbb] 'user' directive is ignored when FPM is not running as root
[%s] NOTICE: [pool cccc] 'user' directive is ignored when FPM is not running as root
[%s] NOTICE: [pool dddd] 'user' directive is ignored when FPM is not running as root
[%s] NOTICE: [pool eeee] 'user' directive is ignored when FPM is not running as root
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
OK cccc
OK aaaa
OK eeee
OK dddd
OK bbbb
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
Done
--CLEAN--
<?php
$logfile = __DIR__.'/php-fpm.log.tmp';
$logdir = __DIR__.'/conf.d';
@unlink($logfile);
foreach(glob("$logdir/*.conf") as $name) {
unlink($name);
}
@rmdir($logdir);
?>

View File

@ -1,67 +0,0 @@
--TEST--
FPM: Test fastcgi_finish_request function
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = __DIR__.'/php-fpm.log.tmp';
$srcfile = __DIR__.'/php-fpm.tmp.php';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = 127.0.0.1:$port
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$code = <<<EOT
<?php
echo "Test Start\n";
fastcgi_finish_request();
echo "Test End\n";
EOT;
file_put_contents($srcfile, $code);
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
$req = run_request('127.0.0.1', $port, $srcfile);
echo strstr($req, "Test Start");
echo "Request ok\n";
} catch (Exception $e) {
echo "Request error\n";
}
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
}
?>
Done
--EXPECTF--
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
Test Start
Request ok
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
Done
--CLEAN--
<?php
$logfile = __DIR__.'/php-fpm.log.tmp';
$srcfile = __DIR__.'/php-fpm.tmp.php';
@unlink($logfile);
@unlink($srcfile);
?>

View File

@ -1,79 +0,0 @@
--TEST--
FPM: Test global prefix
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = 'php-fpm.log.tmp';
$accfile = 'php-fpm.acc.tmp';
$slwfile = 'php-fpm.slw.tmp';
$pidfile = 'php-fpm.pid.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
pid = $pidfile
[test]
listen = 127.0.0.1:$port
access.log = $accfile
slowlog = $slwfile;
request_slowlog_timeout = 1
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail, '--prefix '.__DIR__);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
run_request('127.0.0.1', $port);
echo "Ping ok\n";
} catch (Exception $e) {
echo "Ping error\n";
}
printf("File %s %s\n", $logfile, (file_exists(__DIR__.'/'.$logfile) ? "exists" : "missing"));
printf("File %s %s\n", $accfile, (file_exists(__DIR__.'/'.$accfile) ? "exists" : "missing"));
printf("File %s %s\n", $slwfile, (file_exists(__DIR__.'/'.$slwfile) ? "exists" : "missing"));
printf("File %s %s\n", $pidfile, (file_exists(__DIR__.'/'.$pidfile) ? "exists" : "missing"));
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
printf("File %s %s\n", $pidfile, (file_exists(__DIR__.'/'.$pidfile) ? "still exists" : "removed"));
readfile(__DIR__.'/'.$accfile);
}
?>
--EXPECTF--
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
Ping ok
File php-fpm.log.tmp exists
File php-fpm.acc.tmp exists
File php-fpm.slw.tmp exists
File php-fpm.pid.tmp exists
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
File php-fpm.pid.tmp removed
127.0.0.1 - %s "GET /ping" 200
--CLEAN--
<?php
$logfile = __DIR__.'/php-fpm.log.tmp';
$accfile = __DIR__.'/php-fpm.acc.tmp';
$slwfile = __DIR__.'/php-fpm.slw.tmp';
$pidfile = __DIR__.'/php-fpm.pid.tmp';
@unlink($logfile);
@unlink($accfile);
@unlink($slwfile);
@unlink($pidfile);
?>

View File

@ -1,75 +0,0 @@
--TEST--
FPM: Test pool prefix
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$prefix = __DIR__;
$logfile = __DIR__.'/php-fpm.log.tmp';
$accfile = 'php-fpm.acc.tmp';
$slwfile = 'php-fpm.slw.tmp';
$pidfile = __DIR__.'/php-fpm.pid.tmp';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
pid = $pidfile
[test]
prefix = $prefix;
listen = 127.0.0.1:$port
access.log = $accfile
slowlog = $slwfile;
request_slowlog_timeout = 1
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
run_request('127.0.0.1', $port);
echo "Ping ok\n";
} catch (Exception $e) {
echo "Ping error\n";
}
printf("File %s %s\n", $accfile, (file_exists(__DIR__.'/'.$accfile) ? "exists" : "missing"));
printf("File %s %s\n", $slwfile, (file_exists(__DIR__.'/'.$slwfile) ? "exists" : "missing"));
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
readfile(__DIR__.'/'.$accfile);
}
?>
--EXPECTF--
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
Ping ok
File php-fpm.acc.tmp exists
File php-fpm.slw.tmp exists
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
127.0.0.1 - %s "GET /ping" 200
--CLEAN--
<?php
$logfile = __DIR__.'/php-fpm.log.tmp';
$accfile = __DIR__.'/php-fpm.acc.tmp';
$slwfile = __DIR__.'/php-fpm.slw.tmp';
$pidfile = __DIR__.'/php-fpm.pid.tmp';
@unlink($logfile);
@unlink($accfile);
@unlink($slwfile);
@unlink($pidfile);
?>

View File

@ -1,102 +0,0 @@
--TEST--
FPM: Test Unix Domain Socket with Posix ACL
--SKIPIF--
<?php
include "skipif.inc";
if (!(file_exists('/usr/bin/getfacl') && file_exists('/etc/passwd') && file_exists('/etc/group'))) die ("skip missing getfacl command");
$cfg = <<<EOT
[global]
error_log = /dev/null
[unconfined]
listen = 127.0.0.1:9999
listen.acl_users = nobody
listen.acl_groups = nobody
listen.mode = 0600
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
if (test_fpm_conf($cfg, $msg) == false) { die("skip " . $msg); }
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$socket = dirname(__FILE__).'/php-fpm.sock';
// Select 3 users and 2 groups known by system (avoid root)
$users = $groups = [];
$tmp = file('/etc/passwd');
for ($i=1 ; $i<=3 ; $i++) {
$tab = explode(':', $tmp[$i]);
$users[] = $tab[0];
}
$users = implode(',', $users);
$tmp = file('/etc/group');
for ($i=1 ; $i<=2 ; $i++) {
$tab = explode(':', $tmp[$i]);
$groups[] = $tab[0];
}
$groups = implode(',', $groups);
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = $socket
listen.acl_users = $users
listen.acl_groups = $groups
listen.mode = 0600
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
var_dump(strpos(run_request('unix://'.$socket, -1), 'pong'));
echo "UDS ok\n";
} catch (Exception $e) {
echo "UDS error\n";
}
passthru("/usr/bin/getfacl -cp $socket");
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
}
?>
--EXPECTF--
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
int(%d)
UDS ok
user::rw-
user:%s:rw-
user:%s:rw-
user:%s:rw-
group::---
group:%s:rw-
group:%s:rw-
mask::rw-
other::---
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>

View File

@ -1,81 +0,0 @@
--TEST--
FPM: HTTP_PROXY - CVE-2016-5385
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$logfile = __DIR__.'/php-fpm.log.tmp';
$srcfile = __DIR__.'/php-fpm.tmp.php';
$port = 9000+PHP_INT_SIZE;
$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = 127.0.0.1:$port
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$code = <<<EOT
<?php
echo "Test Start\n";
var_dump(
@\$_SERVER["HTTP_PROXY"],
\$_SERVER["HTTP_FOO"],
getenv("HTTP_PROXY"),
getenv("HTTP_FOO")
);
echo "Test End\n";
EOT;
file_put_contents($srcfile, $code);
$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
fpm_display_log($tail, 2);
try {
$headers = [
'HTTP_FOO' => 'BAR',
'HTTP_PROXY' => 'BADPROXY',
];
$req = run_request('127.0.0.1', $port, $srcfile, '', $headers);
echo strstr($req, "Test Start");
echo "Request ok\n";
} catch (Exception $e) {
echo "Request error\n";
}
proc_terminate($fpm);
fpm_display_log($tail, -1);
fclose($tail);
proc_close($fpm);
}
?>
Done
--EXPECTF--
[%s] NOTICE: fpm is running, pid %d
[%s] NOTICE: ready to handle connections
Test Start
NULL
string(3) "BAR"
bool(false)
string(3) "BAR"
Test End
Request ok
[%s] NOTICE: Terminating ...
[%s] NOTICE: exiting, bye-bye!
Done
--CLEAN--
<?php
$logfile = __DIR__.'/php-fpm.log.tmp';
$srcfile = __DIR__.'/php-fpm.tmp.php';
@unlink($logfile);
@unlink($srcfile);
?>

View File

@ -0,0 +1,40 @@
--TEST--
FPM: bug68381 - Log messages with warning level only
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
log_level = warning
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->checkConnection();
$tester->terminate();
$tester->expectNoLogMessages();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,53 @@
--TEST--
FPM: bug68391 - Configuration inclusion in alphabetical order
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg['main'] = <<<EOT
[global]
error_log = {{FILE:LOG}}
log_level = notice
include = {{INCLUDE:CONF}}
EOT;
$cfgPoolTemplate = <<<EOT
[%name%]
listen = {{ADDR[%name%]}}
user = foo
pm = ondemand
pm.max_children = 5
EOT;
$names = ['cccc', 'aaaa', 'eeee', 'dddd', 'bbbb'];
foreach($names as $name) {
$cfg[$name] = str_replace('%name%', $name, $cfgPoolTemplate);
}
$tester = new FPM\Tester($cfg);
$tester->start();
$userMessage = "'user' directive is ignored when FPM is not running as root";
$tester->expectLogNotice($userMessage, 'aaaa');
$tester->expectLogNotice($userMessage, 'bbbb');
$tester->expectLogNotice($userMessage, 'cccc');
$tester->expectLogNotice($userMessage, 'dddd');
$tester->expectLogNotice($userMessage, 'eeee');
$tester->expectLogStartNotices();
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,42 @@
--TEST--
FPM: bug68420 - IPv4 all addresses
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{PORT}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping('127.0.0.1');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,47 @@
--TEST--
FPM: bug68421 - IPv6 all addresses and access_log
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfIPv6IsNotSupported();
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG:ERR}}
[unconfined]
listen = {{ADDR:IPv6:ANY}}
access.log = {{FILE:LOG:ACC}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping('127.0.0.1');
$tester->ping('[::1]');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
$tester->printAccessLog();
?>
Done
--EXPECTF--
127.0.0.1 %s "GET /ping" 200
::1 %s "GET /ping" 200
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,55 @@
--TEST--
FPM: bug68423 - Multiple pools with different PMs (dynamic + ondemand + static)
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[pool_dynamic]
listen = {{ADDR[dynamic]}}
ping.path = /ping
ping.response = pong-dynamic
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
[pool_ondemand]
listen = {{ADDR[ondemand]}}
ping.path = /ping
ping.response = pong-on-demand
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 10
[pool_static]
listen = {{ADDR[static]}}
ping.path = /ping
ping.response = pong-static
pm = static
pm.max_children = 2
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping('{{ADDR[dynamic]}}', 'pong-dynamic');
$tester->ping('{{ADDR[ondemand]}}', 'pong-on-demand');
$tester->ping('{{ADDR[static]}}', 'pong-static');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,45 @@
--TEST--
FPM: bug68428 - IPv6 allowed client only
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfIPv6IsNotSupported();
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:IPv6:ANY}}
listen.allowed_clients = ::1
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->checkRequest('127.0.0.1', 'IPv4: ok', 'IPv4: error');
$tester->checkRequest('[::1]', 'IPv6: ok', 'IPv6: error');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
IPv4: error
IPv6: ok
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,47 @@
--TEST--
FPM: bug68442 - Signal reload
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
pid = {{FILE:PID}}
[unconfined]
listen = {{ADDR}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping('{{ADDR}}');
$tester->signal('USR2');
$tester->expectLogNotice('Reloading in progress ...');
$tester->expectLogNotice('reloading: .*');
$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
$tester->expectLogStartNotices();
$tester->ping('{{ADDR}}');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,42 @@
--TEST--
FPM: bug68458 - Missing pm.start_servers should emit notice instead of warning
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
log_level = warning
[unconfined]
listen = {{ADDR}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
;pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->checkConnection();
$tester->terminate();
$tester->expectNoLogMessages();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,66 @@
--TEST--
FPM: bug72573 - HTTP_PROXY - CVE-2016-5385
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$code = <<<EOT
<?php
echo "Test Start\n";
var_dump(
@\$_SERVER["HTTP_PROXY"],
\$_SERVER["HTTP_FOO"],
getenv("HTTP_PROXY"),
getenv("HTTP_FOO")
);
echo "Test End\n";
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester
->request(
'',
[
'HTTP_FOO' => 'BAR',
'HTTP_PROXY' => 'BADPROXY',
]
)
->expectBody(
[
'Test Start',
'NULL',
'string(3) "BAR"',
'bool(false)',
'string(3) "BAR"',
'Test End'
]
);
$tester->terminate();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,45 @@
--TEST--
FPM: Function fastcgi_finish_request basic test
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$code = <<<EOT
<?php
echo "Test Start\n";
fastcgi_finish_request();
echo "Test End\n";
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester->request()->expectBody("Test Start");
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -72,25 +72,25 @@ class Client
/**
* Socket
* @var Resource
* @var resource
*/
private $_sock = null;
/**
* Host
* @var String
* @var string
*/
private $_host = null;
/**
* Port
* @var Integer
* @var int
*/
private $_port = null;
/**
* Keep Alive
* @var Boolean
* @var bool
*/
private $_keepAlive = false;
@ -110,27 +110,27 @@ class Client
/**
* Use persistent sockets to connect to backend
* @var Boolean
* @var bool
*/
private $_persistentSocket = false;
/**
* Connect timeout in milliseconds
* @var Integer
* @var int
*/
private $_connectTimeout = 5000;
/**
* Read/Write timeout in milliseconds
* @var Integer
* @var int
*/
private $_readWriteTimeout = 5000;
/**
* Constructor
*
* @param String $host Host of the FastCGI application
* @param Integer $port Port of the FastCGI application
* @param string $host Host of the FastCGI application
* @param int $port Port of the FastCGI application
*/
public function __construct($host, $port)
{
@ -138,15 +138,25 @@ class Client
$this->_port = $port;
}
/**
* Get host.
*
* @return string
*/
public function getHost()
{
return $this->_host;
}
/**
* Define whether or not the FastCGI application should keep the connection
* alive at the end of a request
*
* @param Boolean $b true if the connection should stay alive, false otherwise
* @param bool $b true if the connection should stay alive, false otherwise
*/
public function setKeepAlive($b)
{
$this->_keepAlive = (boolean)$b;
$this->_keepAlive = (bool)$b;
if (!$this->_keepAlive && $this->_sock) {
fclose($this->_sock);
}
@ -155,7 +165,7 @@ class Client
/**
* Get the keep alive status
*
* @return Boolean true if the connection should stay alive, false otherwise
* @return bool true if the connection should stay alive, false otherwise
*/
public function getKeepAlive()
{
@ -166,12 +176,12 @@ class Client
* Define whether or not PHP should attempt to re-use sockets opened by previous
* request for efficiency
*
* @param Boolean $b true if persistent socket should be used, false otherwise
* @param bool $b true if persistent socket should be used, false otherwise
*/
public function setPersistentSocket($b)
{
$was_persistent = ($this->_sock && $this->_persistentSocket);
$this->_persistentSocket = (boolean)$b;
$this->_persistentSocket = (bool)$b;
if (!$this->_persistentSocket && $was_persistent) {
fclose($this->_sock);
}
@ -180,7 +190,7 @@ class Client
/**
* Get the pesistent socket status
*
* @return Boolean true if the socket should be persistent, false otherwise
* @return bool true if the socket should be persistent, false otherwise
*/
public function getPersistentSocket()
{
@ -191,7 +201,7 @@ class Client
/**
* Set the connect timeout
*
* @param Integer number of milliseconds before connect will timeout
* @param int number of milliseconds before connect will timeout
*/
public function setConnectTimeout($timeoutMs)
{
@ -201,7 +211,7 @@ class Client
/**
* Get the connect timeout
*
* @return Integer number of milliseconds before connect will timeout
* @return int number of milliseconds before connect will timeout
*/
public function getConnectTimeout()
{
@ -211,7 +221,7 @@ class Client
/**
* Set the read/write timeout
*
* @param Integer number of milliseconds before read or write call will timeout
* @param int number of milliseconds before read or write call will timeout
*/
public function setReadWriteTimeout($timeoutMs)
{
@ -222,7 +232,7 @@ class Client
/**
* Get the read timeout
*
* @return Integer number of milliseconds before read will timeout
* @return int number of milliseconds before read will timeout
*/
public function getReadWriteTimeout()
{
@ -232,14 +242,18 @@ class Client
/**
* Helper to avoid duplicating milliseconds to secs/usecs in a few places
*
* @param Integer millisecond timeout
* @return Boolean
* @param int millisecond timeout
* @return bool
*/
private function set_ms_timeout($timeoutMs) {
if (!$this->_sock) {
return false;
}
return stream_set_timeout($this->_sock, floor($timeoutMs / 1000), ($timeoutMs % 1000) * 1000);
return stream_set_timeout(
$this->_sock,
floor($timeoutMs / 1000),
($timeoutMs % 1000) * 1000
);
}
@ -250,9 +264,21 @@ class Client
{
if (!$this->_sock) {
if ($this->_persistentSocket) {
$this->_sock = pfsockopen($this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout/1000);
$this->_sock = pfsockopen(
$this->_host,
$this->_port,
$errno,
$errstr,
$this->_connectTimeout/1000
);
} else {
$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout/1000);
$this->_sock = fsockopen(
$this->_host,
$this->_port,
$errno,
$errstr,
$this->_connectTimeout/1000
);
}
if (!$this->_sock) {
@ -268,9 +294,10 @@ class Client
/**
* Build a FastCGI packet
*
* @param Integer $type Type of the packet
* @param String $content Content of the packet
* @param Integer $requestId RequestId
* @param int $type Type of the packet
* @param string $content Content of the packet
* @param int $requestId RequestId
* @return string
*/
private function buildPacket($type, $content, $requestId = 1)
{
@ -289,9 +316,9 @@ class Client
/**
* Build an FastCGI Name value pair
*
* @param String $name Name
* @param String $value Value
* @return String FastCGI Name value pair
* @param string $name Name
* @param string $value Value
* @return string FastCGI Name value pair
*/
private function buildNvpair($name, $value)
{
@ -302,14 +329,16 @@ class Client
$nvpair = chr($nlen);
} else {
/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
$nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
$nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF)
. chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
}
if ($vlen < 128) {
/* valueLengthB0 */
$nvpair .= chr($vlen);
} else {
/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
$nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
$nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF)
. chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
}
/* nameData & valueData */
return $nvpair . $name . $value;
@ -318,7 +347,7 @@ class Client
/**
* Read a set of FastCGI Name value pairs
*
* @param String $data Data containing the set of FastCGI NVPair
* @param string $data Data containing the set of FastCGI NVPair
* @return array of NVPair
*/
private function readNvpair($data, $length = null)
@ -357,7 +386,7 @@ class Client
/**
* Decode a FastCGI Packet
*
* @param String $data String containing all the packet
* @param string $data string containing all the packet
* @return array
*/
private function decodePacketHeader($data)
@ -403,6 +432,7 @@ class Client
*
* @param array $requestedInfo information to retrieve
* @return array
* @throws \Exception
*/
public function getValues(array $requestedInfo)
{
@ -423,11 +453,14 @@ class Client
}
/**
* Execute a request to the FastCGI application
* Execute a request to the FastCGI application and return response body
*
* @param array $params Array of parameters
* @param String $stdin Content
* @return String
* @param string $stdin Content
* @return string
* @throws ForbiddenException
* @throws TimedOutException
* @throws \Exception
*/
public function request(array $params, $stdin)
{
@ -435,20 +468,38 @@ class Client
return $this->wait_for_response($id);
}
/**
* Execute a request to the FastCGI application and return request data
*
* @param array $params Array of parameters
* @param string $stdin Content
* @return array
* @throws ForbiddenException
* @throws TimedOutException
* @throws \Exception
*/
public function request_data(array $params, $stdin)
{
$id = $this->async_request($params, $stdin);
return $this->wait_for_response_data($id);
}
/**
* Execute a request to the FastCGI application asyncronously
*
*
* This sends request to application and returns the assigned ID for that request.
*
* You should keep this id for later use with wait_for_response(). Ids are chosen randomly
* rather than seqentially to guard against false-positives when using persistent sockets.
* In that case it is possible that a delayed response to a request made by a previous script
* invocation comes back on this socket and is mistaken for response to request made with same ID
* during this request.
* rather than sequentially to guard against false-positives when using persistent sockets.
* In that case it is possible that a delayed response to a request made by a previous script
* invocation comes back on this socket and is mistaken for response to request made with same
* ID during this request.
*
* @param array $params Array of parameters
* @param String $stdin Content
* @return Integer
* @param string $stdin Content
* @return int
* @throws TimedOutException
* @throws \Exception
*/
public function async_request(array $params, $stdin)
{
@ -460,10 +511,12 @@ class Client
// Using persistent sockets implies you want them keept alive by server!
$keepAlive = intval($this->_keepAlive || $this->_persistentSocket);
$request = $this->buildPacket(self::BEGIN_REQUEST
,chr(0) . chr(self::RESPONDER) . chr($keepAlive) . str_repeat(chr(0), 5)
,$id
);
$request = $this->buildPacket(
self::BEGIN_REQUEST,
chr(0) . chr(self::RESPONDER) . chr($keepAlive)
. str_repeat(chr(0), 5),
$id
);
$paramsRequest = '';
foreach ($params as $key => $value) {
@ -494,21 +547,26 @@ class Client
$this->_requests[$id] = array(
'state' => self::REQ_STATE_WRITTEN,
'response' => null
'response' => null,
'err_response' => null,
'out_response' => null,
);
return $id;
}
/**
* Blocking call that waits for response to specific request
*
* @param Integer $requestId
* @param Integer $timeoutMs [optional] the number of milliseconds to wait. Defaults to the ReadWriteTimeout value set.
* @return string response body
* Blocking call that waits for response data of the specific request
*
* @param int $requestId
* @param int $timeoutMs [optional] the number of milliseconds to wait.
* @return array response data
* @throws ForbiddenException
* @throws TimedOutException
* @throws \Exception
*/
public function wait_for_response($requestId, $timeoutMs = 0) {
public function wait_for_response_data($requestId, $timeoutMs = 0)
{
if (!isset($this->_requests[$requestId])) {
throw new \Exception('Invalid request id given');
}
@ -537,6 +595,9 @@ class Client
if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
if ($resp['type'] == self::STDERR) {
$this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_ERR;
$this->_requests[$resp['requestId']]['err_response'] .= $resp['content'];
} else {
$this->_requests[$resp['requestId']]['out_response'] .= $resp['content'];
}
$this->_requests[$resp['requestId']]['response'] .= $resp['content'];
}
@ -586,7 +647,22 @@ class Client
throw new \Exception('Role value not known [UNKNOWN_ROLE]');
break;
case self::REQUEST_COMPLETE:
return $this->_requests[$requestId]['response'];
return $this->_requests[$requestId];
}
}
/**
* Blocking call that waits for response to specific request
*
* @param int $requestId
* @param int $timeoutMs [optional] the number of milliseconds to wait.
* @return string The response content.
* @throws ForbiddenException
* @throws TimedOutException
* @throws \Exception
*/
public function wait_for_response($requestId, $timeoutMs = 0)
{
return $this->wait_for_response_data($requestId, $timeoutMs)['response'];
}
}

View File

@ -1,135 +0,0 @@
<?php
function get_fpm_path() /* {{{ */
{
$php_path = getenv("TEST_PHP_EXECUTABLE");
for ($i = 0; $i < 2; $i++) {
$slash_pos = strrpos($php_path, "/");
if ($slash_pos) {
$php_path = substr($php_path, 0, $slash_pos);
} else {
return false;
}
}
if ($php_path && is_dir($php_path)) {
if (file_exists($php_path."/fpm/php-fpm") && is_executable($php_path."/fpm/php-fpm")) {
/* gotcha */
return $php_path."/fpm/php-fpm";
}
$php_sbin_fpm = $php_path."/sbin/php-fpm";
if (file_exists($php_sbin_fpm) && is_executable($php_sbin_fpm)) {
return $php_sbin_fpm;
}
}
return false;
}
/* }}} */
function run_fpm($config, &$out = false, $extra_args = '') /* {{{ */
{
$cfg = dirname(__FILE__).'/test-fpm-config.tmp';
file_put_contents($cfg, $config);
$desc = [];
if ($out !== false) {
$desc = [1 => array('pipe', 'w')];
}
/* Since it's not possible to spawn a process under linux without using a
* shell in php (why?!?) we need a little shell trickery, so that we can
* actually kill php-fpm */
$asroot = getenv('TEST_FPM_RUN_AS_ROOT') ? '--allow-to-run-as-root' : '';
$cmd = get_fpm_path()." $asroot -F -O -y $cfg $extra_args";
$fpm = proc_open("killit () { kill \$child; }; trap killit TERM; $cmd 2>&1 & child=\$!; wait",
$desc, $pipes);
register_shutdown_function(
function($fpm) use($cfg) {
@unlink($cfg);
if (is_resource($fpm)) {
@proc_terminate($fpm);
while (proc_get_status($fpm)['running']) {
usleep(10000);
}
}
},
$fpm
);
if ($out !== false) {
$out = $pipes[1];
}
return $fpm;
}
/* }}} */
function test_fpm_conf($config, &$msg = NULL) { /* {{{ */
$cfg = dirname(__FILE__).'/test-fpm-config.tmp';
file_put_contents($cfg, $config);
exec(get_fpm_path() . ' -t -y ' . $cfg . ' 2>&1', $output, $code);
if ($code) {
$msg = preg_replace("/\[.+?\]/", "", $output[0]);
return false;
}
return true;
}
/* }}} */
function run_fpm_till($needle, $config, $max = 10) /* {{{ */
{
$i = 0;
$fpm = run_fpm($config, $tail);
if (is_resource($fpm)) {
while($i < $max) {
$i++;
$line = fgets($tail);
if(preg_match($needle, $line) === 1) {
break;
}
}
if ($i >= $max) {
$line = false;
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}
return $line;
}
/* }}} */
function fpm_display_log($tail, $n=1, $ignore='systemd') { /* {{{ */
/* Read $n lines or until EOF */
while ($n>0 || ($n<0 && !feof($tail))) {
$a = fgets($tail);
if (empty($ignore) || !strpos($a, $ignore)) {
echo $a;
$n--;
}
}
} /* }}} */
function run_request($host, $port, $uri='/ping', $query='', $headers=array()) { /* {{{ */
require_once 'fcgi.inc';
$client = new Adoy\FastCGI\Client($host, $port);
$params = array_merge(array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'GET',
'SCRIPT_FILENAME' => $uri,
'SCRIPT_NAME' => $uri,
'QUERY_STRING' => $query,
'REQUEST_URI' => $uri . ($query ? '?'.$query : ""),
'DOCUMENT_URI' => $uri,
'SERVER_SOFTWARE' => 'php/fcgiclient',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '9985',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => php_uname('n'),
'SERVER_PROTOCOL' => 'HTTP/1.1',
'CONTENT_TYPE' => '',
'DOCUMENT_ROOT' => __DIR__,
'CONTENT_LENGTH' => 0
), $headers);
return $client->request($params, false)."\n";
}
/* }}} */

476
sapi/fpm/tests/logtool.inc Normal file
View File

@ -0,0 +1,476 @@
<?php
namespace FPM;
class LogTool
{
const P_TIME = '\[\d\d-\w\w\w-\d{4} \d\d:\d\d:\d\d\]';
const P_PREFIX = '\[pool unconfined\] child \d+ said into stderr: ';
const FINAL_SUFFIX = ', pipe is closed';
/**
* @var string
*/
private $message;
/**
* @var string
*/
private $level;
/**
* @var int
*/
private $position;
/**
* @var int
*/
private $suffixPosition;
/**
* @var int
*/
private $limit;
/**
* @var string
*/
private $pattern;
/**
* @var string
*/
private $error;
/**
* @param string $message
* @param int $limit
* @param int $repeat
*/
public function setExpectedMessage(string $message, int $limit, int $repeat = 0)
{
$this->message = ($repeat > 0) ? str_repeat($message, $repeat) : $message;
$this->limit = $limit;
$this->position = 0;
}
/**
* @param string $level
* @return int
*/
public function setExpectedLevel(string $level)
{
return $this->level = $level;
}
/**
* @return string
*/
public function getExpectedLevel(): string
{
return $this->level ?: 'WARNING';
}
/**
* @param string $line
* @return bool
*/
public function checkTruncatedMessage(string $line)
{
if ($this->message === null) {
throw new \LogicException('The message has not been set');
}
$lineLen = strlen($line);
if (!$this->checkLineLength($line)) {
return false;
}
$this->pattern = '/^PHP message: (.*?)(\.\.\.)?$/';
if (preg_match($this->pattern, $line, $matches) === 0) {
return $this->error("Unexpected truncated message: {$line}");
}
if ($lineLen === $this->limit) {
if (!isset($matches[2])) {
return $this->error("The truncated line is not ended with '...'");
}
if (!$this->checkMessage($matches[1])) {
return false;
}
} else {
if (isset($matches[2])) {
// this is expecting that the expected message does not end with '...'
// which should not be an issue for the test purpose.
return $this->error("The line is complete and should not end with '...'");
}
if (!$this->checkMessage($matches[1], -1)) {
return false;
}
}
return true;
}
/**
* @param array $lines
* @param bool $terminated
* @param bool $decorated
* @return bool
*/
public function checkWrappedMessage(array $lines, bool $terminated = true, bool $decorated = true)
{
if ($this->message === null) {
throw new \LogicException('The message has not been set');
}
if ($decorated) {
$this->pattern = sprintf(
'/^(%s %s: %s)"([^"]*)"(.*)?$/',
self::P_TIME,
$this->getExpectedLevel(),
self::P_PREFIX
);
} else {
$this->pattern = null;
}
$idx = 0;
foreach ($lines as $idx => $line) {
if (!$this->checkLine($line)) {
break;
}
}
if ($this->suffixPosition > 0) {
$suffixPattern = sprintf(
'/^%s %s: %s(.*)$/',
self::P_TIME, $this->getExpectedLevel(),
self::P_PREFIX
);
$line = $lines[++$idx];
if (preg_match($suffixPattern, $line, $matches) === 0) {
return $this->error("Unexpected line: $line");
}
if ($matches[1] !== substr(self::FINAL_SUFFIX, $this->suffixPosition)) {
return $this->error(
"The suffix has not been finished from position $this->suffixPosition in line: $line"
);
}
}
if ($terminated) {
return $this->expectTerminatorLines($lines, $idx);
}
return true;
}
/**
* @param string $line
* @return bool
*/
private function checkLine(string $line)
{
if ($this->pattern === null) {
// plain (not decorated) output
$out = rtrim($line);
$finalSuffix = null;
} elseif (($res = preg_match($this->pattern, $line, $matches)) > 0) {
$out = $matches[2];
$finalSuffix = $matches[3] ?? false;
} else {
return $this->error("Unexpected line: $line");
}
$rem = strlen($this->message) - $this->position;
$lineLen = strlen($line);
if (!$this->checkLineLength($line, $lineLen)) {
return false;
}
if (!$this->checkMessage($out, $this->position)) {
return false;
}
$outLen = strlen($out);
if ($rem > $outLen) { // continuous line
if ($lineLen !== $this->limit) {
if ($lineLen + ($rem - $outLen) < $this->limit) {
return $this->error("Printed less than the message len");
}
return $this->error(
"The continuous line length is $lineLen but it should equal to limit $this->limit"
);
}
$this->position += $outLen;
return true;
}
if ($rem !== $outLen) {
return $this->error("Printed more than the message len");
}
if ($finalSuffix === null || $finalSuffix === "") {
return false;
}
if ($finalSuffix === false) {
return $this->error("No final suffix");
}
if (strpos(self::FINAL_SUFFIX, $finalSuffix) === false) {
return $this->error("The final suffix has to be equal to ', pipe is closed'");
}
if (self::FINAL_SUFFIX !== $finalSuffix) {
$this->suffixPosition = strlen($finalSuffix);
}
// complete final suffix printed
return false;
}
/**
* @param string $line
* @param int $lineLen
* @return bool
*/
private function checkLineLength(string $line, $lineLen = null) {
$lineLen = $lineLen ?: strlen($line);
if ($lineLen > $this->limit) {
return $this->error(
"The line length is $lineLen which is higher than limit $this->limit"
);
}
return true;
}
/**
* @param string $matchedMessage
* @param int $expectedMessageStart
* @return bool
*/
private function checkMessage(string $matchedMessage, int $expectedMessageStart = 0)
{
if ($expectedMessageStart < 0) {
$expectedMessage = $this->message;
} else {
$expectedMessage = substr($this->message, $expectedMessageStart, strlen($matchedMessage));
}
if ($expectedMessage !== $matchedMessage) {
return $this->error(
sprintf(
"The actual string(%d) does not match expected string(%d):\n",
strlen($matchedMessage),
strlen($expectedMessage)
) .
"- EXPECT: '$expectedMessage'\n" .
"- ACTUAL: '$matchedMessage'"
);
}
return true;
}
/**
* @param array $lines
* @return bool
*/
public function expectStartingLines(array $lines)
{
if ($this->getError()) {
return false;
}
if (count($lines) < 2) {
return $this->error("No starting lines");
}
return (
$this->expectNotice($lines[0], 'fpm is running, pid \d+') &&
$this->expectNotice($lines[1], 'ready to handle connections')
);
}
/**
* @param array $lines
* @param int $idx
* @return bool
*/
public function expectTerminatorLines(array $lines, int $idx = -1)
{
if ($this->getError()) {
return false;
}
if (count($lines) - $idx < 3) {
return $this->error("No terminating lines");
}
return (
$this->expectNotice($lines[++$idx], 'Terminating ...') &&
$this->expectNotice($lines[++$idx], 'exiting, bye-bye!')
);
}
/**
* @param string $type
* @param string $line
* @param string $expectedMessage
* @param string|null $pool
* @return bool
*/
public function expectEntry(string $type, string $line, string $expectedMessage, $pool = null)
{
if ($this->getError()) {
return false;
}
if ($pool !== null) {
$expectedMessage = '\[pool ' . $pool . '\] ' . $expectedMessage;
}
$line = rtrim($line);
$pattern = sprintf('/^%s %s: %s$/', self::P_TIME, $type, $expectedMessage);
if (preg_match($pattern, $line, $matches) === 0) {
return $this->error(
"The $type does not match expected message:\n" .
"- PATTERN: $pattern\n" .
"- MESSAGE: $line\n" .
"- EXPECT: '$expectedMessage'\n" .
"- ACTUAL: '" . substr($line, strpos($line, $type) + strlen($type) + 2) . "'"
);
}
return true;
}
/**
* @param string $line
* @param string $expectedMessage
* @param string|null $pool
* @return bool
*/
public function expectDebug(string $line, string $expectedMessage, $pool = null)
{
return $this->expectEntry('DEBUG', $line, $expectedMessage, $pool);
}
/**
* @param string $line
* @param string $expectedMessage
* @param string|null $pool
* @return bool
*/
public function expectNotice(string $line, string $expectedMessage, $pool = null)
{
return $this->expectEntry('NOTICE', $line, $expectedMessage, $pool);
}
/**
* @param string $line
* @param string $expectedMessage
* @param string|null $pool
* @return bool
*/
public function expectWarning(string $line, string $expectedMessage, $pool = null)
{
return $this->expectEntry('WARNING', $line, $expectedMessage, $pool);
}
/**
* @param string $line
* @param string $expectedMessage
* @param string|null $pool
* @return bool
*/
public function expectError(string $line, string $expectedMessage, $pool = null)
{
return $this->expectEntry('ERROR', $line, $expectedMessage, $pool);
}
/**
* @param string $line
* @param string $expectedMessage
* @param string|null $pool
* @return bool
*/
public function expectAlert(string $line, string $expectedMessage, $pool = null)
{
return $this->expectEntry('ALERT', $line, $expectedMessage, $pool);
}
/**
* @param string $msg
* @return bool
*/
private function error(string $msg)
{
$this->error = $msg;
echo "ERROR: $msg\n";
return false;
}
/**
* @return string
*/
public function getError()
{
return $this->error;
}
}
if (isset($argv[1]) && $argv[1] === 'logtool-selftest') {
$cases = [
[
'limit' => 1050,
'lines' => [
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
str_repeat('a', 968) . '"',
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
str_repeat('a', 968) . '"',
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
str_repeat('a', 112) . '", pipe is closed',
'[08-Oct-2017 19:53:55] NOTICE: Terminating ...',
'[08-Oct-2017 19:53:55] NOTICE: exiting, bye-bye!',
],
'message' => str_repeat('a', 2048),
'type' => 'stdio',
],
[
'limit' => 1050,
'lines' => [
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
str_repeat('a', 968) . '"',
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
str_repeat('a', 968) . '"',
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
str_repeat('a', 964) . '", pi',
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: pe is closed',
'[08-Oct-2017 19:53:55] NOTICE: Terminating ...',
'[08-Oct-2017 19:53:55] NOTICE: exiting, bye-bye!',
],
'message' => str_repeat('a', 2900),
'type' => 'stdio',
],
[
'limit' => 1024,
'line' => '[08-Oct-2017 19:53:50] WARNING: ' . str_repeat('a',989) . '...',
'message' => str_repeat('a', 2900),
'type' => 'message',
],
[
'limit' => 1024,
'line' => '[08-Oct-2017 19:53:50] WARNING: ' . str_repeat('a',20),
'message' => str_repeat('a', 20),
'type' => 'message',
],
];
foreach ($cases as $case) {
printf("Test message with len %d and limit %d: ", strlen($case['message']), $case['limit']);
$logTool = new LogTool();
$logTool->setExpectedMessage($case['message'], $case['limit']);
if ($case['type'] === 'stdio') {
$logTool->checkWrappedMessage($case['lines']);
} else {
$logTool->checkTruncatedMessage($case['line']);
}
if (!$logTool->getError()) {
echo "OK\n";
}
}
echo "Done\n";
}

View File

@ -0,0 +1,50 @@
--TEST--
FPM: Main invocation with prefix
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{RFILE:LOG:ERR}}
pid = {{RFILE:PID}}
[unconfined]
listen = {{ADDR}}
access.log = {{RFILE:LOG:ACC}}
slowlog = {{RFILE:LOG:SLOW}}
request_slowlog_timeout = 1
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$prefix = __DIR__;
$tester = new FPM\Tester($cfg);
$tester->start('--prefix ' . $prefix);
$tester->expectLogStartNotices();
$tester->expectFile(FPM\Tester::FILE_EXT_LOG_ACC, $prefix);
$tester->expectFile(FPM\Tester::FILE_EXT_LOG_ERR, $prefix);
$tester->expectFile(FPM\Tester::FILE_EXT_LOG_SLOW, $prefix);
$tester->expectFile(FPM\Tester::FILE_EXT_PID, $prefix);
$tester->ping();
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
$tester->expectNoFile(FPM\Tester::FILE_EXT_PID, $prefix);
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -5,9 +5,9 @@ FPM: version string
--FILE--
<?php
include "include.inc";
require_once "tester.inc";
$php = get_fpm_path();
$php = \FPM\Tester::findExecutable();
var_dump(`$php -n -v`);

View File

@ -1,27 +1,32 @@
--TEST--
FPM: Apparmor Test
--DESCRIPTION--
This test tries to launches a pool which tries to change to non existing
apparmor hat a. Test succeeds if apparmor is not running or hat is non
existent.
FPM: AppArmor basic test
--SKIPIF--
<?php
include "skipif.inc";
include "skipapparmor.inc";
$config = <<<EOT
[global]
error_log = /dev/null
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
apparmor_hat = a
EOT;
FPM\Tester::skipIfConfigFails($config);
?>
--FILE--
<?php
include "include.inc";
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = $logfile
[a]
listen = 127.0.0.1:9001
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:UDS}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
@ -30,6 +35,7 @@ pm.max_spare_servers = 3
apparmor_hat = a
EOT;
$tester = new FPM\Tester($cfg);
/* libapparmor has a bug which can cause SIGSEGV till Version 2.8.0-0ubuntu28
See https://bugs.launchpad.net/apparmor/+bug/1196880
Possible outcomes:
@ -41,14 +47,17 @@ EOT;
- exited with code 70
Change to successful; Hat not existent (Process gets killed by apparmor)
*/
var_dump(run_fpm_till('/(SIGSEGV|failed to query apparmor confinement|failed to change to new confinement|exited with code 70)/', $cfg));
$tester->runTill(
'/(SIGSEGV|failed to query apparmor confinement|' .
'failed to change to new confinement|exited with code 70)/'
);
?>
--EXPECTF--
string(%d) "%s
"
Done
--EXPECT--
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,54 @@
--TEST--
FPM: Pool prefix
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$prefix = __DIR__;
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
pid = {{FILE:PID}}
[unconfined]
prefix = $prefix
listen = {{ADDR}}
access.log = {{RFILE:LOG:ACC}}
slowlog = {{RFILE:LOG:SLOW}}
request_slowlog_timeout = 1
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping();
$tester->expectFile(FPM\Tester::FILE_EXT_LOG_ACC, $prefix);
$tester->expectFile(FPM\Tester::FILE_EXT_LOG_ERR);
$tester->expectFile(FPM\Tester::FILE_EXT_LOG_SLOW, $prefix);
$tester->expectFile(FPM\Tester::FILE_EXT_PID);
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
$tester->expectNoFile(FPM\Tester::FILE_EXT_PID);
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,45 @@
--TEST--
FPM: Process manager config option pm.start_servers missing
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
;pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogNotice(
"pm.start_servers is not set. It's been set to 2.",
'unconfined'
);
$tester->expectLogStartNotices();
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,46 @@
--TEST--
FPM: Process user setting ignored when FPM is not running as root
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
user = foo
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogNotice(
"'user' directive is ignored when FPM is not running as root",
'unconfined'
);
$tester->expectLogStartNotices();
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

281
sapi/fpm/tests/response.inc Normal file
View File

@ -0,0 +1,281 @@
<?php
namespace FPM;
class Response
{
const HEADER_SEPARATOR = "\r\n\r\n";
/**
* @var array
*/
private $data;
/**
* @var string
*/
private $rawData;
/**
* @var string
*/
private $rawHeaders;
/**
* @var string
*/
private $rawBody;
/**
* @var array
*/
private $headers;
/**
* @var bool
*/
private $valid;
/**
* @var bool
*/
private $expectInvalid;
/**
* @param string|array|null $data
* @param bool $expectInvalid
*/
public function __construct($data = null, $expectInvalid = false)
{
if (!is_array($data)) {
$data = [
'response' => $data,
'err_response' => null,
'out_response' => $data,
];
}
$this->data = $data;
$this->expectInvalid = $expectInvalid;
}
/**
* @param mixed $body
* @param string $contentType
* @return Response
*/
public function expectBody($body, $contentType = 'text/html')
{
if ($multiLine = is_array($body)) {
$body = implode("\n", $body);
}
if (
$this->checkIfValid() &&
$this->checkDefaultHeaders($contentType) &&
$body !== $this->rawBody
) {
if ($multiLine) {
$this->error(
"==> The expected body:\n$body\n" .
"==> does not match the actual body:\n$this->rawBody"
);
} else {
$this->error(
"The expected body '$body' does not match actual body '$this->rawBody'"
);
}
}
return $this;
}
/**
* @return Response
*/
public function expectEmptyBody()
{
return $this->expectBody('');
}
/**
* @param string $contentType
* @return string|null
*/
public function getBody($contentType = 'text/html')
{
if ($this->checkIfValid() && $this->checkDefaultHeaders($contentType)) {
return $this->rawBody;
}
return null;
}
/**
* Print raw body
*/
public function dumpBody()
{
var_dump($this->getBody());
}
/**
* Print raw body
*/
public function printBody()
{
echo $this->getBody();
}
/**
* Debug response output
*/
public function debugOutput()
{
echo "-------------- RESPONSE: --------------\n";
echo "OUT:\n";
echo $this->data['out_response'];
echo "ERR:\n";
echo $this->data['err_response'];
echo "---------------------------------------\n\n";
}
/**
* @return string|null
*/
public function getErrorData()
{
return $this->data['err_response'];
}
/**
* Check if the response is valid and if not emit error message
*
* @return bool
*/
private function checkIfValid()
{
if ($this->isValid()) {
return true;
}
if (!$this->expectInvalid) {
$this->error("The response is invalid: $this->rawData");
}
return false;
}
/**
* @param string $contentType
* @return bool
*/
private function checkDefaultHeaders($contentType)
{
// check default headers
return (
$this->checkHeader('X-Powered-By', '|^PHP/7|', true) &&
$this->checkHeader('Content-type', '|^' . $contentType . '(;\s?charset=\w+)?|', true)
);
}
/**
* @param string $name
* @param string $value
* @param bool $useRegex
* @return bool
*/
private function checkHeader(string $name, string $value, $useRegex = false)
{
$lcName = strtolower($name);
$headers = $this->getHeaders();
if (!isset($headers[$lcName])) {
return $this->error("The header $name is not present");
}
$header = $headers[$lcName];
if (!$useRegex) {
if ($header === $value) {
return true;
}
return $this->error("The header $name value '$header' is not the same as '$value'");
}
if (!preg_match($value, $header)) {
return $this->error("The header $name value '$header' does not match RegExp '$value'");
}
return true;
}
/**
* @return array|null
*/
private function getHeaders()
{
if (!$this->isValid()) {
return null;
}
if (is_array($this->headers)) {
return $this->headers;
}
$headerRows = explode("\r\n", $this->rawHeaders);
$headers = [];
foreach ($headerRows as $headerRow) {
$colonPosition = strpos($headerRow, ':');
if ($colonPosition === false) {
$this->error("Invalid header row (no colon): $headerRow");
}
$headers[strtolower(substr($headerRow, 0, $colonPosition))] = trim(
substr($headerRow, $colonPosition + 1)
);
}
return ($this->headers = $headers);
}
/**
* @return bool
*/
private function isValid()
{
if ($this->valid === null) {
$this->processData();
}
return $this->valid;
}
/**
* Process data and set validity and raw data
*/
private function processData()
{
$this->rawData = $this->data['out_response'];
$this->valid = (
!is_null($this->rawData) &&
strpos($this->rawData, self::HEADER_SEPARATOR)
);
if ($this->valid) {
list ($this->rawHeaders, $this->rawBody) = array_map(
'trim',
explode(self::HEADER_SEPARATOR, $this->rawData)
);
}
}
/**
* Emit error message
*
* @param string $message
* @return bool
*/
private function error($message)
{
echo "ERROR: $message\n";
return false;
}
}

View File

@ -1,30 +0,0 @@
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
$cfg = <<<EOT
[global]
error_log = $logfile
[a]
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
apparmor_hat = a
EOT;
$fpm = run_fpm($cfg, $out, '-t');
$ok = false;
if (is_resource($fpm)) {
if (strpos(stream_get_contents($out), "test is successful") !== FALSE) {
$ok = true;
}
fclose($out);
proc_close($fpm);
}
if (!$ok) {
die("skip No apparmor support built in");
}
?>

View File

@ -1,17 +1,15 @@
<?php
// Do not run on Windows
if (substr(PHP_OS, 0, 3) == 'WIN') {
die ("skip not for Windows");
die("skip not for Windows");
}
// Running as root is not allowed without TEST_FPM_RUN_AS_ROOT env
if (!getmyuid() && !getenv('TEST_FPM_RUN_AS_ROOT')) {
die('Refusing to run as root');
die('skip Refusing to run as root');
}
include dirname(__FILE__)."/include.inc";
require_once "tester.inc";
if (!get_fpm_path()) {
die("skip FPM not found");
}
?>
if (!FPM\Tester::findExecutable()) {
die("skip php-fpm binary not found");
}

View File

@ -0,0 +1,48 @@
--TEST--
FPM: Socket for invalid allowed client only
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
listen.allowed_clients = xxx
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
catch_workers_output = yes
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->checkRequest('127.0.0.1', 'Req: ok', 'Req: error');
$tester->terminate();
// this is from child when starting
$tester->expectLogLine("ERROR: Wrong IP address 'xxx' in listen.allowed_clients");
$tester->expectLogLine("ERROR: There are no allowed addresses");
// this is from the request
$tester->expectLogLine("ERROR: Connection disallowed: IP address '127.0.0.1' has been dropped.");
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Req: error
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,45 @@
--TEST--
FPM: Socket for IPv4 allowed client only
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfIPv6IsNotSupported();
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:IPv6:ANY}}
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->checkRequest('127.0.0.1', 'IPv4: ok', 'IPv4: error');
$tester->checkRequest('[::1]', 'IPv6: ok', 'IPv6: error');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
IPv4: ok
IPv6: error
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,37 @@
--TEST--
FPM: Socket for IPv4 connection
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:IPv4}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,44 @@
--TEST--
FPM: Socket for IPv6 any address connection
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfIPv6IsNotSupported();
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:IPv6:ANY}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->checkConnection('127.0.0.1', 'IPv4: ok');
$tester->checkConnection('[::1]', 'IPv6: ok');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
IPv4: ok
IPv6: ok
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,40 @@
--TEST--
FPM: Socket for IPv6 connection
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfIPv6IsNotSupported();
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:IPv6}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,87 @@
--TEST--
FPM: Unix Domain Socket with Posix ACL
--SKIPIF--
<?php
include "skipif.inc";
FPM\Tester::skipIfAnyFileDoesNotExist(['/usr/bin/getfacl', '/etc/passwd', '/etc/group']);
$config = <<<EOT
[global]
error_log = /dev/null
[unconfined]
listen = {{ADDR}}
listen.acl_users = nobody
listen.acl_groups = nobody
listen.mode = 0600
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
FPM\Tester::skipIfConfigFails($config);
?>
--FILE--
<?php
require_once "tester.inc";
// Select 3 users and 2 groups known by system (avoid root)
$users = $groups = [];
$tmp = file('/etc/passwd');
for ($i=1 ; $i <= 3 ; $i++) {
$tab = explode(':', $tmp[$i]);
$users[] = $tab[0];
}
$users = implode(',', $users);
$tmp = file('/etc/group');
for ($i=1 ; $i <= 2 ; $i++) {
$tab = explode(':', $tmp[$i]);
$groups[] = $tab[0];
}
$groups = implode(',', $groups);
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:UDS}}
listen.acl_users = $users
listen.acl_groups = $groups
listen.mode = 0600
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping('{{ADDR:UDS}}');
passthru("/usr/bin/getfacl -cp " . $tester->getListen('{{ADDR:UDS}}'));
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECTF--
user::rw-
user:%s:rw-
user:%s:rw-
user:%s:rw-
group::---
group:%s:rw-
group:%s:rw-
mask::rw-
other::---
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,40 @@
--TEST--
FPM: Unix Domain Socket connection
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR:UDS}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->ping('{{ADDR:UDS}}');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -0,0 +1,50 @@
--TEST--
FPM: Status basic test
--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
pm.status_path = /status
EOT;
$expectedStatusData = [
'pool' => 'unconfined',
'process manager' => 'static',
'listen queue' => 0,
'max listen queue' => 0,
'idle processes' => 0,
'active processes' => 1,
'total processes' => 1,
'max active processes' => 1,
'max children reached' => 0,
'slow requests' => 0,
];
$tester = new FPM\Tester($cfg);
$tester->start();
$tester->expectLogStartNotices();
$tester->request()->expectEmptyBody();
$tester->status($expectedStatusData);
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

199
sapi/fpm/tests/status.inc Normal file
View File

@ -0,0 +1,199 @@
<?php
namespace FPM;
class Status
{
const HTML_TITLE = 'PHP-FPM Status Page';
/**
* @var array
*/
private $contentTypes = [
'plain' => 'text/plain',
'html' => 'text/html',
'xml' => 'text/xml',
'json' => 'application/json',
];
/**
* @var array
*/
private $defaultFields = [
'pool' => '\w+',
'process manager' => '(static|dynamic|ondemand)',
'start time' => '\d+\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2}\s[+-]\d{4}',
'start since' => '\d+',
'accepted conn' => '\d+',
'listen queue' => '\d+',
'max listen queue' => '\d+',
'listen queue len' => '\d+',
'idle processes' => '\d+',
'active processes' => '\d+',
'total processes' => '\d+',
'max active processes' => '\d+',
'max children reached' => '\d+',
'slow requests' => '\d+',
];
/**
* Check status page.
*
* @param Response $response
* @param array $fields
* @param string $type
* @throws \Exception
*/
public function checkStatus(Response $response, array $fields, string $type)
{
if (!isset($this->contentTypes[$type])) {
throw new \Exception('Invalid content type ' . $type);
}
$body = $response->getBody($this->contentTypes[$type]);
if ($body === null) {
return;
}
$method = "checkStatus" . ucfirst($type);
$this->$method($body, array_merge($this->defaultFields, $fields));
}
/**
* Make status check for status page.
*
* @param string $body
* @param array $fields
* @param string $rowPattern
* @param string $header
* @param string $footer
* @param null|callable $nameTransformer
* @param null|callable $valueTransformer
* @param bool $startTimeTimestamp
* @param bool $closingName
*/
private function makeStatusCheck(
string $body,
array $fields,
string $rowPattern,
string $header = '',
string $footer = '',
$nameTransformer = null,
$valueTransformer = null,
bool $startTimeTimestamp = false,
bool $closingName = false
) {
if ($startTimeTimestamp && $fields['start time'][0] === '\\') {
$fields['start time'] = '\d+';
}
$pattern = '|' . $header;
foreach ($fields as $name => $value) {
if ($nameTransformer) {
$name = call_user_func($nameTransformer, $name);
}
if ($valueTransformer) {
$value = call_user_func($valueTransformer, $value);
}
if ($closingName) {
$pattern .= sprintf($rowPattern, $name, $value, $name);
} else {
$pattern .= sprintf($rowPattern, $name, $value);
}
}
$pattern = rtrim($pattern, $rowPattern[strlen($rowPattern) - 1]);
$pattern .= $footer . '|';
if (!preg_match($pattern, $body)) {
echo "ERROR: Expected body does not match pattern\n";
echo "BODY:\n";
var_dump($body);
echo "PATTERN:\n";
var_dump($pattern);
}
}
/**
* Check plain status page.
*
* @param string $body
* @param array $fields
*/
protected function checkStatusPlain(string $body, array $fields)
{
$this->makeStatusCheck($body, $fields, "%s:\s+%s\n");
}
/**
* Check html status page.
*
* @param string $body
* @param array $fields
*/
protected function checkStatusHtml(string $body, array $fields)
{
$header = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " .
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" .
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" .
"<head><title>" . self::HTML_TITLE . "</title></head>\n" .
"<body>\n<table>\n";
$footer = "\n</table>\n</body></html>";
$this->makeStatusCheck(
$body,
$fields,
"<tr><th>%s</th><td>%s</td></tr>\n",
$header,
$footer
);
}
/**
* Check xml status page.
*
* @param string $body
* @param array $fields
*/
protected function checkStatusXml(string $body, array $fields)
{
$this->makeStatusCheck(
$body,
$fields,
"<%s>%s</%s>\n",
"<\?xml version=\"1.0\" \?>\n<status>\n",
"\n</status>",
function ($name) {
return str_replace(' ', '-', $name);
},
null,
true,
true
);
}
/**
* Check json status page.
*
* @param string $body
* @param array $fields
*/
protected function checkStatusJson(string $body, array $fields)
{
$this->makeStatusCheck(
$body,
$fields,
'"%s":%s,',
'{',
'}',
null,
function ($value) {
if (is_numeric($value) || $value === '\d+') {
return $value;
}
return '"' . $value . '"';
},
true
);
}
}

1183
sapi/fpm/tests/tester.inc Normal file

File diff suppressed because it is too large Load Diff