How do use Guzzle 7 fully asynchronously with ReactPHP event loop without blocking for I/O?

430 Views Asked by At

I have a DiscordPHP bot that I'm trying to download some web pages that require a cookie. It seems I need to use a curl handler with Guzzle because the ReactPHP http browser doesn't support cookies.

I've created this minimal script:

use GuzzleHttp\Client;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\HandlerStack;
use Psr\Http\Message\ResponseInterface;

include 'vendor/autoload.php';

$loop = \React\EventLoop\Loop::get();

$curl = new CurlMultiHandler;
$handler = HandlerStack::create($curl);
$client = new Client( [ /* 'cookies' => $cookieJar, */ 'handler' => $handler ] );

$promise = $client->getAsync('https://httpstat.us/200')
    ->then(
        function (ResponseInterface $res) {
            echo 'response: ' . $res->getStatusCode() . PHP_EOL;
        },
        function (\Exception $e) {
            echo $e->getMessage() . PHP_EOL;
        }
    );


$timer = $loop->addPeriodicTimer(0.5, function () use ($curl, $promise) {
    if ($promise->getState() === 'pending') {
    echo 'Tick ';
    $curl->tick();
    }
    echo 'Beat ';
});

$loop->run();

This exits immediately without the code adding the addPeriodicTimer to check pending and call tick() manually:

$ time php minimal.php
0.022u 0.010s 0:00.03 100.0%    0+0k 0+0io 67pf+0w

With the timer, it works as expected:

Tick Beat Tick Beat Tick Beat Tick Beat Tick Beat Tick Beat Tick response: 200
Beat Beat Beat ...

The idea to use a tick() came from this 73 comment closed thread on github.com.

There are some similar questions, but none of them seem to address this issue:

How do I start a HTTP GET with a cookie jar and get the response in a ReactPHP event loop without using a blocking call such as ->wait() or manually tick()ing curl's handler?

1

There are 1 best solutions below

3
Simon Frings On

the ReactPHP http browser doesn't support cookies.

Well, ReactPHP doesn't set cookies automatically, there's already a ticket discussing this topic: https://github.com/reactphp/http/issues/445, but you can still go the manual way and set HTTP cookie header.

It's also worth mentioning that using ReactPHP with Guzzle won't work as Guzzle will block ReactPHP's eventloop. This means you can send multiple requests, but can't do anything else asynchronously.