amphp/http-client

repository·5.x·Indexed 20 days ago

https://github.com/amphp/http-client

An asynchronous HTTP client for PHP built on the Revolt event loop. It supports HTTP/1 and HTTP/2 without requiring the ext/curl extension, featuring connection pooling, multiplexing, and concurrent requests. The library provides a standards-compliant API with support for secure-by-default TLS, transparent redirect following, cookie and session management, and custom behavior via Application and Network interceptors.

Tokens
2.9K
Snippets
9
Records
12
Agent score
73%

What's inside amphp/http-client

  1. Overview of amphp/http-client features

    5.x

    The amphp/http-client is an event-driven, asynchronous HTTP client for PHP. Key capabilities include:

    • Protocol Support: Supports HTTP/1 and HTTP/2 with connection pooling (keep-alive for HTTP/1.1 and multiplexing for HTTP/2).
    • Concurrency: Requests are concurrent by default.
    • Resource Management: Streams entity bodies for efficient memory management during large transfers and automatically decodes compressed bodies (gzip, deflate).
    • Security & Standards: Implements secure-by-default TLS (https://), supports all standard and custom HTTP methods, and simplifies form submissions.
    • Advanced Features: Transparently follows redirects, manages cookies and sessions, and functions behind HTTP proxies.
  2. Configure Redirect Behavior

    5.x

    By default, HttpClientBuilder follows up to ten redirects. You can customize or disable this using HttpClientBuilder::followRedirects(int $limit). Setting the limit to 0 disables automatic redirects.

    Redirect Policy Details:

    • The FollowRedirects interceptor only follows redirects for the GET method. For other methods receiving 307 or 308 responses, the response is returned as-is.
    • Cross-origin redirects are attempted without any headers set; application headers are discarded for security.
    • If using HttpClientBuilder, the FollowRedirects interceptor is the outermost one. To ensure headers are present in the response of a redirected request, it is recommended to set headers via interceptors rather than directly on the Request object.
  3. Customize behavior with Interceptors

    5.x

    Interceptors allow you to compose custom behavior into the HttpClient. You can register them using HttpClientBuilder::intercept().

    There are two types of interceptors:

    1. ApplicationInterceptor: The standard type used for most logic (e.g., adding headers, caching, retries).
    2. NetworkInterceptor: Used when you need access to underlying connection properties like IPs or TLS settings.

    Warning: NetworkInterceptor implementations must be highly performant to avoid triggering client timeouts, as they run after the connection is established.

    Common Interceptors:

    • AddRequestHeader / AddResponseHeader
    • RemoveRequestHeader / RemoveResponseHeader
    • SetRequestHeader / SetResponseHeader
    • RetryRequests
    • FollowRedirects
    • SetRequestTimeout
    • CookieHandler (via amphp/http-client-cookies)
    • PrivateCache (via amphp/http-client-cache)
    use Amp\Http\Client\HttpClientBuilder;
    use Amp\Http\Client\Interceptor\SetRequestHeader;
    use Amp\Http\Client\Interceptor\SetResponseHeader;
    use Amp\Http\Client\Request;
    
    $client = (new HttpClientBuilder())
        ->intercept(new SetRequestHeader('x-foo', 'bar'))
        ->intercept(new SetResponseHeader('x-tea', 'now'))
        ->build();
    
    $response = $client->request(new Request("https://httpbin.org/get"));
  4. Quickstart: Use the HttpClient

    5.x

    The primary way to interact with the library is through the HttpClient class. You can create a default client using HttpClientBuilder::buildDefault(). In its simplest form, you can pass a URL string to request(), which defaults to a GET request.

    Note that HttpClient returns a Response object as soon as the headers are received. To get the full body, you must buffer the payload.

    use Amp\
    Http\\Client\\HttpClientBuilder;
    use Amp\\Http\\Client\\Request;
    
    $client = HttpClientBuilder::buildDefault();
    
    $response = $client->request(new Request("https://httpbin.org/get"));
    
    var_dump($response->getStatus());
    var_dump($response->getHeaders());
    var_dump($response->getBody()->buffer());
  5. Install amphp/http-client via Composer

    5.x

    Install the asynchronous HTTP client using Composer. This package provides an HTTP client based on Revolt that supports HTTP/1 and HTTP/2 without requiring ext/curl.

    composer require amphp/http-client
  6. Log requests to HTTP Archive (HAR)

    5.x

    You can log all requests and responses, including detailed timing information, to an HTTP Archive (HAR) file using the LogHttpArchive event listener. This file can be imported into browser developer tools or online HAR viewers.

    Warning: HAR files may contain sensitive information in URLs or headers. Use caution when sharing them.

    use Amp\Http\Client\HttpClientBuilder;
    use Amp\Http\Client\EventListener\LogHttpArchive;
    
    $httpClient = (new HttpClientBuilder())
        ->listen(new LogHttpArchive('/tmp/http-client.har'))
        ->build();
    
    $httpClient->request(...);
  7. Handle an HTTP Response

    5.x

    The Response object provides access to the server's response. Like requests, Response objects are mutable.

    • Status: Use getStatus() (integer) and getReason() (string) to check the HTTP status.
    • Protocol: Use getProtocolVersion() to get the HTTP version.
    • Headers: Access headers via hasHeader(), getHeader(), getHeaderArray(), or getHeaders(). The getHeaders() method returns an associative array where keys are header names and values are arrays of strings:
      ["header-1" => ["value-1", "value-2"], "header-2" => ["value-1"]]
    • Body: getBody() returns a Payload.
      • Warning: $response->getBody()->read() reads only a single chunk. To get the entire body, use $response->getBody()->buffer().
    • Request Context:
      • getRequest(): Returns the request corresponding to the response (may differ from original if redirects occurred).
      • getOriginalRequest(): Returns the initial request sent by the client.
      • getPreviousResponse(): Accesses previous responses in a redirect chain (note: bodies of previous responses are discarded).
    $response = $client->request($request);
    
    var_dump($response->getStatus(), $response->getReason());
    var_dump($response->getProtocolVersion());
    $body = $response->getBody()->buffer();
  8. Configure a Request

    5.x

    The Request class is used to define the details of an HTTP request.

    Important: Request objects are mutable. If you need to retry a request or use it for sub-requests, you should clone the object to perform a deep clone.

    Key capabilities:

    • URI: Set via constructor or setUri(string $uri).
    • Method: Set via constructor or setMethod(string $method). Defaults to GET.
    • Headers:
      • setHeader(string $field, string $value): Replaces existing values for the field.
      • addHeader(string $field, string $value): Adds an additional header line.
      • setHeaders(array $headers): Sets multiple headers at once.
      • getHeader(string $field): Returns the first header value or null.
      • getHeaderArray(string $field): Returns all header values for a field as an array.
    • Body: Set via setBody($body). Accepts string, null, or HttpContent. Strings and nulls are automatically converted to HttpContent to ensure the body can be re-sent during redirects or retries.
    $request = new Request("https://httpbin.org/post", "POST");
    $request->setBody("foobar");
    $request->setUri("https://google.com/");
    $request->setMethod("PUT");
    $request->setHeader("X-Foobar", "Hello World");
  9. Use ModifyRequest to transform outgoing requests

    5.x

    The ModifyRequest interceptor allows you to apply transformations to an outgoing Request using a provided mapper closure. This is useful for tasks like adding default headers, modifying URLs, or injecting authentication tokens globally for all requests passing through the interceptor.

    The mapper closure must accept a Request and return either a modified Request instance or null. If the closure returns null, the original request is used without modification.

    ModifyRequest implements both NetworkInterceptor and ApplicationInterceptor, meaning it can be used at different layers of the HTTP client stack.

    use Amp".$
        Http".$
        Client".$
        Interceptor".$
        ModifyRequest;
    use Amp".$
        Http".$
        Client".$
        Request;
    
    // Example: An interceptor that adds a custom User-Agent header to every request
    $userAgentInterceptor = new ModifyRequest(function (Request $request): Request {
        return $request->withHeader('User-Agent', 'MyCustomApp/1.0');
    });
    
    // This interceptor can then be added to your HttpClient stack
  10. Use ModifyResponse to transform incoming responses

    5.x

    The ModifyResponse interceptor allows you to apply a transformation function to an incoming Response object. It can be used as either a NetworkInterceptor or an ApplicationInterceptor.

    To use it, pass a closure to the constructor. This closure receives the original Response and must return either a new Response object or null (in which case the original response is returned).

    When used as an ApplicationInterceptor, it also registers the mapper via $request->interceptPush(), ensuring that the transformation logic is applied even if the request is processed through different layers of the client stack.

    ```php
    use Amp\Http\Client\Interceptor\ModifyResponse;
    use Amp\\
  11. Handle HTTP-related errors with HttpException

    5.x
    When working with amphp/http-client, all library-specific errors inherit from Amp\Http\Client\HttpException. You should catch this exception to handle failures related to the HTTP client's operation, such as connection issues or protocol errors, separately from general PHP exceptions.
  12. Handle TooManyRedirectsException

    5.x

    When an HTTP request exceeds the configured redirect limit or enters a redirect loop, the Amp\Http\Client\Interceptor\TooManyRedirectsException is thrown. This exception extends Amp\Http\Client\HttpException. You can catch this exception to inspect the last response received before the limit was reached by calling getResponse().

    use Amp\Http\Client\Interceptor\TooManyRedirectsException;
    
    try {
        $response = $client->request($request);
    } catch (TooManyRedirectsException $exception) {
        $lastResponse = $exception->getResponse();
        // Handle the redirect loop or limit exceeded case
    }