Skip to content

Commit

Permalink
Merge pull request #1 from rtckit/v0.8.5
Browse files Browse the repository at this point in the history
v0.8.5
  • Loading branch information
cdosoftei authored Feb 8, 2022
2 parents f868f94 + 8b70f55 commit 23c7201
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 53 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "rtckit/react-esl",
"description": "Asynchronous FreeSWITCH Event Socket Layer (ESL) Library",
"version": "0.8.0",
"version": "0.8.5",
"type": "library",
"keywords": [
"freeswitch",
Expand Down Expand Up @@ -34,7 +34,7 @@
"clue/stdio-react": "^2.5",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^9.5",
"vimeo/psalm": "^4.18"
"vimeo/psalm": "^4.20"
},
"autoload": {
"psr-4": {
Expand Down
36 changes: 22 additions & 14 deletions src/InboundClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
Response,
ResponseInterface
};
USE Closure;
use Closure;
use Throwable;

/**
Expand Down Expand Up @@ -105,12 +105,18 @@ public function connect(): PromiseInterface
* Processes raw inbound bytes
*
* @param string $chunk
* @throws ReactESLException
*/
protected function dataHandler(string $chunk): void
{
$responses = [];
$ret = $this->esl->consume($chunk, $responses);

try {
$ret = $this->esl->consume($chunk, $responses);
} catch (Throwable $t) {
$this->emit('error', [$t]);

return;
}

assert(!is_null($responses));

Expand Down Expand Up @@ -143,26 +149,28 @@ protected function dataHandler(string $chunk): void
continue;
}

if (empty($this->queue)) {
$deferred = array_shift($this->queue);

if (is_null($deferred)) {
$contentType = $response->getHeader(AbstractHeader::CONTENT_TYPE) ?? '';

throw new ReactESLException(
'Unexpected reply received (ENOMSG) ' . $contentType,
defined('SOCKET_ENOMSG') ? SOCKET_ENOMSG : 42
$this->emit(
'error',
[new ReactESLException(
'Unexpected reply received (ENOMSG) ' . $contentType,
defined('SOCKET_ENOMSG') ? SOCKET_ENOMSG : 42
)]
);
} else {
$deferred->resolve($response);
}

$deferred = array_shift($this->queue);
$deferred->resolve($response);
}
}

/**
* Processes early inbound messages (authentication)
*
* @param MessageInterface $response
*
* @throws ReactESLException
*/
public function preAuthHandler(MessageInterface $response): void
{
Expand All @@ -181,14 +189,14 @@ public function preAuthHandler(MessageInterface $response): void
$this->authenticated = true;
$this->deferredConnect->resolve($this);
} else {
throw new ReactESLException('Authentication failed');
$this->deferredConnect->reject(new ReactESLException('Authentication failed'));
}
} else if ($response instanceof Response\TextDisconnectNotice) {
$this->emit('disconnect', [$response]);
} else if ($response instanceof Response\TextRudeRejection) {
$this->deferredConnect->reject(new ReactESLException('Access denied'));
} else {
throw new ReactESLException('Unexpected response (expecting auth/request)');
$this->deferredConnect->reject(new ReactESLException('Unexpected response (expecting auth/request)'));
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/InboundServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Request
};
use Closure;
use Throwable;

/**
* Inbound ESL Server class
Expand Down Expand Up @@ -69,7 +70,13 @@ protected function connectionHandler(DuplexStreamInterface $stream): void
$client = new RemoteInboundClient($esl);

$stream->on('data', function (string $chunk) use ($client, $esl) {
$esl->consume($chunk, $requests);
try {
$esl->consume($chunk, $requests);
} catch (Throwable $t) {
$client->emit('error', [$t]);

return;
}

assert(!is_null($requests));

Expand Down
10 changes: 7 additions & 3 deletions src/OutboundClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,16 @@ public function connect(): PromiseInterface
* Processes raw inbound bytes
*
* @param string $chunk
* @throws ReactESLException
*/
protected function dataHandler(string $chunk): void
{
$requests = [];
$ret = $this->esl->consume($chunk, $requests);
try {
$this->esl->consume($chunk, $requests);
} catch (Throwable $t) {
$this->emit('error', [$t]);

return;
}

assert(!is_null($requests));

Expand Down
9 changes: 8 additions & 1 deletion src/OutboundServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
Response
};
use Closure;
use Throwable;

