switch to http/2 prior-knowledge for non-tls requests
authorgggeek <giunta.gaetano@gmail.com>
Thu, 26 May 2022 10:41:40 +0000 (10:41 +0000)
committergggeek <giunta.gaetano@gmail.com>
Thu, 26 May 2022 10:41:40 +0000 (10:41 +0000)
NEWS
src/Client.php
src/PhpXmlRpc.php
tests/6HTTPTest.php

diff --git a/NEWS b/NEWS
index 5a20e18..89026ee 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,14 @@
+XML-RPC for PHP version 4.7.1 - 2022/5/25
+
+* fixed: http/2 on non-https requests works in either "prior-knowledge" mode or "upgrade" mode, known as h2c.
+  Given the fact that h2c is not compatible with POST requests, we switched to using "prior-knowledge" mode for requests
+  sent with the `http2` argument passed to the client's constructor or `send` method.
+  NB: this means that requests sent with `http2` are only compatible with servers and proxies known to be http/2 compliant.
+
+
 XML-RPC for PHP version 4.7.0 - 2022/5/25
 
+
 * new: HTTP/2 is supported by both the Client and Server components (with the php cURL extension being required to use
   it client-side).
   To force the client to use http/2 and http/2-tls requests, pass `http2` or `http2tls` as 3rd argument to `Client::send`.
index baaa960..0e4fac5 100644 (file)
@@ -137,16 +137,17 @@ class Client
      *                     should use and empty string for all other parameters)
      *                     e.g. /xmlrpc/server.php
      *                     e.g. http://phpxmlrpc.sourceforge.net/server.php
-     *                     e.g. https://james:bond@secret.service.com:443/xmlrpcserver?agent=007
-     *                     e.g. http2tls://fast-and-secure-services/endpoint
+     *                     e.g. https://james:bond@secret.service.com:444/xmlrpcserver?agent=007
+     *                     e.g. http2tls://fast-and-secure-services.org/endpoint
      * @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', 'http11', 'http2' and 'http2tls' can
+     * @param string $method the http protocol variant: defaults to 'http'; 'https', 'http11', 'http2tls' and 'http2' can
      *                       be used if CURL is installed. The value set here can be overridden in any call to $this->send().
-     *                       Use 'http2' to make the lib attempt to use http/2 without tls and 'http2tls' for secure http/2
-     *                       (note that 'http2' will most likely not result in an http/2 connection in any case, as it
-     *                       seems that upgrading a POST request on the fly from http is hard or impossible)
+     *                       Use 'http2tls' to make the lib attempt to use http/2 over a secure connection, and 'http2'
+     *                       for http/2 without tls. Note that 'http2' will not use the h2c 'upgrade' method, and be
+     *                       thus incompatible with any server/proxy not supporting http/2. This is because POST
+     *                       request are not compatible with h2c.
      */
     public function __construct($path, $server = '', $port = '', $method = '')
     {
@@ -480,11 +481,12 @@ 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', 'https', 'http2' and 'http2tls'. If left unspecified,
+     * @param string $method valid values are 'http', 'http11', 'https', 'http2tls' and 'http2'. If left unspecified,
      *                       the http protocol chosen during creation of the object will be used.
-     *                       Use 'http2' to make the lib attempt to use http/2 without tls and 'http2tls' for secure http/2
-     *                       (note that 'http2' will most likely not result in an http/2 connection in any case, as it
-     *                       seems that upgrading a POST request on the fly from http is hard or impossible)
+     *                       Use 'http2tls' to make the lib attempt to use http/2 over a secure connection, and 'http2'
+     *                       for http/2 without tls. Note that 'http2' will not use the h2c 'upgrade' method, and be
+     *                       thus incompatible with any server/proxy not supporting http/2. This is because POST
+     *                       request are not compatible with h2c.
      *
      * @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
@@ -883,14 +885,17 @@ class Client
             $this->errstr = 'CURL unavailable on this install';
             return new Response(0, PhpXmlRpc::$xmlrpcerr['no_curl'], PhpXmlRpc::$xmlrpcstr['no_curl']);
         }
-        if ($method == 'https') {
+        if ($method == 'https' || $method == 'http2tls') {
+            // q: what about installs where we get back a string, but curl is linked to other ssl libs than openssl?
             if (($info = curl_version()) &&
                 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version'])))
             ) {
                 $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')) {
+        }
+        if (($method == 'http2tls' && !defined('CURL_HTTP_VERSION_2TLS')) ||
+            ($method == 'http2' && !defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))) {
             $this->errstr = 'HTTP/2 unavailable on this install';
             return new Response(0, PhpXmlRpc::$xmlrpcerr['no_http2'], PhpXmlRpc::$xmlrpcstr['no_http2']);
         }
@@ -1006,7 +1011,7 @@ class Client
                 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
                 break;
             case 'http2':
-                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
+                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
                 break;
             case 'http2tls':
                 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
index e49c1eb..901d1eb 100644 (file)
@@ -80,7 +80,7 @@ class PhpXmlRpc
     public static $xmlrpc_internalencoding = "UTF-8";
 
     public static $xmlrpcName = "XML-RPC for PHP";
-    public static $xmlrpcVersion = "4.7.0";
+    public static $xmlrpcVersion = "4.7.1";
 
     // let user errors start at 800
     public static $xmlrpcerruser = 800;
index a1c4655..fa013bb 100644 (file)
@@ -315,7 +315,7 @@ class HTTPTest extends ServerTest
         {
             $this->markTestSkipped('CURL missing: cannot test http/2');
             return;
-        } else if (!defined('CURL_HTTP_VERSION_2'))
+        } else if (!defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))
         {
             $this->markTestSkipped('CURL http/2 support missing: cannot test http/2');
             return;
@@ -325,10 +325,9 @@ class HTTPTest extends ServerTest
         $this->client->method = 'http2';
         //$this->client->keepalive = false; // q: is this a good idea?
 
-        /// @todo we disable checking for HTTP2 on plain http calls, as that is unsupported for POST requests
-        //$this->expectHttp2 = true;
+        $this->expectHttp2 = true;
         $this->$method();
-        //$this->expectHttp2 = false;
+        $this->expectHttp2 = false;
     }
 
     /**
@@ -345,7 +344,7 @@ class HTTPTest extends ServerTest
         {
             $this->markTestSkipped('HTTPS SERVER definition missing: cannot test http/2 tls');
             return;
-        } else if (!defined('CURL_HTTP_VERSION_2'))
+        } else if (!defined('CURL_HTTP_VERSION_2TLS'))
         {
             $this->markTestSkipped('CURL http/2 support missing: cannot test http/2 tls');
             return;