Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Response object in case of HTTP errors #35

Merged
merged 3 commits into from
Jun 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ It shares all properties of the [`Message`](#message) parent class.
The `Request` value object represents the outgoing request to be sent via the [`Browser`](#browser).
It shares all properties of the [`Message`](#message) parent class.

### ResponseException

The `ResponseException` is an `Exception` sub-class that will be used to reject
a request promise if the remote server returns a non-success status code
(anything but 2xx or 3xx).
You can control this behavior via the ["obeySuccessCode" option](#options).

The `getCode()` method can be used to return the HTTP response status code.

The `getResponse()` method can be used to access its underlying [`Response`](#response) object.

## Advanced

### Sender
Expand Down
3 changes: 2 additions & 1 deletion src/Io/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Clue\React\Buzz\Browser;
use React\HttpClient\Client as HttpClient;
use Clue\React\Buzz\Io\Sender;
use Clue\React\Buzz\Message\ResponseException;

class Transaction
{
Expand Down Expand Up @@ -79,7 +80,7 @@ public function onResponse(Response $response, Request $request)

// only status codes 200-399 are considered to be valid, reject otherwise
if ($this->obeySuccessCode && ($response->getCode() < 200 || $response->getCode() >= 400)) {
throw new \RuntimeException('HTTP status code ' . $response->getCode() . ' (' . $response->getReasonPhrase() . ')', $response->getCode());
throw new ResponseException($response);
}

// resolve our initial promise
Expand Down
38 changes: 38 additions & 0 deletions src/Message/ResponseException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Clue\React\Buzz\Message;

use RuntimeException;

/**
* A ResponseException will be returned for valid Response objects that use an HTTP error code
*
* You can access the original Response object via its getter.
*/
class ResponseException extends RuntimeException
{
private $response;

public function __construct(Response $response, $message = null, $code = null, $previous = null)
{
if ($message === null) {
$message = 'HTTP status code ' . $response->getCode() . ' (' . $response->getReasonPhrase() . ')';
}
if ($code === null) {
$code = $response->getCode();
}
parent::__construct($message, $code, $previous);

$this->response = $response;
}

/**
* get Response message object
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
}
11 changes: 9 additions & 2 deletions tests/FunctionalBrowserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,16 @@ public function testInvalidPort()
$this->loop->run();
}

public function testInvalidPath()
public function testErrorStatusCodeRejectsWithResponseException()
{
$this->expectPromiseReject($this->browser->get($this->base . 'status/404'));
$that = $this;
$this->expectPromiseReject($this->browser->get($this->base . 'status/404'))->then(null, function ($e) use ($that) {
$that->assertInstanceOf('Clue\Buzz\React\Message\ResponseException', $e);
$that->assertEquals(404, $e->getCode());

$that->assertInstanceOf('Clue\Buzz\React\Message\Response', $e->getResponse());
$that->assertEquals(404, $e->getResponse()->getCode());
});

$this->loop->run();
}
Expand Down
27 changes: 27 additions & 0 deletions tests/Io/TransactionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Clue\React\Buzz\Io\Transaction;
use Clue\React\Buzz\Message\Response;

class TransactionTest extends TestCase
{
public function testReceivingErrorResponseWillRejectWithResponseException()
{
$request = $this->getMockBuilder('Clue\React\Buzz\Message\Request')->disableOriginalConstructor()->getMock();
$response = new Response('HTTP/1.0', 404, 'File not found');

// mock sender to resolve promise with the given $response in response to the given $request
$sender = $this->getMockBuilder('Clue\React\Buzz\Io\Sender')->disableOriginalConstructor()->getMock();
$sender->expects($this->once())->method('send')->with($this->equalTo($request))->will($this->returnValue($this->createPromiseResolved($response)));

$transaction = new Transaction($request, $sender);
$promise = $transaction->send();

$that = $this;
$this->expectPromiseReject($promise)->then(null, function ($exception) use ($that, $response) {
$that->assertInstanceOf('Clue\React\Buzz\Message\ResponseException', $exception);
$that->assertEquals(404, $exception->getCode());
$that->assertSame($response, $exception->getResponse());
});
}
}
18 changes: 18 additions & 0 deletions tests/Message/ResponseExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

use Clue\React\Buzz\Message\Response;
use Clue\React\Buzz\Message\ResponseException;

class ResponseExceptionTest extends TestCase
{
public function testCtorDefaults()
{
$response = new Response('HTTP/1.0', 404, 'File not found');
$e = new ResponseException($response);

$this->assertEquals(404, $e->getCode());
$this->assertEquals('HTTP status code 404 (File not found)', $e->getMessage());

$this->assertSame($response, $e->getResponse());
}
}
10 changes: 10 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use React\Promise\Deferred;

require_once __DIR__ . '/../vendor/autoload.php';

error_reporting(-1);
Expand Down Expand Up @@ -71,6 +73,14 @@ protected function expectPromiseReject($promise)

return $promise;
}

protected function createPromiseResolved($value = null)
{
$deferred = new Deferred();
$deferred->resolve($value);

return $deferred->promise();
}
}

class CallableStub
Expand Down