diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md index 2c4b706914..8b8bbdf8cd 100644 --- a/src/Symfony/Component/HttpClient/CHANGELOG.md +++ b/src/Symfony/Component/HttpClient/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * added `NoPrivateNetworkHttpClient` decorator * added `AmpHttpClient`, a portable HTTP/2 implementation based on Amp * added `LoggerAwareInterface` to `ScopingHttpClient` and `TraceableHttpClient` + * made `HttpClient::create()` return an `AmpHttpClient` when `amphp/http-client` is found but curl is not or too old 4.4.0 ----- diff --git a/src/Symfony/Component/HttpClient/HttpClient.php b/src/Symfony/Component/HttpClient/HttpClient.php index b8ce62a233..405c18ce92 100644 --- a/src/Symfony/Component/HttpClient/HttpClient.php +++ b/src/Symfony/Component/HttpClient/HttpClient.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpClient; +use Amp\Http\Client\Connection\ConnectionLimitingPool; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -29,6 +30,25 @@ final class HttpClient */ public static function create(array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 50): HttpClientInterface { + if ($amp = class_exists(ConnectionLimitingPool::class)) { + if (!\extension_loaded('curl')) { + return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); + } + + // Skip curl when HTTP/2 push is unsupported or buggy, see https://bugs.php.net/77535 + if (\PHP_VERSION_ID < 70217 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304) || !\defined('CURLMOPT_PUSHFUNCTION')) { + return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); + } + + static $curlVersion = null; + $curlVersion = $curlVersion ?? curl_version(); + + // HTTP/2 push crashes before curl 7.61 + if (0x073d00 > $curlVersion['version_number'] || !(CURL_VERSION_HTTP2 & $curlVersion['features'])) { + return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); + } + } + if (\extension_loaded('curl')) { if ('\\' !== \DIRECTORY_SEPARATOR || ini_get('curl.cainfo') || ini_get('openssl.cafile') || ini_get('openssl.capath')) { return new CurlHttpClient($defaultOptions, $maxHostConnections, $maxPendingPushes); @@ -37,6 +57,10 @@ final class HttpClient @trigger_error('Configure the "curl.cainfo", "openssl.cafile" or "openssl.capath" php.ini setting to enable the CurlHttpClient', E_USER_WARNING); } + if ($amp) { + return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); + } + return new NativeHttpClient($defaultOptions, $maxHostConnections); } diff --git a/src/Symfony/Component/HttpClient/Internal/AmpClientState.php b/src/Symfony/Component/HttpClient/Internal/AmpClientState.php index 6fa8a2fc20..afd4c88fb8 100644 --- a/src/Symfony/Component/HttpClient/Internal/AmpClientState.php +++ b/src/Symfony/Component/HttpClient/Internal/AmpClientState.php @@ -161,7 +161,9 @@ final class AmpClientState extends ClientState }; $connector->connector = new DnsConnector(new AmpResolver($this->dnsCache)); - $context = (new ConnectContext())->withTlsContext($context); + $context = (new ConnectContext()) + ->withTcpNoDelay() + ->withTlsContext($context); if ($options['bindto']) { if (file_exists($options['bindto'])) { diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php index e2b0d9f6eb..bd7b83ce3a 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php @@ -12,18 +12,18 @@ namespace Symfony\Component\HttpClient\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpClient\AmpHttpClient; use Symfony\Component\HttpClient\CurlHttpClient; use Symfony\Component\HttpClient\HttpClient; -use Symfony\Component\HttpClient\NativeHttpClient; class HttpClientTest extends TestCase { public function testCreateClient() { - if (\extension_loaded('curl') && ('\\' !== \DIRECTORY_SEPARATOR || ini_get('curl.cainfo') || ini_get('openssl.cafile') || ini_get('openssl.capath'))) { + if (\extension_loaded('curl') && ('\\' !== \DIRECTORY_SEPARATOR || ini_get('curl.cainfo') || ini_get('openssl.cafile') || ini_get('openssl.capath')) && 0x073d00 <= curl_version()['version_number']) { $this->assertInstanceOf(CurlHttpClient::class, HttpClient::create()); } else { - $this->assertInstanceOf(NativeHttpClient::class, HttpClient::create()); + $this->assertInstanceOf(AmpHttpClient::class, HttpClient::create()); } } } diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index e6fadb1941..47ddbcaa2e 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -28,7 +28,7 @@ "symfony/service-contracts": "^1.0|^2" }, "require-dev": { - "amphp/http-client": "^4.2", + "amphp/http-client": "^4.2.1", "amphp/http-tunnel": "^1.0", "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.3.1",