diff --git a/README.md b/README.md index 51bbef1e..d705193f 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,8 @@ $server->on('error', function (Exception $e) { ``` The server will also emit an `error` event if you return an invalid -type in the callback function or have a unhandled `Exception`. -If your callback function throws an exception, +type in the callback function or have a unhandled `Exception` or `Throwable`. +If your callback function throws an `Exception` or `Throwable`, the `Server` will emit a `RuntimeException` and add the thrown exception as previous: @@ -540,9 +540,9 @@ $server = new Server(function (ServerRequestInterface $request) use ($stream) { }); ``` -An invalid return value or an unhandled `Exception` in the code of the callback -function, will result in an `500 Internal Server Error` message. -Make sure to catch `Exceptions` to create own response messages. +An invalid return value or an unhandled `Exception` or `Throwable` in the code +of the callback function, will result in an `500 Internal Server Error` message. +Make sure to catch `Exceptions` or `Throwables` to create own response messages. After the return in the callback function the response will be processed by the `Server`. The `Server` will add the protocol version of the request, so you don't have to. diff --git a/composer.json b/composer.json index e73e29c7..e040c784 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "ringcentral/psr7": "^1.2", "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.6", - "react/promise": "^2.1 || ^1.2.1", + "react/promise": "^2.3 || ^1.2.1", "evenement/evenement": "^2.0 || ^1.0" }, "autoload": { diff --git a/src/Server.php b/src/Server.php index 6f9236e4..aebc9c8e 100644 --- a/src/Server.php +++ b/src/Server.php @@ -257,7 +257,14 @@ function ($response) use ($that, $conn, $request) { function ($error) use ($that, $conn, $request) { $message = 'The response callback is expected to resolve with an object implementing Psr\Http\Message\ResponseInterface, but rejected with "%s" instead.'; $message = sprintf($message, is_object($error) ? get_class($error) : gettype($error)); - $exception = new \RuntimeException($message, null, $error instanceof \Exception ? $error : null); + + $previous = null; + + if ($error instanceof \Throwable || $error instanceof \Exception) { + $previous = $error; + } + + $exception = new \RuntimeException($message, null, $previous); $that->emit('error', array($exception)); return $that->writeError($conn, 500, $request); diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 61ef1fbd..4ea1722a 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -2805,6 +2805,52 @@ function ($data) use (&$buffer) { $this->assertEquals('hello', $exception->getPrevious()->getMessage()); } + /** + * @requires PHP 7 + */ + public function testThrowableThrowInCallBackFunctionWillResultInErrorMessage() + { + $server = new Server(function (ServerRequestInterface $request) { + throw new \Error('hello'); + }); + + $exception = null; + $server->on('error', function (\Exception $ex) use (&$exception) { + $exception = $ex; + }); + + $buffer = ''; + $this->connection + ->expects($this->any()) + ->method('write') + ->will( + $this->returnCallback( + function ($data) use (&$buffer) { + $buffer .= $data; + } + ) + ); + + $server->listen($this->socket); + $this->socket->emit('connection', array($this->connection)); + + $data = $this->createGetRequest(); + + try { + $this->connection->emit('data', array($data)); + } catch (\Error $e) { + $this->markTestSkipped( + 'A \Throwable bubbled out of the request callback. ' . + 'This happened most probably due to react/promise:^1.0 being installed ' . + 'which does not support \Throwable.' + ); + } + + $this->assertInstanceOf('RuntimeException', $exception); + $this->assertContains("HTTP/1.1 500 Internal Server Error\r\n", $buffer); + $this->assertEquals('hello', $exception->getPrevious()->getMessage()); + } + public function testRejectOfNonExceptionWillResultInErrorMessage() { $server = new Server(function (ServerRequestInterface $request) {