From: gggeek Date: Wed, 25 May 2022 14:15:21 +0000 (+0000) Subject: add http/2 support client-side, and tests X-Git-Tag: 4.7.0~1 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=7e23e7e8c574f66a60906a0a75c788caf8c90e58;p=plcapi.git add http/2 support client-side, and tests --- diff --git a/src/Client.php b/src/Client.php index cd1491c3..08a9a192 100644 --- a/src/Client.php +++ b/src/Client.php @@ -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); diff --git a/src/PhpXmlRpc.php b/src/PhpXmlRpc.php index 416d53f7..2f4daff1 100644 --- a/src/PhpXmlRpc.php +++ b/src/PhpXmlRpc.php @@ -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', diff --git a/tests/6HTTPTest.php b/tests/6HTTPTest.php index 3a14809c..83c361c7 100644 --- a/tests/6HTTPTest.php +++ b/tests/6HTTPTest.php @@ -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 diff --git a/tests/ci/config/apache_vhost b/tests/ci/config/apache_vhost index 88307bf6..6c7b6757 100644 --- a/tests/ci/config/apache_vhost +++ b/tests/ci/config/apache_vhost @@ -2,6 +2,9 @@ # HTTPSERVER # TESTS_ROOT_DIR +# Enable http2 +Protocols h2 h2c http/1.1 + DocumentRoot ${TESTS_ROOT_DIR} diff --git a/tests/ci/setup/setup_apache.sh b/tests/ci/setup/setup_apache.sh index b29cde2e..abde4511 100755 --- a/tests/ci/setup/setup_apache.sh +++ b/tests/ci/setup/setup_apache.sh @@ -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