Skip to main content
The bundled drivers cover the most common HTTP libraries, but there are situations where you need a custom integration — perhaps with a proprietary HTTP library, a legacy system, or a specialized transport. This chapter shows how to create a custom driver, register it with the driver registry, and use it through the standard client API.

The Driver Contract

Every driver must implement the CanHandleHttpRequest interface, which defines a single method:
The method receives an HttpRequest and returns an HttpResponse. That is the entire contract. The driver is responsible for converting these value objects into whatever the underlying HTTP library expects.

Creating a Custom Driver

Here is a template for a custom driver:
The key points are:
  • Accept HttpClientConfig, CanHandleEvents, and an optional vendor client instance in the constructor. This matches the signature expected by the driver registry.
  • Return HttpResponse::sync() for buffered responses and HttpResponse::streaming() for streamed responses.
  • Wrap vendor exceptions in HttpRequestException to maintain a consistent exception hierarchy.

Registering the Driver

To make your driver available by name (e.g., 'acme'), register it with the driver registry:
Then use it through the builder:
The factory function receives the config, events dispatcher, and optional client instance. This lets users pass a pre-configured vendor client through withClientInstance('acme', $myClient).

Injecting a Driver Directly

If you do not need the registry, bypass it entirely by passing a driver instance:
Or use the static shorthand:

Reusing Vendor Client Instances

When your vendor client requires special setup (custom SSL certificates, proxy configuration, connection pools), create the instance yourself and pass it through:
This pattern works with any registered driver. The withClientInstance() method sets both the driver name and the instance, so the driver factory receives it instead of creating its own.

Streaming in Custom Drivers

The HttpResponse::streaming() factory accepts a StreamInterface implementation. The simplest approach is to yield chunks from a generator and wrap them with BufferedStream::fromStream():
The BufferedStream, ArrayStream, IterableStream, and TransformStream classes in the Cognesy\Http\Stream namespace provide various stream implementations you can use or compose.

See Also