/**
* Outbound ESL Server class
Expand Down Expand Up @@ -74,7 +75,13 @@ protected function connectionHandler(DuplexStreamInterface $stream): void
$client = new RemoteOutboundClient($esl);

$stream->on('data', function (string $chunk) use ($client, $esl) {
$esl->consume($chunk, $responses);
try {
$esl->consume($chunk, $responses);
} catch (Throwable $t) {
$client->emit('error', [$t]);

return;
}

assert(!is_null($responses));

Expand Down
55 changes: 34 additions & 21 deletions tests/InboundClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ public function testConnectFailed(): void
$this->assertFalse($this->isPropertyInitialized($client, 'esl'));
}

public function testDataHandlerOnParsingError(): void
{
$context = $this->prepareClientAndConnectors();
$this->setPropertyValue($context->client, 'authenticated', true);
$dataHandler = $this->getMethod($context->client, 'dataHandler');

$this->setPropertyValue($context->client, 'esl', new AsyncConnection(AsyncConnection::INBOUND_CLIENT));

$context->client->on('error', function (\Throwable $t) {
$this->assertInstanceOf(ESL\Exception\ESLException::class, $t);
});

$dataHandler->invokeArgs($context->client, ["Content-Type: bogus/no-such-thing\n\n"]);
}

public function testDataHandlerOnPreAuth(): void
{
$context = $this->prepareClientAndConnectors();
Expand Down Expand Up @@ -253,6 +268,10 @@ public function testDataHandlerOnOutOfSequence(): void

$response = "Content-Type: api/response\n\n";

$context->client->on('error', function (\Throwable $t) {
$this->assertInstanceOf(ReactESLException::class, $t);
});

$context->esl
->method('consume')
->with($response, [])
Expand All @@ -262,7 +281,6 @@ public function testDataHandlerOnOutOfSequence(): void
return ESL\Connection::SUCCESS;
}));

$this->expectException(ReactESLException::class);
$dataHandler->invokeArgs($context->client, [$response]);
}

Expand Down Expand Up @@ -334,17 +352,6 @@ public function testPreAuthHandlerOnCommandReplySuccess(): void
$this->assertTrue($this->getPropertyValue($context->client, 'authenticated'));
}

public function testPreAuthHandlerOnCommandReplyFailure(): void
{
$context = $this->prepareClientAndConnectors();
$preAuthHandler = $this->getMethod($context->client, 'preAuthHandler');

$this->expectException(ReactESLException::class);
$preAuthHandler->invokeArgs($context->client, [
(new ESL\Response\CommandReply)->setHeader(ESL\AbstractHeader::REPLY_TEXT, '-ERR invalid')
]);
}

public function testPreAuthHandlerOnDisconnectNotice(): void
{
$context = $this->prepareClientAndConnectors();
Expand Down Expand Up @@ -377,8 +384,22 @@ public function testPreAuthHandlerOnUnexpectedResponse(): void
$context = $this->prepareClientAndConnectors();
$preAuthHandler = $this->getMethod($context->client, 'preAuthHandler');

$this->expectException(ReactESLException::class);
$preAuthHandler->invokeArgs($context->client, [new ESL\Response\ApiResponse]);
$context->promise
->then(function () {
$this->fail('Should never resolve');
})
->otherwise(function (\Throwable $t) {
$this->assertInstanceOf(ReactESLException::class, $t);
});
}

public function testClose(): void
{
$context = $this->prepareClientAndConnectors();

$context->stream->expects($this->once())->method('close');
$context->client->close();
}

private function prepareClientAndConnectors(): stdClass
Expand Down Expand Up @@ -408,12 +429,4 @@ private function prepareClientAndConnectors(): stdClass

return $ret;
}

public function testClose(): void
{
$context = $this->prepareClientAndConnectors();

$context->stream->expects($this->once())->method('close');
$context->client->close();
}
}
15 changes: 6 additions & 9 deletions tests/InboundServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,22 @@ public function testConnectionHandler(): void
if ($response instanceof ESL\Response\AuthRequest) {
return (new ESL\Request\Auth)->setParameters('ClueCon')->render();
}

return '';
} catch (ESL\Exception\ESLException $e) {
$request = ESL\Request::parse($bytes);

$this->assertInstanceOf(ESL\Request\Api::class, $request);
$this->assertEquals('version', $request->getParameters());
}
} catch (ESL\Exception\ESLException $e) {}

return $bytes;
});

$server->on('auth', function (RemoteInboundClient $client, ESL\Request\Auth $request) use ($stream) {
$client->on('error', function (\Throwable $t) {
$this->assertInstanceOf(ESL\Exception\ESLException::class, $t);
});

$this->assertEquals('ClueCon', $request->getParameters());

$client->send((new ESL\Response\CommandReply)->setHeader('reply-text', '+OK accepted'));
$client->setAuthenticated(true);

$stream->write("api version\n\n");
$stream->write("bogus\n\n");
});

$connHandler->invokeArgs($server, [$stream]);
Expand Down
6 changes: 6 additions & 0 deletions tests/OutboundClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ public function testDataHandler(): void
"linger\n\n" .
"myevents json\n\n"
]);

$client->on('error', function (\Throwable $t) {
$this->assertInstanceOf(ESL\Exception\ESLException::class, $t);
});

$dataHandler->invokeArgs($client, ["bogus\n\n"]);
}

public function testClose(): void
Expand Down
9 changes: 7 additions & 2 deletions tests/OutboundServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ public function testConnectionHandler(): void
$client->on('error', function (\Throwable $t) {
if ($t instanceof ReactESLException) {
$this->assertTrue(true, 'Received out of sequence exception');
} else if ($t instanceof ESL\Exception\ESLException) {
$this->assertTrue(true, 'Received parsing exception');
} else {
$this->fail('Unrecognized exception');
}
});

$stream->write("content-type: api/response\nexpected: false\n\n");

$deferred = new Deferred;
$this->setPropertyValue($client, 'queue', [$deferred]);

Expand All @@ -146,6 +146,11 @@ public function testConnectionHandler(): void
$this->assertEquals('true', $response->getHeader('expected'));
});

$this->setPropertyValue($client, 'queue', []);
$stream->write("content-type: api/response\nexpected: false\n\n");

$stream->write("content-type: bogus\n\n");

$stream->close();
});

Expand Down

0 comments on commit 23c7201

Please sign in to comment.