From d9b29029f9793ffdefbd575dd26cc54c78c0a1cf Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Wed, 16 Jan 2019 18:14:30 +0000
Subject: [PATCH] Update and integrate openssl client proxy test

---
 .../tests/ServerClientProxyTestCase.inc       | 117 ------------------
 ext/openssl/tests/ServerClientTestCase.inc    |  80 +++++++-----
 .../{non_blocking_eof.phpt => bug77390.phpt}  |  25 +++-
 3 files changed, 72 insertions(+), 150 deletions(-)
 delete mode 100644 ext/openssl/tests/ServerClientProxyTestCase.inc
 rename ext/openssl/tests/{non_blocking_eof.phpt => bug77390.phpt} (71%)

diff --git a/ext/openssl/tests/ServerClientProxyTestCase.inc b/ext/openssl/tests/ServerClientProxyTestCase.inc
deleted file mode 100644
index c55159f52a..0000000000
--- a/ext/openssl/tests/ServerClientProxyTestCase.inc
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-const WORKER_ARGV_VALUE = 'RUN_WORKER';
-
-function phpt_notify($worker = null)
-{
-	ServerClientProxyTestCase::getInstance()->notify($worker);
-}
-
-function phpt_wait($worker = null)
-{
-	ServerClientProxyTestCase::getInstance()->wait($worker);
-}
-
-/**
- * This is a singleton to let the wait/notify functions work
- * I know it's horrible, but it's a means to an end
- */
-class ServerClientProxyTestCase
-{
-	private $isWorker = false;
-
-	private $workerHandles = [];
-
-	private $workerStdIn = [];
-
-	private $workerStdOut = [];
-
-	private static $instance;
-
-	public static function getInstance($isWorker = false)
-	{
-		if (!isset(self::$instance)) {
-			self::$instance = new self($isWorker);
-		}
-
-		return self::$instance;
-	}
-
-	public function __construct($isWorker = false)
-	{
-		if (!isset(self::$instance)) {
-			self::$instance = $this;
-		}
-
-		$this->isWorker = $isWorker;
-	}
-
-	private function spawnWorkerProcess($worker, $code)
-	{
-		if (defined("PHP_WINDOWS_VERSION_MAJOR")) {
-				$ini = php_ini_loaded_file();
-				$cmd = sprintf('%s %s "%s" %s', PHP_BINARY, $ini ? "-n -c $ini" : "", __FILE__, WORKER_ARGV_VALUE);
-		} else {
-				$cmd = sprintf('%s "%s" %s %s', PHP_BINARY, __FILE__, WORKER_ARGV_VALUE, $worker);
-		}
-		$this->workerHandle[$worker] = proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], STDERR], $pipes);
-		$this->workerStdIn[$worker] = $pipes[0];
-		$this->workerStdOut[$worker] = $pipes[1];
-
-		fwrite($this->workerStdIn[$worker], $code . "\n---\n");
-	}
-
-	private function cleanupWorkerProcess($worker)
-	{
-		fclose($this->workerStdIn[$worker]);
-		fclose($this->workerStdOut[$worker]);
-		proc_close($this->workerHandle[$worker]);
-	}
-
-	private function stripPhpTagsFromCode($code)
-	{
-		return preg_replace('/^\s*<\?(?:php)?|\?>\s*$/i', '', $code);
-	}
-
-	public function runWorker()
-	{
-		$code = '';
-
-		while (1) {
-			$line = fgets(STDIN);
-
-			if (trim($line) === "---") {
-				break;
-			}
-
-			$code .= $line;
-		}
-
-		eval($code);
-	}
-
-	public function run($testCode, array $workerCodes)
-	{
-		foreach ($workerCodes as $worker => $code) {
-			$this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code));
-		}
-		eval($this->stripPhpTagsFromCode($testCode));
-		foreach ($workerCodes as $worker => $code) {
-			$this->cleanupWorkerProcess($worker);
-		}
-	}
-
-	public function wait($worker)
-	{
-		fgets($this->isWorker ? STDIN : $this->workerStdOut[$worker]);
-	}
-
-	public function notify($worker)
-	{
-		fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n");
-	}
-}
-
-if (isset($argv[1]) && $argv[1] === WORKER_ARGV_VALUE) {
-	ServerClientProxyTestCase::getInstance(true)->runWorker();
-}
diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc
index f0e40fa535..4bad3c2995 100644
--- a/ext/openssl/tests/ServerClientTestCase.inc
+++ b/ext/openssl/tests/ServerClientTestCase.inc
@@ -2,14 +2,16 @@
 
 const WORKER_ARGV_VALUE = 'RUN_WORKER';
 
