← All changes
|
vendor_prefixed/guzzlehttp/guzzle/src/Handler/CurlVersion.php
+0
-206
28.4
→
trunk
View file →
| @@ -1,206 +1,0 @@ | ||
| 1 | -<?php | |
| 2 | - | |
| 3 | -namespace YoastSEO_Vendor\GuzzleHttp\Handler; | |
| 4 | - | |
| 5 | -/** | |
| 6 | - * @internal | |
| 7 | - */ | |
| 8 | -final class CurlVersion | |
| 9 | -{ | |
| 10 | - private const MIN_VERSION = '7.21.2'; | |
| 11 | - private const TLS_12_VERSION = '7.34.0'; | |
| 12 | - private const TLS_13_VERSION = '7.52.0'; | |
| 13 | - private const CONNECTION_CAP_VERSION = '7.30.0'; | |
| 14 | - // CURLOPT_PIPEWAIT exists since libcurl 7.43.0, and multi handles have | |
| 15 | - // multiplexed by default since 7.62.0 - but a 7.65.0-7.65.1 regression | |
| 16 | - // dropped that default, which 7.65.2 restored, so 7.65.2 is the floor at | |
| 17 | - // which PIPEWAIT is reliably effective. | |
| 18 | - private const MULTIPLEX_VERSION = '7.65.2'; | |
| 19 | - // libcurl's connection matcher refuses to hand a transfer wanting | |
| 20 | - // HTTP/1.x a pooled connection that already negotiated HTTP/2 or newer | |
| 21 | - // from 7.77.0: ConnectionExists() in lib/url.c gained the check between | |
| 22 | - // the 7.76.0 and 7.77.0 releases. The HTTP/2 branch of the check | |
| 23 | - // regressed to a debug log in 8.11.0 (curl commit 433d730) and was | |
| 24 | - // restored in 8.13.0 via the negotiation mask (curl commit db72b8d), so | |
| 25 | - // 8.11.0 through 8.12.1 are vulnerable again. | |
| 26 | - private const HTTP_VERSION_REUSE_MATCH_VERSION = '7.77.0'; | |
| 27 | - private const HTTP_VERSION_REUSE_MATCH_REGRESSION = '8.11.0'; | |
| 28 | - private const HTTP_VERSION_REUSE_MATCH_RESTORED = '8.13.0'; | |
| 29 | - // CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE restricts the ALPN offer to h2 only | |
| 30 | - // since libcurl 8.10.0, and connection reuse matching stopped handing | |
| 31 | - // lower-version connections to prior-knowledge transfers in 8.14.0; below | |
| 32 | - // that, a required request could silently be sent over a reused HTTP/1.1 | |
| 33 | - // connection. | |
| 34 | - private const REQUIRED_MULTIPLEX_VERSION = '8.14.0'; | |
| 35 | - // curl 7.52.0 introduced HTTPS proxy support, advertised by a feature bit | |
| 36 | - // (a build can meet the version yet lack the feature). Earlier libcurl | |
| 37 | - // mishandles an https:// proxy: before 7.50.2 it silently downgrades to a | |
| 38 | - // plaintext HTTP proxy, and 7.50.2 through 7.51 reject it at connect time. | |
| 39 | - private const HTTPS_PROXY_VERSION = '7.52.0'; | |
| 40 | - private const HANDLER_SHARING_VERSION = '7.35.0'; | |
| 41 | - private const SSL_SESSION_SHARING_VERSION = '8.6.0'; | |
| 42 | - // curl 7.57.0 added share-handle connection caches through | |
| 43 | - // CURL_LOCK_DATA_CONNECT; older share objects can only hold DNS, TLS | |
| 44 | - // session, and cookie data, never connections. | |
| 45 | - private const SHARE_CONNECTION_CACHE_VERSION = '7.57.0'; | |
| 46 | - // curl 7.83.1 added proxy TLS-SRP to the connection-reuse match | |
| 47 | - // (CVE-2022-27782); the proxy client certificate was matched from 7.52.0, | |
| 48 | - // so proxy TLS credentials are trusted from 7.83.1 onwards. | |
| 49 | - private const PROXY_TLS_CREDENTIAL_REUSE_VERSION = '7.83.1'; | |
| 50 | - // curl 8.19.0 fixed proxy tunnel reuse after credential changes | |
| 51 | - // (CVE-2026-3784), but related proxy credential leak flaws were only | |
| 52 | - // fixed in 8.20.0, so connection reuse is trusted from 8.20.0 onwards. | |
| 53 | - private const PROXY_CREDENTIAL_REUSE_VERSION = '8.20.0'; | |
| 54 | - // curl 7.69.0 started comparing SOCKS proxy credentials when matching | |
| 55 | - // connections for reuse (curl #4835); older libcurl matches a SOCKS proxy | |
| 56 | - // by type, host, and port only. | |
| 57 | - private const SOCKS_PROXY_CREDENTIAL_REUSE_VERSION = '7.69.0'; | |
| 58 | - private const PROXY_HEADER_SEPARATION_VERSION = '7.37.0'; | |
| 59 | - /** | |
| 60 | - * @var array{version: string, features: int}|false|null | |
| 61 | - */ | |
| 62 | - private static $versionInfo; | |
| 63 | - private function __construct() | |
| 64 | - { | |
| 65 | - } | |
| 66 | - public static function supportsCurlHandler() : bool | |
| 67 | - { | |
| 68 | - $version = self::getVersion(); | |
| 69 | - return $version !== null && \version_compare($version, self::MIN_VERSION, '>='); | |
| 70 | - } | |
| 71 | - public static function supportsTls12() : bool | |
| 72 | - { | |
| 73 | - $version = self::getVersion(); | |
| 74 | - return self::supportsSsl() && \defined('CURL_SSLVERSION_TLSv1_2') && $version !== null && \version_compare($version, self::TLS_12_VERSION, '>='); | |
| 75 | - } | |
| 76 | - public static function supportsTls13() : bool | |
| 77 | - { | |
| 78 | - $version = self::getVersion(); | |
| 79 | - return self::supportsSsl() && \defined('CURL_SSLVERSION_TLSv1_3') && $version !== null && \version_compare($version, self::TLS_13_VERSION, '>='); | |
| 80 | - } | |
| 81 | - public static function supportsHttp2() : bool | |
| 82 | - { | |
| 83 | - $versionInfo = self::getVersionInfo(); | |
| 84 | - return self::supportsTls12() && \defined('CURL_VERSION_HTTP2') && $versionInfo !== null && 0 !== (\CURL_VERSION_HTTP2 & $versionInfo['features']); | |
| 85 | - } | |
| 86 | - public static function supportsMultiplex() : bool | |
| 87 | - { | |
| 88 | - $version = self::getVersion(); | |
| 89 | - return \defined('CURLOPT_PIPEWAIT') && $version !== null && \version_compare($version, self::MULTIPLEX_VERSION, '>='); | |
| 90 | - } | |
| 91 | - public static function supportsHttpVersionReuseMatching() : bool | |
| 92 | - { | |
| 93 | - $version = self::getVersion(); | |
| 94 | - if ($version === null || \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_VERSION, '<')) { | |
| 95 | - return \false; | |
| 96 | - } | |
| 97 | - return \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_REGRESSION, '<') || \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_RESTORED, '>='); | |
| 98 | - } | |
| 99 | - public static function supportsConnectionCaps() : bool | |
| 100 | - { | |
| 101 | - $version = self::getVersion(); | |
| 102 | - return \defined('CURLMOPT_MAX_HOST_CONNECTIONS') && \defined('CURLMOPT_MAX_TOTAL_CONNECTIONS') && $version !== null && \version_compare($version, self::CONNECTION_CAP_VERSION, '>='); | |
| 103 | - } | |
| 104 | - public static function ensureConnectionCapsSupported(string $option) : void | |
| 105 | - { | |
| 106 | - if (self::supportsConnectionCaps()) { | |
| 107 | - return; | |
| 108 | - } | |
| 109 | - throw new \InvalidArgumentException(\sprintf('The "%s" option requires PHP cURL support for CURLMOPT_MAX_HOST_CONNECTIONS and CURLMOPT_MAX_TOTAL_CONNECTIONS with libcurl %s or newer.', $option, self::CONNECTION_CAP_VERSION)); | |
| 110 | - } | |
| 111 | - public static function supportsRequiredMultiplex() : bool | |
| 112 | - { | |
| 113 | - $version = self::getVersion(); | |
| 114 | - return \defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE') && $version !== null && self::supportsHttp2() && \version_compare($version, self::REQUIRED_MULTIPLEX_VERSION, '>='); | |
| 115 | - } | |
| 116 | - public static function supportsHttpsProxy() : bool | |
| 117 | - { | |
| 118 | - $versionInfo = self::getVersionInfo(); | |
| 119 | - // CURL_VERSION_HTTPS_PROXY is not defined on every supported PHP | |
| 120 | - // version; fall back to the curl.h bit value. | |
| 121 | - $httpsProxyFeature = \defined('CURL_VERSION_HTTPS_PROXY') ? \CURL_VERSION_HTTPS_PROXY : 1 << 21; | |
| 122 | - return $versionInfo !== null && \version_compare($versionInfo['version'], self::HTTPS_PROXY_VERSION, '>=') && 0 !== ($httpsProxyFeature & $versionInfo['features']); | |
| 123 | - } | |
| 124 | - public static function supportsNtlm() : bool | |
| 125 | - { | |
| 126 | - $versionInfo = self::getVersionInfo(); | |
| 127 | - // CURL_VERSION_NTLM is not defined on every supported PHP version; fall | |
| 128 | - // back to the curl.h bit value. | |
| 129 | - $ntlmFeature = \defined('CURL_VERSION_NTLM') ? \CURL_VERSION_NTLM : 1 << 4; | |
| 130 | - return \defined('CURLAUTH_NTLM') && $versionInfo !== null && 0 !== ($ntlmFeature & $versionInfo['features']); | |
| 131 | - } | |
| 132 | - public static function supportsHandlerSharing() : bool | |
| 133 | - { | |
| 134 | - $version = self::getVersion(); | |
| 135 | - return $version !== null && \version_compare($version, self::HANDLER_SHARING_VERSION, '>='); | |
| 136 | - } | |
| 137 | - public static function ensureHandlerSharingSupported() : void | |
| 138 | - { | |
| 139 | - if (!self::supportsHandlerSharing()) { | |
| 140 | - throw new \InvalidArgumentException(\sprintf('The "transport_sharing" option requires libcurl %s or higher for cURL share handles.', self::HANDLER_SHARING_VERSION)); | |
| 141 | - } | |
| 142 | - } | |
| 143 | - public static function supportsSslSessionSharing() : bool | |
| 144 | - { | |
| 145 | - $version = self::getVersion(); | |
| 146 | - return self::supportsSsl() && $version !== null && \version_compare($version, self::SSL_SESSION_SHARING_VERSION, '>='); | |
| 147 | - } | |
| 148 | - public static function ensureSslSessionSharingSupported() : void | |
| 149 | - { | |
| 150 | - if (!self::supportsSslSessionSharing()) { | |
| 151 | - throw new \InvalidArgumentException(\sprintf('The "transport_sharing" option requires libcurl %s or higher with SSL support for SSL session sharing.', self::SSL_SESSION_SHARING_VERSION)); | |
| 152 | - } | |
| 153 | - } | |
| 154 | - public static function supportsShareConnectionCaches() : bool | |
| 155 | - { | |
| 156 | - $version = self::getVersion(); | |
| 157 | - // An undetectable libcurl version is treated as capable so the | |
| 158 | - // opaque share safeguards fail closed. | |
| 159 | - return $version === null || \version_compare($version, self::SHARE_CONNECTION_CACHE_VERSION, '>='); | |
| 160 | - } | |
| 161 | - public static function supportsProxyTlsCredentialAwareConnectionReuse() : bool | |
| 162 | - { | |
| 163 | - $version = self::getVersion(); | |
| 164 | - return $version !== null && \version_compare($version, self::PROXY_TLS_CREDENTIAL_REUSE_VERSION, '>='); | |
| 165 | - } | |
| 166 | - public static function supportsProxyCredentialAwareConnectionReuse() : bool | |
| 167 | - { | |
| 168 | - $version = self::getVersion(); | |
| 169 | - return $version !== null && \version_compare($version, self::PROXY_CREDENTIAL_REUSE_VERSION, '>='); | |
| 170 | - } | |
| 171 | - public static function supportsSocksProxyCredentialAwareConnectionReuse() : bool | |
| 172 | - { | |
| 173 | - $version = self::getVersion(); | |
| 174 | - return $version !== null && \version_compare($version, self::SOCKS_PROXY_CREDENTIAL_REUSE_VERSION, '>='); | |
| 175 | - } | |
| 176 | - public static function supportsProxyHeaderSeparation() : bool | |
| 177 | - { | |
| 178 | - $version = self::getVersion(); | |
| 179 | - return $version !== null && \version_compare($version, self::PROXY_HEADER_SEPARATION_VERSION, '>=') && \defined('CURLOPT_PROXYHEADER') && \defined('CURLOPT_HEADEROPT') && \defined('CURLHEADER_SEPARATE'); | |
| 180 | - } | |
| 181 | - private static function supportsSsl() : bool | |
| 182 | - { | |
| 183 | - $versionInfo = self::getVersionInfo(); | |
| 184 | - return \defined('CURL_VERSION_SSL') && $versionInfo !== null && 0 !== (\CURL_VERSION_SSL & $versionInfo['features']); | |
| 185 | - } | |
| 186 | - public static function getVersion() : ?string | |
| 187 | - { | |
| 188 | - $versionInfo = self::getVersionInfo(); | |
| 189 | - return $versionInfo === null ? null : $versionInfo['version']; | |
| 190 | - } | |
| 191 | - /** | |
| 192 | - * @return array{version: string, features: int}|null | |
| 193 | - */ | |
| 194 | - private static function getVersionInfo() : ?array | |
| 195 | - { | |
| 196 | - if (self::$versionInfo === null) { | |
| 197 | - if (!\function_exists('curl_version')) { | |
| 198 | - self::$versionInfo = \false; | |
| 199 | - } else { | |
| 200 | - $versionInfo = \curl_version(); | |
| 201 | - self::$versionInfo = \is_array($versionInfo) && isset($versionInfo['version'], $versionInfo['features']) && \is_string($versionInfo['version']) && \is_int($versionInfo['features']) ? ['version' => $versionInfo['version'], 'features' => $versionInfo['features']] : \false; | |
| 202 | - } | |
| 203 | - } | |
| 204 | - return self::$versionInfo === \false ? null : self::$versionInfo; | |
| 205 | - } | |
| 206 | -} | |