Failing connections cause exceptions thrown outside of Promise
one-horned-flying opened this issue · 2 comments
I'm getting this error when connecting to RabbitMQ
PHP Fatal error: Uncaught Bunny\Exception\ClientException: Broken pipe or closed connection. in /.../vendor/bunny/bunny/src/Bunny/AbstractClient.php:289
This happens because when I connect, I'm connecting with specific credentials and RabbitMQ is denying the connection. When this happens the stream is closed so we get an end of file:
bunny/src/Bunny/AbstractClient.php
Lines 288 to 290 in d0dec8f
I have multiple connections opened by different users, so I need to handle this gracefully. This is the extension I've written to handle the problem.
I'm using https://packagist.org/packages/evenement/evenement as well.
Hope this helps.
<?php
declare(strict_types=1);
namespace MyClient;
use Bunny\Async\Client;
use Evenement\EventEmitterInterface;
use Evenement\EventEmitterTrait;
use React\Promise;
class BunnyAsyncClient extends Client implements EventEmitterInterface
{
use EventEmitterTrait;
/**
*
*/
public function onDataAvailable()
{
try {
parent::onDataAvailable();
} catch (\Throwable $e) {
$this->eventLoop->removeReadStream($this->getStream());
$this->eventLoop->futureTick(function () use ($e) {
$this->emit('error', [$e, $this]);
});
}
}
/**
* @return Promise\PromiseInterface
*/
public function connect()
{
$deferred = new Promise\Deferred();
$errBack = function (\Throwable $e) use ($deferred, &$errBack) {
$this->removeListener('error', $errBack);
$deferred->reject($e);
};
$this->on('error', $errBack);
parent::connect()->then(
function () use ($deferred) {
return $deferred->resolve($this);
},
function (\Throwable $e) use ($deferred) {
// needed in case rejected not by the errBack
$deferred->reject($e);
}
)->always(function () use ($errBack) {
$this->removeListener('error', $errBack);
});
return $deferred->promise();
}
}
I noticed when this exception caused,call $client->isConnected() gave you the wrong result which is true while I was expecting false.
I'm wondering if it can be fixed by just write following code in that if condition:
if (@feof($this->stream)) {
$this->eventLoop->removeReadStream($this->getStream());
$this->state = ClientStateEnum::ERROR;
throw new ClientException("Broken pipe or closed connection.");
}
A similar issue occurs when disconnecting a client that has not opened a channel yet.
======================================================================
The application has thrown an exception!
======================================================================
Bunny\Exception\ClientException
Connection closed by server: CHANNEL_ERROR - expected 'channel.open'
----------------------------------------------------------------------
/var/www/vendor/bunny/bunny/src/Bunny/AbstractClient.php:448
#0 /var/www/vendor/bunny/bunny/src/Bunny/Async/Client.php(306): Bunny\AbstractClient->onFrameReceived(Object(Bunny\Protocol\MethodConnectionCloseFrame))
#1 /var/www/vendor/react/event-loop/src/StreamSelectLoop.php(245): Bunny\Async\Client->onDataAvailable(Resource id #2073)
#2 /var/www/vendor/react/event-loop/src/StreamSelectLoop.php(212): React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)
#3 /var/www/Common/src/Adapter/Cli/CliController.php(88): React\EventLoop\StreamSelectLoop->run()