-function phpt_notify()
+const WORKER_DEFAULT_NAME = 'server';
+
+function phpt_notify($worker = WORKER_DEFAULT_NAME)
 {
-    ServerClientTestCase::getInstance()->notify();
+    ServerClientTestCase::getInstance()->notify($worker);
 }
 
-function phpt_wait()
+function phpt_wait($worker = WORKER_DEFAULT_NAME)
 {
-    ServerClientTestCase::getInstance()->wait();
+    ServerClientTestCase::getInstance()->wait($worker);
 }
 
 /**
@@ -20,11 +22,11 @@ class ServerClientTestCase
 {
     private $isWorker = false;
 
-    private $workerHandle;
+    private $workerHandle = [];
 
-    private $workerStdIn;
+    private $workerStdIn = [];
 
-    private $workerStdOut;
+    private $workerStdOut = [];
 
     private static $instance;
 
@@ -46,26 +48,41 @@ class ServerClientTestCase
         $this->isWorker = $isWorker;
     }
 
-    private function spawnWorkerProcess($code)
+    private function spawnWorkerProcess($worker, $code)
     {
         if (defined("PHP_WINDOWS_VERSION_MAJOR")) {
-                $ini = php_ini_loaded_file();
-                $cmd = sprintf('%s %s "%s" %s', PHP_BINARY, $ini ? "-n -c $ini" : "", __FILE__, WORKER_ARGV_VALUE);
+            $ini = php_ini_loaded_file();
+            $cmd = sprintf(
+                '%s %s "%s" %s',
+                PHP_BINARY, $ini ? "-n -c $ini" : "",
+                __FILE__,
+                WORKER_ARGV_VALUE
+            );
         } else {
-                $cmd = sprintf('%s "%s" %s', PHP_BINARY, __FILE__, WORKER_ARGV_VALUE);
+            $cmd = sprintf(
+                '%s "%s" %s %s',
+                PHP_BINARY,
+                __FILE__,
+                WORKER_ARGV_VALUE,
+                $worker
+            );
         }
-        $this->workerHandle = proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], STDERR], $pipes);
-        $this->workerStdIn = $pipes[0];
-        $this->workerStdOut = $pipes[1];
-
-        fwrite($this->workerStdIn, $code . "\n---\n");
+        $this->workerHandle[$worker] = proc_open(
+            $cmd,
+            [['pipe', 'r'], ['pipe', 'w'], STDERR],
+            $pipes
+        );
+        $this->workerStdIn[$worker] = $pipes[0];
+        $this->workerStdOut[$worker] = $pipes[1];
+
+        fwrite($this->workerStdIn[$worker], $code . "\n---\n");
     }
 
-    private function cleanupWorkerProcess()
+    private function cleanupWorkerProcess($worker)
     {
-        fclose($this->workerStdIn);
-        fclose($this->workerStdOut);
-        proc_close($this->workerHandle);
+        fclose($this->workerStdIn[$worker]);
+        fclose($this->workerStdOut[$worker]);
+        proc_close($this->workerHandle[$worker]);
     }
 
     private function stripPhpTagsFromCode($code)
@@ -90,21 +107,28 @@ class ServerClientTestCase
         eval($code);
     }
 
-    public function run($proc1Code, $proc2Code)
+    public function run($masterCode, $workerCode)
     {
-        $this->spawnWorkerProcess($this->stripPhpTagsFromCode($proc2Code));
-        eval($this->stripPhpTagsFromCode($proc1Code));
-        $this->cleanupWorkerProcess();
+        if (!is_array($workerCode)) {
+            $workerCode = [WORKER_DEFAULT_NAME => $workerCode];
+        }
+        foreach ($workerCode as $worker => $code) {
+            $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code));
+        }
+        eval($this->stripPhpTagsFromCode($masterCode));
+        foreach ($workerCode as $worker => $code) {
+            $this->cleanupWorkerProcess($worker);
+        }
     }
 
-    public function wait()
+    public function wait($worker)
     {
-        fgets($this->isWorker ? STDIN : $this->workerStdOut);
+        fgets($this->isWorker ? STDIN : $this->workerStdOut[$worker]);
     }
 
-    public function notify()
+    public function notify($worker)
     {
-        fwrite($this->isWorker ? STDOUT : $this->workerStdIn, "\n");
+        fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n");
     }
 }
 
diff --git a/ext/openssl/tests/non_blocking_eof.phpt b/ext/openssl/tests/bug77390.phpt
similarity index 71%
rename from ext/openssl/tests/non_blocking_eof.phpt
rename to ext/openssl/tests/bug77390.phpt
index 86b3815153..d746a5d4aa 100644
--- a/ext/openssl/tests/non_blocking_eof.phpt
+++ b/ext/openssl/tests/bug77390.phpt
@@ -1,5 +1,5 @@
 --TEST--
-php_stream_eof() should not block on SSL non-blocking streams when packets are fragmented
+Bug #76705: feof might hang on TLS streams in case of fragmented TLS records
 --SKIPIF--
 <?php
 if (!extension_loaded("openssl")) die("skip openssl not loaded");
@@ -7,9 +7,12 @@ if (!function_exists("proc_open")) die("skip no proc_open");
 ?>
 --FILE--
 <?php
+$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug77390.pem.tmp';
+$cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug77390-ca.pem.tmp';
 
+$peerName = 'bug77390';
 $clientCode = <<<'CODE'
-	$context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => 'bug54992.local']]);
+	$context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => '%s']]);
 
 	phpt_wait('server');
 	phpt_notify('proxy');
@@ -32,9 +35,10 @@ $clientCode = <<<'CODE'
 	phpt_notify('server');
 	phpt_notify('proxy');
 CODE;
+$clientCode = sprintf($clientCode, $peerName);
 
 $serverCode = <<<'CODE'
-	$context = stream_context_create(['ssl' => ['local_cert' => __DIR__ . '/bug54992.pem']]);
+	$context = stream_context_create(['ssl' => ['local_cert' => '%s']]);
 
 	$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
 	$fp = stream_socket_server("ssl://127.0.0.1:10011", $errornum, $errorstr, $flags, $context);
@@ -46,6 +50,7 @@ $serverCode = <<<'CODE'
 	phpt_wait();
 	fclose($conn);
 CODE;
+$serverCode = sprintf($serverCode, $certFile);
 
 $proxyCode = <<<'CODE'
 	phpt_wait();
@@ -87,12 +92,22 @@ $proxyCode = <<<'CODE'
 	phpt_wait();
 CODE;
 
-include 'ServerClientProxyTestCase.inc';
-ServerClientProxyTestCase::getInstance()->run($clientCode, [
+include 'CertificateGenerator.inc';
+$certificateGenerator = new CertificateGenerator();
+$certificateGenerator->saveCaCert($cacertFile);
+$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile);
+
+include 'ServerClientTestCase.inc';
+ServerClientTestCase::getInstance()->run($clientCode, [
 	'server' => $serverCode,
 	'proxy' => $proxyCode,
 ]);
 ?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'bug77390.pem.tmp');
+@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'bug77390-ca.pem.tmp');
+?>
 --EXPECT--
 string(0) ""
 string(0) ""
-- 
2.40.0