WordPress / blueprints-library

32 stars 7 forks source link

Rewrite AsyncHttp\Client for cleaner API and Transfer-Encoding support #113

Closed adamziel closed 4 months ago

adamziel commented 4 months ago

Refactors the AsyncHttp\Client to simplify the usage and the internal implementation. This will be helpful for rewriting URLs in WordPress posts and downloading the related assets.

As a reminder, AsyncHttp\Client is a PHP HTTP client that can do asynchronous processing of multiple requests without curl or any other dependencies.

Changes

This PR also ships an implementation of a HTTP proxy built with this client library – it could come handy for running an in-browser Git client:

https://github.com/WordPress/blueprints-library/blob/http-client-api-refactir/http_proxy.php

Usage example

$requests = [
    new Request( "https://wordpress.org/latest.zip" ),
    new Request( "https://raw.githubusercontent.com/wpaccessibility/a11y-theme-unit-test/master/a11y-theme-unit-test-data.xml" ),
];

$client = new Client( [
    'concurrency' => 10
] );
$client->enqueue( $requests );

while ( $client->await_next_event() ) {
    $request = $client->get_request();
    echo "Request " . $request->id . ": " . $client->get_event() . " ";
    switch ( $client->get_event() ) {
        case Client::EVENT_BODY_CHUNK_AVAILABLE:
            echo $request->response->received_bytes . "/". $request->response->total_bytes ." bytes received";
            file_put_contents( 'downloads/' . $request->id, $client->get_response_body_chunk(), FILE_APPEND);
            break;
        case Client::EVENT_REDIRECT:
        case Client::EVENT_GOT_HEADERS:
        case Client::EVENT_FINISHED:
            break;
        case Client::EVENT_FAILED:
            echo "– ❌ Failed request to " . $request->url . " – " . $request->error;
            break;
    }
    echo "\n";
}

HTTP Proxy example

// Encode the current request details in a Request object
$requests = [
    new Request(
        $target_url,
        [
            'method' => $_SERVER['REQUEST_METHOD'],
            'headers' => [
                ...getallheaders(),
                // Ensure we won't receive an unsupported content encoding
                // just because the client browser supports it.
                'Accept-Encoding' => 'gzip, deflate',
                'Host' => parse_url($target_url, PHP_URL_HOST),
            ],
            // Naively assume only POST requests have body
            'body_stream' => $_SERVER['REQUEST_METHOD'] === 'POST' ? fopen('php://input', 'r') : null,
        ]
    ),
];

$client = new Client();
$client->enqueue( $requests );

$headers_sent = false;
while ( $client->await_next_event() ) {
    // Pass the response headers and body to the client,
    // Consult the previous example for the details.
}

Future work

cc @dmsnell @MayPaw @reimic