add http/2 support client-side, and tests
authorgggeek <giunta.gaetano@gmail.com>
Wed, 25 May 2022 14:15:21 +0000 (14:15 +0000)
committergggeek <giunta.gaetano@gmail.com>
Wed, 25 May 2022 14:15:21 +0000 (14:15 +0000)
src/Client.php
src/PhpXmlRpc.php
tests/6HTTPTest.php
tests/ci/config/apache_vhost
tests/ci/setup/setup_apache.sh

index cd1491c..08a9a19 100644 (file)
@@ -140,8 +140,8 @@ class Client
      * @param string $server the server name / ip address
      * @param integer $port the port the server is listening on, when omitted defaults to 80 or 443 depending on
      *                      protocol used
-     * @param string $method the http protocol variant: defaults to 'http'; 'https' and 'http11' can be used if CURL is
-     *                       installed. The value set here can be overridden in any call to $this->send().
+     * @param string $method the http protocol variant: defaults to 'http'; 'https', 'http11', 'http2' and 'http2tls' can
+     *                       be used if CURL is installed. The value set here can be overridden in any call to $this->send().
      */
     public function __construct($path, $server = '', $port = '', $method = '')
     {
@@ -475,8 +475,8 @@ class Client
      *                         This timeout value is passed to fsockopen(). It is also used for detecting server
      *                         timeouts during communication (i.e. if the server does not send anything to the client
      *                         for $timeout seconds, the connection will be closed).
-     * @param string $method valid values are 'http', 'http11' and 'https'. If left unspecified, the http protocol
-     *                       chosen during creation of the object will be used.
+     * @param string $method valid values are 'http', 'http11', 'https', 'http2' and 'http2tls'. If left unspecified,
+     *                       the http protocol chosen during creation of the object will be used.
      *
      * @return Response|Response[] Note that the client will always return a Response object, even if the call fails
      * @todo allow throwing exceptions instead of returning responses in case of failed calls and/or Fault responses
@@ -506,7 +506,7 @@ class Client
         /// @todo we could be smarter about this and force usage of curl in scenarios where it is both available and
         ///       needed, such as digest or ntlm auth. Do not attempt to use it for https if not present
         $useCurl = ($this->use_curl == self::USE_CURL_ALWAYS) || ($this->use_curl == self::USE_CURL_AUTO &&
-            ($method == 'https' || $method == 'http11'));
+            ($method == 'https' || $method == 'http11' || $method == 'http2' || $method == 'http2tls'));
 
         if ($useCurl) {
             $r = $this->sendPayloadCURL(
@@ -859,7 +859,7 @@ class Client
      * @param string $proxyUsername
      * @param string $proxyPassword
      * @param int $proxyAuthType
-     * @param string $method 'http' (let curl decide), 'http10', 'http11' or 'https'
+     * @param string $method 'http' (let curl decide), 'http10', 'http11', 'https', 'http2' or 'http2tls'
      * @param bool $keepAlive
      * @param string $key
      * @param string $keyPass
@@ -882,10 +882,13 @@ class Client
                 $this->errstr = 'SSL unavailable on this install';
                 return new Response(0, PhpXmlRpc::$xmlrpcerr['no_ssl'], PhpXmlRpc::$xmlrpcstr['no_ssl']);
             }
+        } elseif (($method == 'http2' || $method == 'http2tls') && !defined('CURL_HTTP_VERSION_2')) {
+            $this->errstr = 'HTTP/2 unavailable on this install';
+            return new Response(0, PhpXmlRpc::$xmlrpcerr['no_http2'], PhpXmlRpc::$xmlrpcstr['no_http2']);
         }
 
         if ($port == 0) {
-            if (in_array($method, array('http', 'http10', 'http11'))) {
+            if (in_array($method, array('http', 'http10', 'http11', 'http2'))) {
                 $port = 80;
             } else {
                 $port = 443;
@@ -922,10 +925,14 @@ class Client
         }
 
         if (!$keepAlive || !$this->xmlrpc_curl_handle) {
-            if ($method == 'http11' || $method == 'http10') {
+            if ($method == 'http11' || $method == 'http10' || $method == 'http2') {
                 $protocol = 'http';
             } else {
-                $protocol = $method;
+                if ($method == 'http2tls') {
+                    $protocol = 'https';
+                } else {
+                    $protocol = $method;
+                }
             }
             $curl = curl_init($protocol . '://' . $server . ':' . $port . $this->path);
             if ($keepAlive) {
@@ -983,10 +990,19 @@ class Client
             curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
         }
 
-        if ($method == 'http10') {
-            curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
-        } elseif ($method == 'http11') {
-            curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+        switch($method) {
+            case 'http10':
+                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+                break;
+            case 'http11':
+                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+                break;
+            case 'http2':
+                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
+                break;
+            case 'http2tls':
+                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+                break;
         }
 
         if ($username && $password) {
@@ -998,7 +1014,7 @@ class Client
             }
         }
 
-        if ($method == 'https') {
+        if ($method == 'https' || $method == 'http2tls') {
             // set cert file
             if ($cert) {
                 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
index 416d53f..2f4daff 100644 (file)
@@ -26,6 +26,7 @@ class PhpXmlRpc
         'multicall_recursion' => 12,
         'multicall_noparams' => 13,
         'multicall_notarray' => 14,
+        'no_http2' => 15,
 
         'cannot_decompress' => 103,
         'decompress_fail' => 104,
@@ -53,6 +54,7 @@ class PhpXmlRpc
         'multicall_recursion' => 'Recursive system.multicall forbidden',
         'multicall_noparams' => 'Missing params',
         'multicall_notarray' => 'params is not an array',
+        'no_http2' => 'No HTTP/2 support compiled in',
 
         'cannot_decompress' => 'Received from server compressed HTTP and cannot decompress',
         'decompress_fail' => 'Received from server invalid compressed HTTP',
index 3a14809..83c361c 100644 (file)
@@ -303,6 +303,51 @@ class HTTPTest extends ServerTest
         $this->$method();
     }
 
+    /**
+     * @dataProvider getSingleHttpTestMethods
+     * @param string $method
+     */
+    public function testHttp2($method)
+    {
+        if (!function_exists('curl_init'))
+        {
+            $this->markTestSkipped('CURL missing: cannot test http/2');
+            return;
+        }
+
+        $this->method = 'http2'; // not an error the double assignment!
+        $this->client->method = 'http2';
+        //$this->client->keepalive = false; // q: is this a good idea?
+        $this->$method();
+    }
+
+    /**
+     * @dataProvider getSingleHttpTestMethods
+     * @param string $method
+     */
+    public function testHttp2tls($method)
+    {
+        if (!function_exists('curl_init'))
+        {
+            $this->markTestSkipped('CURL missing: cannot test http/2 tls');
+            return;
+        } else if ($this->args['HTTPSSERVER'] == '')
+        {
+            $this->markTestSkipped('HTTPS SERVER definition missing: cannot test http/2 tls');
+            return;
+        }
+
+        $this->client->server = $this->args['HTTPSSERVER'];
+        $this->method = 'http2tls';
+        $this->client->method = 'http2tls';
+        $this->client->path = $this->args['HTTPSURI'];
+        $this->client->setSSLVerifyPeer(!$this->args['HTTPSIGNOREPEER']);
+        $this->client->setSSLVerifyHost($this->args['HTTPSVERIFYHOST']);
+        $this->client->setSSLVersion($this->args['SSLVERSION']);
+
+        $this->$method();
+    }
+
     /**
      * @dataProvider getSingleHttpTestMethods
      * @param string $method
index 88307bf..6c7b675 100644 (file)
@@ -2,6 +2,9 @@
 # HTTPSERVER
 # TESTS_ROOT_DIR
 
+# Enable http2
+Protocols h2 h2c http/1.1
+
 <VirtualHost *:80>
 
   DocumentRoot ${TESTS_ROOT_DIR}
index b29cde2..abde451 100755 (executable)
@@ -12,7 +12,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y apache2
 
 # set up Apache for php-fpm
 
-a2enmod rewrite proxy_fcgi setenvif ssl
+a2enmod rewrite proxy_fcgi setenvif ssl http2
 
 # in case mod-php was enabled (this is the case at least on GHA's ubuntu with php 5.x and shivammathur/setup-php)
 # @todo silence errors in a smarter way