diff --git a/sapi/fpm/tests/002.phpt b/sapi/fpm/tests/002.phpt deleted file mode 100644 index 5ad9e4bd5cb..00000000000 --- a/sapi/fpm/tests/002.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -FPM: Startup and connect ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/003.phpt b/sapi/fpm/tests/003.phpt deleted file mode 100644 index 8accbd0d101..00000000000 --- a/sapi/fpm/tests/003.phpt +++ /dev/null @@ -1,54 +0,0 @@ ---TEST-- -FPM: Test IPv6 support ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/004.phpt b/sapi/fpm/tests/004.phpt deleted file mode 100644 index 4375f8d0d03..00000000000 --- a/sapi/fpm/tests/004.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -FPM: Test IPv4/IPv6 support ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/005.phpt b/sapi/fpm/tests/005.phpt deleted file mode 100644 index 6c8210ec8e7..00000000000 --- a/sapi/fpm/tests/005.phpt +++ /dev/null @@ -1,60 +0,0 @@ ---TEST-- -FPM: Test IPv4 allowed clients ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/006.phpt b/sapi/fpm/tests/006.phpt deleted file mode 100644 index e5520873359..00000000000 --- a/sapi/fpm/tests/006.phpt +++ /dev/null @@ -1,60 +0,0 @@ ---TEST-- -FPM: Test IPv6 allowed clients (bug #68428) ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/007.phpt b/sapi/fpm/tests/007.phpt deleted file mode 100644 index 6329af209ad..00000000000 --- a/sapi/fpm/tests/007.phpt +++ /dev/null @@ -1,71 +0,0 @@ ---TEST-- -FPM: Test IPv6 all addresses and access_log (bug #68421) ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/008.phpt b/sapi/fpm/tests/008.phpt deleted file mode 100644 index d5fe05ba761..00000000000 --- a/sapi/fpm/tests/008.phpt +++ /dev/null @@ -1,100 +0,0 @@ ---TEST-- -FPM: Test multi pool (dynamic + ondemand + static) (bug #68423) ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/009.phpt b/sapi/fpm/tests/009.phpt deleted file mode 100644 index 34cdffcf833..00000000000 --- a/sapi/fpm/tests/009.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -FPM: Test Unix Domain Socket ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/010.phpt b/sapi/fpm/tests/010.phpt deleted file mode 100644 index 49e1a079230..00000000000 --- a/sapi/fpm/tests/010.phpt +++ /dev/null @@ -1,87 +0,0 @@ ---TEST-- -FPM: Test status page ---SKIPIF-- - ---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-- - ---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-- - diff --git a/sapi/fpm/tests/011.phpt b/sapi/fpm/tests/011.phpt deleted file mode 100644 index 0b849f873ba..00000000000 --- a/sapi/fpm/tests/011.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -FPM: Test IPv4 all addresses (bug #68420) ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/012.phpt b/sapi/fpm/tests/012.phpt deleted file mode 100644 index d96c53081c2..00000000000 --- a/sapi/fpm/tests/012.phpt +++ /dev/null @@ -1,79 +0,0 @@ ---TEST-- -FPM: Test reload configuration (bug #68442) ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/013.phpt b/sapi/fpm/tests/013.phpt deleted file mode 100644 index d28f3e4e009..00000000000 --- a/sapi/fpm/tests/013.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -FPM: Test for log_level in fpm_unix_init_main #68381 ---SKIPIF-- - ---FILE-- - -Done ---EXPECTF-- -Started -Done ---CLEAN-- - diff --git a/sapi/fpm/tests/014.phpt b/sapi/fpm/tests/014.phpt deleted file mode 100644 index 56e24715a8f..00000000000 --- a/sapi/fpm/tests/014.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -FPM: Test for pm.start_servers default calculation message being a notice and not a warning #68458 ---SKIPIF-- - ---FILE-- - -Done ---EXPECTF-- -Started -Done ---CLEAN-- - diff --git a/sapi/fpm/tests/015.phpt b/sapi/fpm/tests/015.phpt deleted file mode 100644 index a3c7ad3eee8..00000000000 --- a/sapi/fpm/tests/015.phpt +++ /dev/null @@ -1,89 +0,0 @@ ---TEST-- -FPM: Test various messages on start, from master and childs ---SKIPIF-- - ---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-- - -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-- - diff --git a/sapi/fpm/tests/016.phpt b/sapi/fpm/tests/016.phpt deleted file mode 100644 index 04ba4b5f694..00000000000 --- a/sapi/fpm/tests/016.phpt +++ /dev/null @@ -1,101 +0,0 @@ ---TEST-- -FPM: Test splited configuration and load order #68391 ---SKIPIF-- - ---FILE-- - -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-- - diff --git a/sapi/fpm/tests/017.phpt b/sapi/fpm/tests/017.phpt deleted file mode 100644 index 46e5efd3f75..00000000000 --- a/sapi/fpm/tests/017.phpt +++ /dev/null @@ -1,67 +0,0 @@ ---TEST-- -FPM: Test fastcgi_finish_request function ---SKIPIF-- - ---FILE-- - -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-- - diff --git a/sapi/fpm/tests/019.phpt b/sapi/fpm/tests/019.phpt deleted file mode 100644 index 7bed9e2436e..00000000000 --- a/sapi/fpm/tests/019.phpt +++ /dev/null @@ -1,79 +0,0 @@ ---TEST-- -FPM: Test global prefix ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/020.phpt b/sapi/fpm/tests/020.phpt deleted file mode 100644 index 81317500152..00000000000 --- a/sapi/fpm/tests/020.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -FPM: Test pool prefix ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/021-uds-acl.phpt b/sapi/fpm/tests/021-uds-acl.phpt deleted file mode 100644 index 6e9ec08d8b2..00000000000 --- a/sapi/fpm/tests/021-uds-acl.phpt +++ /dev/null @@ -1,102 +0,0 @@ ---TEST-- -FPM: Test Unix Domain Socket with Posix ACL ---SKIPIF-- - ---FILE-- - ---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-- - diff --git a/sapi/fpm/tests/022-cve-2016-5385.phpt b/sapi/fpm/tests/022-cve-2016-5385.phpt deleted file mode 100644 index 0bdf238f30e..00000000000 --- a/sapi/fpm/tests/022-cve-2016-5385.phpt +++ /dev/null @@ -1,81 +0,0 @@ ---TEST-- -FPM: HTTP_PROXY - CVE-2016-5385 ---SKIPIF-- - ---FILE-- - '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-- - diff --git a/sapi/fpm/tests/bug68381-log-level-warning.phpt b/sapi/fpm/tests/bug68381-log-level-warning.phpt new file mode 100644 index 00000000000..8d4a9af4cc5 --- /dev/null +++ b/sapi/fpm/tests/bug68381-log-level-warning.phpt @@ -0,0 +1,40 @@ +--TEST-- +FPM: bug68381 - Log messages with warning level only +--SKIPIF-- + +--FILE-- +start(); +$tester->checkConnection(); +$tester->terminate(); +$tester->expectNoLogMessages(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/bug68391-conf-include-order.phpt b/sapi/fpm/tests/bug68391-conf-include-order.phpt new file mode 100644 index 00000000000..012a978f298 --- /dev/null +++ b/sapi/fpm/tests/bug68391-conf-include-order.phpt @@ -0,0 +1,53 @@ +--TEST-- +FPM: bug68391 - Configuration inclusion in alphabetical order +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/bug68420-ipv4-all-addresses.phpt b/sapi/fpm/tests/bug68420-ipv4-all-addresses.phpt new file mode 100644 index 00000000000..9a4692d17c1 --- /dev/null +++ b/sapi/fpm/tests/bug68420-ipv4-all-addresses.phpt @@ -0,0 +1,42 @@ +--TEST-- +FPM: bug68420 - IPv4 all addresses +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->ping('127.0.0.1'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/bug68421-ipv6-access-log.phpt b/sapi/fpm/tests/bug68421-ipv6-access-log.phpt new file mode 100644 index 00000000000..80c115c17f9 --- /dev/null +++ b/sapi/fpm/tests/bug68421-ipv6-access-log.phpt @@ -0,0 +1,47 @@ +--TEST-- +FPM: bug68421 - IPv6 all addresses and access_log +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/bug68423-multi-pool-all-pms.phpt b/sapi/fpm/tests/bug68423-multi-pool-all-pms.phpt new file mode 100644 index 00000000000..ae6b48351a0 --- /dev/null +++ b/sapi/fpm/tests/bug68423-multi-pool-all-pms.phpt @@ -0,0 +1,55 @@ +--TEST-- +FPM: bug68423 - Multiple pools with different PMs (dynamic + ondemand + static) +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/bug68428-ipv6-allowed-clients.phpt b/sapi/fpm/tests/bug68428-ipv6-allowed-clients.phpt new file mode 100644 index 00000000000..0998cf0acaf --- /dev/null +++ b/sapi/fpm/tests/bug68428-ipv6-allowed-clients.phpt @@ -0,0 +1,45 @@ +--TEST-- +FPM: bug68428 - IPv6 allowed client only +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/bug68442-signal-reload.phpt b/sapi/fpm/tests/bug68442-signal-reload.phpt new file mode 100644 index 00000000000..d15c8e14e76 --- /dev/null +++ b/sapi/fpm/tests/bug68442-signal-reload.phpt @@ -0,0 +1,47 @@ +--TEST-- +FPM: bug68442 - Signal reload +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/bug68458-pm-no-start-server.phpt b/sapi/fpm/tests/bug68458-pm-no-start-server.phpt new file mode 100644 index 00000000000..c0c69b64b99 --- /dev/null +++ b/sapi/fpm/tests/bug68458-pm-no-start-server.phpt @@ -0,0 +1,42 @@ +--TEST-- +FPM: bug68458 - Missing pm.start_servers should emit notice instead of warning +--SKIPIF-- + +--FILE-- +start(); +$tester->checkConnection(); +$tester->terminate(); +$tester->expectNoLogMessages(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/bug72573-http-proxy.phpt b/sapi/fpm/tests/bug72573-http-proxy.phpt new file mode 100644 index 00000000000..ffa60d97134 --- /dev/null +++ b/sapi/fpm/tests/bug72573-http-proxy.phpt @@ -0,0 +1,66 @@ +--TEST-- +FPM: bug72573 - HTTP_PROXY - CVE-2016-5385 +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/fastcgi_finish_request_basic.phpt b/sapi/fpm/tests/fastcgi_finish_request_basic.phpt new file mode 100644 index 00000000000..939782fa312 --- /dev/null +++ b/sapi/fpm/tests/fastcgi_finish_request_basic.phpt @@ -0,0 +1,45 @@ +--TEST-- +FPM: Function fastcgi_finish_request basic test +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->request()->expectBody("Test Start"); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/fcgi.inc b/sapi/fpm/tests/fcgi.inc index b31676260da..71bdad17b95 100644 --- a/sapi/fpm/tests/fcgi.inc +++ b/sapi/fpm/tests/fcgi.inc @@ -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']; + } } diff --git a/sapi/fpm/tests/include.inc b/sapi/fpm/tests/include.inc deleted file mode 100644 index dd2e69b9280..00000000000 --- a/sapi/fpm/tests/include.inc +++ /dev/null @@ -1,135 +0,0 @@ - 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"; -} -/* }}} */ diff --git a/sapi/fpm/tests/logtool.inc b/sapi/fpm/tests/logtool.inc new file mode 100644 index 00000000000..219c6fedbb8 --- /dev/null +++ b/sapi/fpm/tests/logtool.inc @@ -0,0 +1,476 @@ +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"; +} diff --git a/sapi/fpm/tests/main-global-prefix.phpt b/sapi/fpm/tests/main-global-prefix.phpt new file mode 100644 index 00000000000..710e688c408 --- /dev/null +++ b/sapi/fpm/tests/main-global-prefix.phpt @@ -0,0 +1,50 @@ +--TEST-- +FPM: Main invocation with prefix +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/001.phpt b/sapi/fpm/tests/main-version.phpt similarity index 81% rename from sapi/fpm/tests/001.phpt rename to sapi/fpm/tests/main-version.phpt index b721bfa9254..6e42aae48f7 100644 --- a/sapi/fpm/tests/001.phpt +++ b/sapi/fpm/tests/main-version.phpt @@ -5,9 +5,9 @@ FPM: version string --FILE-- --FILE-- 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-- +require_once "tester.inc"; +FPM\Tester::clean(); +?> \ No newline at end of file diff --git a/sapi/fpm/tests/pool-prefix.phpt b/sapi/fpm/tests/pool-prefix.phpt new file mode 100644 index 00000000000..b32b37e13a6 --- /dev/null +++ b/sapi/fpm/tests/pool-prefix.phpt @@ -0,0 +1,54 @@ +--TEST-- +FPM: Pool prefix +--SKIPIF-- + +--FILE-- +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-- + \ No newline at end of file diff --git a/sapi/fpm/tests/proc-no-start-server.phpt b/sapi/fpm/tests/proc-no-start-server.phpt new file mode 100644 index 00000000000..82f10727b68 --- /dev/null +++ b/sapi/fpm/tests/proc-no-start-server.phpt @@ -0,0 +1,45 @@ +--TEST-- +FPM: Process manager config option pm.start_servers missing +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/proc-user-ignored.phpt b/sapi/fpm/tests/proc-user-ignored.phpt new file mode 100644 index 00000000000..42a7dc2392b --- /dev/null +++ b/sapi/fpm/tests/proc-user-ignored.phpt @@ -0,0 +1,46 @@ +--TEST-- +FPM: Process user setting ignored when FPM is not running as root +--SKIPIF-- + +--FILE-- +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-- + \ No newline at end of file diff --git a/sapi/fpm/tests/response.inc b/sapi/fpm/tests/response.inc new file mode 100644 index 00000000000..9888ec1b83f --- /dev/null +++ b/sapi/fpm/tests/response.inc @@ -0,0 +1,281 @@ + $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; + } +} \ No newline at end of file diff --git a/sapi/fpm/tests/skipapparmor.inc b/sapi/fpm/tests/skipapparmor.inc deleted file mode 100644 index b286d0361dc..00000000000 --- a/sapi/fpm/tests/skipapparmor.inc +++ /dev/null @@ -1,30 +0,0 @@ - diff --git a/sapi/fpm/tests/skipif.inc b/sapi/fpm/tests/skipif.inc index 08c6bbff69d..25910a8e05e 100644 --- a/sapi/fpm/tests/skipif.inc +++ b/sapi/fpm/tests/skipif.inc @@ -1,17 +1,15 @@ +if (!FPM\Tester::findExecutable()) { + die("skip php-fpm binary not found"); +} \ No newline at end of file diff --git a/sapi/fpm/tests/socket-invalid-allowed-clients.phpt b/sapi/fpm/tests/socket-invalid-allowed-clients.phpt new file mode 100644 index 00000000000..b2240687fb7 --- /dev/null +++ b/sapi/fpm/tests/socket-invalid-allowed-clients.phpt @@ -0,0 +1,48 @@ +--TEST-- +FPM: Socket for invalid allowed client only +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/socket-ipv4-allowed-clients.phpt b/sapi/fpm/tests/socket-ipv4-allowed-clients.phpt new file mode 100644 index 00000000000..3de7c0049b5 --- /dev/null +++ b/sapi/fpm/tests/socket-ipv4-allowed-clients.phpt @@ -0,0 +1,45 @@ +--TEST-- +FPM: Socket for IPv4 allowed client only +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/socket-ipv4-basic.phpt b/sapi/fpm/tests/socket-ipv4-basic.phpt new file mode 100644 index 00000000000..5cce244d10d --- /dev/null +++ b/sapi/fpm/tests/socket-ipv4-basic.phpt @@ -0,0 +1,37 @@ +--TEST-- +FPM: Socket for IPv4 connection +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/socket-ipv6-any.phpt b/sapi/fpm/tests/socket-ipv6-any.phpt new file mode 100644 index 00000000000..11441acddbc --- /dev/null +++ b/sapi/fpm/tests/socket-ipv6-any.phpt @@ -0,0 +1,44 @@ +--TEST-- +FPM: Socket for IPv6 any address connection +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/socket-ipv6-basic.phpt b/sapi/fpm/tests/socket-ipv6-basic.phpt new file mode 100644 index 00000000000..b91dc19718f --- /dev/null +++ b/sapi/fpm/tests/socket-ipv6-basic.phpt @@ -0,0 +1,40 @@ +--TEST-- +FPM: Socket for IPv6 connection +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/socket-uds-acl.phpt b/sapi/fpm/tests/socket-uds-acl.phpt new file mode 100644 index 00000000000..6423ae446ce --- /dev/null +++ b/sapi/fpm/tests/socket-uds-acl.phpt @@ -0,0 +1,87 @@ +--TEST-- +FPM: Unix Domain Socket with Posix ACL +--SKIPIF-- + +--FILE-- +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-- + \ No newline at end of file diff --git a/sapi/fpm/tests/socket-uds-basic.phpt b/sapi/fpm/tests/socket-uds-basic.phpt new file mode 100644 index 00000000000..b22f3384f9d --- /dev/null +++ b/sapi/fpm/tests/socket-uds-basic.phpt @@ -0,0 +1,40 @@ +--TEST-- +FPM: Unix Domain Socket connection +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->ping('{{ADDR:UDS}}'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/status-basic.phpt b/sapi/fpm/tests/status-basic.phpt new file mode 100644 index 00000000000..323592262f1 --- /dev/null +++ b/sapi/fpm/tests/status-basic.phpt @@ -0,0 +1,50 @@ +--TEST-- +FPM: Status basic test +--SKIPIF-- + +--FILE-- + '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-- + diff --git a/sapi/fpm/tests/status.inc b/sapi/fpm/tests/status.inc new file mode 100644 index 00000000000..5965f130eb2 --- /dev/null +++ b/sapi/fpm/tests/status.inc @@ -0,0 +1,199 @@ + '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 = "\n" . + "\n" . + "