summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbyr Valg <valga@abyrga.ru>2018-04-29 12:05:59 +0300
committerJakub Zelenka <bukka@php.net>2019-01-25 14:09:55 +0000
commit0c84c2ef503a531b43fa5f0f6b30e0d220c72e28 (patch)
tree5ce89289f596967dafc3d58226f8cd764400d138
parent08c5679380115962c31f3df0bc317eb2018eb514 (diff)
downloadphp-git-0c84c2ef503a531b43fa5f0f6b30e0d220c72e28.tar.gz
Add a test for fragmented SSL packets
-rw-r--r--ext/openssl/tests/ServerClientProxyTestCase.inc117
-rw-r--r--ext/openssl/tests/non_blocking_eof.phpt99
2 files changed, 216 insertions, 0 deletions
diff --git a/ext/openssl/tests/ServerClientProxyTestCase.inc b/ext/openssl/tests/ServerClientProxyTestCase.inc
new file mode 100644
index 0000000000..c55159f52a
--- /dev/null
+++ b/ext/openssl/tests/ServerClientProxyTestCase.inc
@@ -0,0 +1,117 @@
+<?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/non_blocking_eof.phpt b/ext/openssl/tests/non_blocking_eof.phpt
new file mode 100644
index 0000000000..86b3815153
--- /dev/null
+++ b/ext/openssl/tests/non_blocking_eof.phpt
@@ -0,0 +1,99 @@
+--TEST--
+php_stream_eof() should not block on SSL non-blocking streams when packets are fragmented
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip openssl not loaded");
+if (!function_exists("proc_open")) die("skip no proc_open");
+?>
+--FILE--
+<?php
+
+$clientCode = <<<'CODE'
+ $context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => 'bug54992.local']]);
+
+ phpt_wait('server');
+ phpt_notify('proxy');
+
+ phpt_wait('proxy');
+ $fp = stream_socket_client("ssl://127.0.0.1:10012", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context);
+ stream_set_blocking($fp, false);
+
+ $read = [$fp];
+ $buf = '';
+ while (stream_select($read, $write, $except, 1000)) {
+ $chunk = stream_get_contents($fp, 4096);
+ var_dump($chunk);
+ $buf .= $chunk;
+ if ($buf === 'hello, world') {
+ break;
+ }
+ }
+
+ phpt_notify('server');
+ phpt_notify('proxy');
+CODE;
+
+$serverCode = <<<'CODE'
+ $context = stream_context_create(['ssl' => ['local_cert' => __DIR__ . '/bug54992.pem']]);
+
+ $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
+ $fp = stream_socket_server("ssl://127.0.0.1:10011", $errornum, $errorstr, $flags, $context);
+ phpt_notify();
+
+ $conn = stream_socket_accept($fp);
+ fwrite($conn, 'hello, world');
+
+ phpt_wait();
+ fclose($conn);
+CODE;
+
+$proxyCode = <<<'CODE'
+ phpt_wait();
+
+ $upstream = stream_socket_client("tcp://127.0.0.1:10011", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT);
+ stream_set_blocking($upstream, false);
+
+ $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
+ $server = stream_socket_server("tcp://127.0.0.1:10012", $errornum, $errorstr, $flags);
+ phpt_notify();
+
+ $conn = stream_socket_accept($server);
+ stream_set_blocking($conn, false);
+
+ $read = [$upstream, $conn];
+ while (stream_select($read, $write, $except, 1)) {
+ foreach ($read as $fp) {
+ $data = stream_get_contents($fp);
+ if ($fp === $conn) {
+ fwrite($upstream, $data);
+ } else {
+ if ($data !== '' && $data[0] === chr(23)) {
+ $parts = str_split($data, (int) ceil(strlen($data) / 3));
+ foreach ($parts as $part) {
+ fwrite($conn, $part);
+ usleep(1000);
+ }
+ } else {
+ fwrite($conn, $data);
+ }
+ }
+ }
+ if (feof($upstream)) {
+ break;
+ }
+ $read = [$upstream, $conn];
+ }
+
+ phpt_wait();
+CODE;
+
+include 'ServerClientProxyTestCase.inc';
+ServerClientProxyTestCase::getInstance()->run($clientCode, [
+ 'server' => $serverCode,
+ 'proxy' => $proxyCode,
+]);
+?>
+--EXPECT--
+string(0) ""
+string(0) ""
+string(12) "hello, world"