DEV Community

Cover image for Small Swoole Symfony HTTP Client: async HTTP
sebk69
sebk69

Posted on

Small Swoole Symfony HTTP Client: async HTTP

I'm pleased to introduce Small Swoole Symfony HTTP Client, an asynchronous, non-blocking HTTP client for applications running with Swoole or OpenSwoole.

It implements Symfony's familiar HttpClientInterface, allowing application code to use Symfony HTTP client contracts while requests are handled by a transport designed for coroutine-based runtimes.

But compatibility should be demonstrated, not simply claimed.

That is why the project has two important quality guarantees:

  • 100% code coverage
  • Validation against Symfony's official HTTP client reference test suite

Symfony itself points HTTP client implementors to Symfony\Contracts\HttpClient\Test\HttpClientTestCase as the reference suite for validating implementations.

This means the client is tested against the behavior Symfony expects—not only against a collection of project-specific happy paths.

Why this client?

Traditional PHP applications usually complete one request and terminate. Swoole and OpenSwoole applications are different: they stay alive, run coroutines and benefit from non-blocking I/O.

Small Swoole Symfony HTTP Client connects that runtime model with Symfony's established HTTP client API.

use Small\SwooleSymfonyHttpClient\SwooleHttpClient;

$client = new SwooleHttpClient();

$response = $client->request(
    'GET',
    'https://example.com/api/resources',
);

$data = $response->toArray();
Enter fullscreen mode Exit fullscreen mode

Existing services can depend on Symfony's contract:

use Symfony\Contracts\HttpClient\HttpClientInterface;

final class ApiClient
{
    public function __construct(
        private readonly HttpClientInterface $httpClient,
    ) {
    }

    public function fetchResources(): array
    {
        return $this->httpClient
            ->request('GET', 'https://example.com/api/resources')
            ->toArray();
    }
}
Enter fullscreen mode Exit fullscreen mode

The application remains decoupled from the concrete transport while benefiting from Swoole underneath.

Tested as a Symfony HTTP client

Reaching 100% coverage is valuable, but coverage alone does not prove that an implementation follows an external contract correctly.

For this project, the two approaches complement each other:

  1. The project's own test suite covers 100% of its code.
  2. Symfony's official HttpClientTestCase verifies the expected behavior of an HttpClientInterface implementation.

The reference suite exercises the contract from a Symfony consumer's perspective. It helps detect subtle compatibility issues involving requests, responses, options, streaming and error handling.

In other words, the goal is not merely to expose methods with the right names. The goal is to behave like a real Symfony HTTP client.

Supported capabilities

The client supports the features expected for real-world API communication, including:

  • Asynchronous, non-blocking requests
  • Swoole and OpenSwoole compatibility
  • Redirect handling
  • Request and connection timeouts
  • Basic authentication
  • Bearer-token authentication
  • Custom headers
  • JSON and form request bodies
  • Configurable retry handling
  • Proxy support
  • HTTP/2
  • Symfony response streaming
  • Default options through withOptions()

For example:

$client = (new SwooleHttpClient())->withOptions([
    'base_uri' => 'https://api.example.com',
    'auth_bearer' => $_ENV['API_TOKEN'],
    'headers' => [
        'Accept' => 'application/json',
    ],
    'timeout' => 5,
]);

$response = $client->request('POST', '/messages', [
    'json' => [
        'message' => 'Hello from Swoole',
    ],
]);
Enter fullscreen mode Exit fullscreen mode

Connection pooling and flow control

The package also provides PooledSwooleHttpClient for workloads that benefit from persistent, reusable connections.

use Small\SwooleSymfonyHttpClient\PooledSwooleHttpClient;

$client = new PooledSwooleHttpClient([
    'base_uri' => 'https://api.example.com',
    'max_connectors' => 10,
    'max_wait_time' => 5,
]);

$response = $client->request('GET', '/resources');
Enter fullscreen mode Exit fullscreen mode

The pool can limit the number of simultaneous connectors and control how long a coroutine waits for an available connection.

A rate controller can also be configured when an application needs to limit consumption of an external API:

$client = new PooledSwooleHttpClient([
    'base_uri' => 'https://api.example.com',
    'max_connectors' => 10,
    'rate_controller' => [
        [
            'name' => 'api',
            'unitForSecond' => 10,
            'maxTicks' => 20,
        ],
    ],
]);
Enter fullscreen mode Exit fullscreen mode

This is useful when high concurrency inside the application must coexist with rate limits imposed by another service.

PSR-18 support

Small Swoole Symfony HTTP Client also provides PSR-18 adapters, making it usable by libraries that depend on Psr\Http\Client\ClientInterface.

use Nyholm\Psr7\Factory\Psr17Factory;
use Small\SwooleSymfonyHttpClient\SwooleHttpClient;
use Small\SwooleSymfonyHttpClient\SwooleHttpClientPsr18Adapter;

$psr17Factory = new Psr17Factory();

$client = new SwooleHttpClient();

$psr18Client = new SwooleHttpClientPsr18Adapter(
    $client,
    $psr17Factory,
    $psr17Factory,
);

$request = $psr17Factory->createRequest(
    'GET',
    'https://example.com/api/resources',
);

$response = $psr18Client->sendRequest($request);
Enter fullscreen mode Exit fullscreen mode

A corresponding adapter is available for the pooled client as well.

Installation

The package requires PHP 8.3 or newer and supports Swoole 5.x or OpenSwoole 22.1.2 and newer.

Install it with Composer:

composer require small/swoole-symfony-http-client
Enter fullscreen mode Exit fullscreen mode

Small, interoperable and thoroughly tested

The purpose of this project is focused: provide a Swoole-native HTTP transport without asking Symfony applications to abandon Symfony contracts.

You get:

  • A coroutine-friendly, non-blocking transport
  • Symfony HttpClientInterface compatibility
  • PSR-18 interoperability
  • Optional connection pooling and flow control
  • 100% project code coverage
  • Execution against Symfony's official HTTP client reference tests

For an HTTP client, correctness matters as much as performance. The combination of complete coverage and validation against Symfony's own tests provides a stronger foundation for using the library in real applications.

The project is open source under the MIT license. Feedback, bug reports and contributions are welcome.

Top comments (0)