HTTP
Event-driven, streaming HTTP client and server implementation for ReactPHP.
This HTTP library provides re-usable implementations for an HTTP client and
server based on ReactPHP's Socket and
EventLoop components.
Its client component allows you to send any number of async HTTP/HTTPS requests
concurrently.
Its server component allows you to build plaintext HTTP and secure HTTPS servers
that accept incoming HTTP requests from HTTP clients (such as web browsers).
This library provides async, streaming means for all of this, so you can handle
multiple concurrent HTTP requests without blocking.
Table of contents
- Quickstart example
- Client Usage
- Server Usage
- API
- Install
- Tests
- License
Quickstart example
Once installed, you can use the following code to access an HTTP web server and send some simple HTTP GET requests:
<?php
require __DIR__ . '/vendor/autoload.php';
$client = new React\Http\Browser();
$client->get('http://www.google.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
var_dump($response->getHeaders(), (string)$response->getBody());
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});This is an HTTP server which responds with Hello World! to every request.
<?php
require __DIR__ . '/vendor/autoload.php';
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
return React\Http\Message\Response::plaintext(
"Hello World!\n"
);
});
$socket = new React\Socket\SocketServer('127.0.0.1:8080');
$http->listen($socket);See also the examples.
Client Usage
Request methods
Most importantly, this project provides a Browser object that
offers several methods that resemble the HTTP protocol methods:
$browser->get($url, array $headers = array());
$browser->head($url, array $headers = array());
$browser->post($url, array $headers = array(), string|ReadableStreamInterface $body = '');
$browser->delete($url, array $headers = array(), string|ReadableStreamInterface $body = '');
$browser->put($url, array $headers = array(), string|ReadableStreamInterface $body = '');
$browser->patch($url, array $headers = array(), string|ReadableStreamInterface $body = '');Each of these methods requires a $url and some optional parameters to send an
HTTP request. Each of these method names matches the respective HTTP request
method, for example the get() method sends an HTTP GET request.
You can optionally pass an associative array of additional $headers that will be
sent with this HTTP request. Additionally, each method will automatically add a
matching Content-Length request header if an outgoing request body is given and its
size is known and non-empty. For an empty request body, if will only include a
Content-Length: 0 request header if the request method usually expects a request
body (only applies to POST, PUT and PATCH HTTP request methods).
If you're using a streaming request body, it will default
to using Transfer-Encoding: chunked unless you explicitly pass in a matching Content-Length
request header. See also streaming request for more details.
By default, all of the above methods default to sending requests using the
HTTP/1.1 protocol version. If you want to explicitly use the legacy HTTP/1.0
protocol version, you can use the withProtocolVersion()
method. If you want to use any other or even custom HTTP request method, you can
use the request() method.
Each of the above methods supports async operation and either fulfills with a
PSR-7 ResponseInterface
or rejects with an Exception.
Please see the following chapter about promises for more details.
Promises
Sending requests is async (non-blocking), so you can actually send multiple
requests in parallel.
The Browser will respond to each request with a
PSR-7 ResponseInterface
message, the order is not guaranteed.
Sending requests uses a Promise-based
interface that makes it easy to react to when an HTTP request is completed
(i.e. either successfully fulfilled or rejected with an error):
$browser->get($url)->then(
function (Psr\Http\Message\ResponseInterface $response) {
var_dump('Response received', $response);
},
function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
}
);If this looks strange to you, you can also use the more traditional blocking API.
Keep in mind that resolving the Promise with the full response message means the whole response body has to be kept in memory. This is easy to get started and works reasonably well for smaller responses (such as common HTML pages or RESTful or JSON API requests).
You may also want to look into the streaming API:
- If you're dealing with lots of concurrent requests (100+) or
- If you want to process individual data chunks as they happen (without having to wait for the full response body) or
- If you're expecting a big response body size (1 MiB or more, for example when downloading binary files) or
- If you're unsure about the response body size (better be safe than sorry when accessing arbitrary remote HTTP endpoints and the response body size is unknown in advance).
Cancellation
The returned Promise is implemented in such a way that it can be cancelled when it is still pending. Cancelling a pending promise will reject its value with an Exception and clean up any underlying resources.
$promise = $browser->get($url);
Loop::addTimer(2.0, function () use ($promise) {
$promise->cancel();
});Timeouts
This library uses a very efficient HTTP implementation, so most HTTP requests
should usually be completed in mere milliseconds. However, when sending HTTP
requests over an unreliable network (the internet), there are a number of things
that can go wrong and may cause the request to fail after a time. As such, this
library respects PHP's default_socket_timeout setting (default 60s) as a timeout
for sending the outgoing HTTP request and waiting for a successful response and
will otherwise cancel the pending request and reject its value with an Exception.
Note that this timeout value covers creating the underlying transport connection, sending the HTTP request, receiving the HTTP response headers and its full response body and following any eventual redirects. See also redirects below to configure the number of redirects to follow (or disable following redirects altogether) and also streaming below to not take receiving large response bodies into account for this timeout.
You can use the withTimeout() method to pass a custom timeout
value in seconds like this:
$browser = $browser->withTimeout(10.0);
$browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
// response received within 10 seconds maximum
var_dump($response->getHeaders());
}, function (Exception $e) {
echo 'Error: ' . $e->