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
Handle errors at each step of the HTTP request lifecycle.
Drops support for PHP 7.0 and 7.1 since WordPress is dropping that support, too.
Provide await_next_event() as a single, filterable interface for consuming all the HTTP activity. Remove the onProgress callback and various other ways of waiting for information on specific requests.
Introduce an internal event_loop_tick() function that runs all the available non-blocking operations.
Move all the logic from functions into the Client class. It is now less generic, but I'd argue it already wasn't that generic and at least now we can avoid going back and froth between functions and that class.
Support Transfer-Encoding: chunked, Transfer-Encoding: gzip, and Content-Encoding: gzip via stream wrappers.
Remove most of the complexity associated with making PHP streams central to how the library works. In this version, the focus is on the Client object so we no longer have to go out of our way to store data in stream context, struggle with stream filters, passthrough data between stream wrappers layers etc.
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:
$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
Unit tests.
Abundant inline documentation with examples and explanation of technical decisions.
Standard way of piping HTTP responses into ZIP processor, XML processor, HTML tag processor etc.
Find a useful way of treating HTTP error codes such as 404 or 501. Currently these requests are marked as "finished", not "failed", because the connection was successfully created and the server replied with a valid HTTP response. Perhaps it's fine not to do that. This could be a lower-level library and that behavior could belong to a higher-level client.
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
await_next_event()
as a single, filterable interface for consuming all the HTTP activity. Remove theonProgress
callback and various other ways of waiting for information on specific requests.event_loop_tick()
function that runs all the available non-blocking operations.Client
class. It is now less generic, but I'd argue it already wasn't that generic and at least now we can avoid going back and froth between functions and that class.Transfer-Encoding: chunked
,Transfer-Encoding: gzip
, andContent-Encoding: gzip
via stream wrappers.Client
object so we no longer have to go out of our way to store data in stream context, struggle with stream filters, passthrough data between stream wrappers layers etc.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
HTTP Proxy example
Future work
cc @dmsnell @MayPaw @reimic