Home / Symfony / New in Symfony 8.1: HttpClient Improvements

New in Symfony 8.1: HttpClient Improvements

Symfony 8.1 ships a batch of improvements for the HttpClient component that
touch performance, interoperability, security and testing.

Persistent cURL Connections


Kostiantyn Miakshyn
Contributed by
Kostiantyn Miakshyn
in
#62751

When you make many requests in a row, re-establishing connections for every
request adds DNS, TCP and TLS overhead. PHP 8.5 introduces persistent cURL
handles
and Symfony 8.1 exposes them through the new
extra.use_persistent_connections option of CurlHttpClient:

use SymfonyComponentHttpClientCurlHttpClient;

$client = new CurlHttpClient([
    'extra' => [
        'use_persistent_connections' => true,
    ],
]);

When enabled (and running on PHP 8.5 or later), the DNS cache, SSL sessions and
connection data are reused across requests, reducing the connection overhead.
This is most useful for CLI commands and workers that make many sequential requests.

Using Symfony HttpClient as a Guzzle Handler


Nicolas Grekas
Contributed by
Nicolas Grekas
in
#63433

Some SDKs are built on top of Guzzle and don’t allow replacing the underlying
HTTP client. Symfony 8.1 adds a GuzzleHttpHandler that lets those SDKs use
Symfony HttpClient as their transport layer, benefiting from features such as
retries, HTTP/2 support and scoped clients:

use GuzzleHttpClient;
use SymfonyComponentHttpClientGuzzleHttpHandler;

$guzzle = new Client(['handler' => new GuzzleHttpHandler()]);

$response = $guzzle->request('GET', 'https://symfony.com/versions.json');

By default, the handler creates a new HttpClient instance. You can also pass any
HttpClientInterface implementation to reuse the same configuration across
your application and third-party SDKs.

Allowing Specific Hosts in NoPrivateNetworkHttpClient


Pascal CESCON
Contributed by
Pascal CESCON
in
#64160

NoPrivateNetworkHttpClient helps protect applications from SSRF attacks
by blocking requests to private networks (10.0.0.0/8, 192.168.0.0/16,
etc.). Symfony 8.1 adds a third $allowList argument that lets you exempt
specific hosts (e.g. a local proxy) while continuing to block all other
private-network addresses:

use SymfonyComponentHttpClientHttpClient;
use SymfonyComponentHttpClientNoPrivateNetworkHttpClient;

$client = new NoPrivateNetworkHttpClient(
    HttpClient::create(),
    null,           // null = block the default private subnets
    '10.0.0.42',    // allow this host (also accepts an array of IPs/CIDR subnets)
);

Safer Default for Cached Responses


Jérôme Parmentier


Nicolas Grekas
Contributed by
Jérôme Parmentier
and Nicolas Grekas
in
#63441

CachingHttpClient stores responses according to the cache-control directives
returned by the server. When no cache directives are provided, responses could
previously remain cached indefinitely.

In Symfony 8.1, the maximum time-to-live now defaults to 86400 seconds (24
hours) instead of being unlimited. Server-provided TTLs are still respected,
but they are capped to this value to prevent stale cache entries from living
indefinitely. You can change the cap with the max_ttl option:

# config/packages/framework.yaml
framework:
    http_client:
        scoped_clients:
            example.client:
                base_uri: 'https://example.com'
                caching:
                    max_ttl: 3600

Passing null to disable the cap is now deprecated; use a positive integer instead.

Failing Fast with max_connect_duration


Alexandre Daubois
Contributed by
Alexandre Daubois
in
#62854

The timeout option controls how long the client waits for activity on an
idle connection and max_duration caps the total request time. Neither
provides a strict deadline for establishing the connection.

Symfony 8.1 adds the max_connect_duration option, which limits the time
spent on DNS resolution, the TCP connection and the TLS handshake:

$response = $client->request('GET', 'https://...', [
    // fail fast if the connection cannot be established within 0.5 seconds
    'max_connect_duration' => 0.5,
    'timeout' => 10,
]);

A value of 0 or less means there is no limit on connection time, as long as
the timeout option is respected.

Per-Client Mocking in Tests


Tarjei Huse
Contributed by
Tarjei Huse
in
#52265

When testing, you can set mock_response_factory to replace HttpClient with a
mock that returns canned responses. In Symfony 8.1, this option can now be
configured per scoped client, making it easier to mock specific APIs while
leaving others untouched.

Set it to true for a MockHttpClient that returns empty 200
responses, to false to disable mocking for a specific client, or to a
service ID for full control over the returned responses:

# config/packages/test/framework.yaml
framework:
    http_client:
        mock_response_factory: true
        scoped_clients:
            my_api.client:
                base_uri: 'https://my-api.com'
                mock_response_factory: AppTestsMyApiMockClientCallback
            not_mocked.client:
                base_uri: 'https://example.com'
                mock_response_factory: false

Custom DNS Resolution


Peter Potrowl
Contributed by
Peter Potrowl
in
#63770

Symfony 8.1 adds a DnsResolvingHttpClient decorator that lets you provide
custom DNS resolution logic (e.g. to route requests to a specific server while
debugging). The resolver receives the hostname and returns the IP address to
use, or null to fall back to the default transport resolution:

use SymfonyComponentHttpClientDnsResolvingHttpClient;
use SymfonyComponentHttpClientHttpClient;

$resolver = function (string $hostname): ?string {
    // implement your own resolution logic (service discovery, caching, etc.)
    return $ip; // or null to use the default DNS resolution
};

$client = new DnsResolvingHttpClient(HttpClient::create(), $resolver);


Sponsor the Symfony project.
Tagged:

Leave a Reply

Your email address will not be published. Required fields are marked *