diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 23dd02c4..f55fc847 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -79,24 +79,63 @@ class RequestException extends TransferException $level = floor($response->getStatusCode() / 100); if ($level == '4') { - $label = 'Client error response'; + $label = 'Client Error'; $className = __NAMESPACE__ . '\\ClientException'; } elseif ($level == '5') { - $label = 'Server error response'; + $label = 'Server Error'; $className = __NAMESPACE__ . '\\ServerException'; } else { - $label = 'Unsuccessful response'; + $label = 'Unsuccessful Request'; $className = __CLASS__; } - $message = $label . ' [url] ' . $request->getUri() - . ' [http method] ' . $request->getMethod() - . ' [status code] ' . $response->getStatusCode() - . ' [reason phrase] ' . $response->getReasonPhrase(); + // Server Error: `GET /` resulted in a `404 Not Found` response: + // ... (truncated) + $message = sprintf( + '%s: `%s` resulted in a `%s` response', + $label, + $request->getMethod() . ' ' . $request->getUri(), + $response->getStatusCode() . ' ' . $response->getReasonPhrase() + ); + + $summary = static::getResponseBodySummary($response); + + if (is_string($summary)) { + $message .= ":\n{$summary}\n"; + } return new $className($message, $request, $response, $previous, $ctx); } + /** + * Get a short summary of the response + * + * Will return `null` if the response is not printable. + * + * @param ResponseInterface $response + * + * @return string|null + */ + public static function getResponseBodySummary(ResponseInterface $response) + { + $body = $response->getBody(); + + if (!$body->isSeekable()) { + return null; + } + + $summary = $body->read(120); + $body->rewind(); + + // Matches any printable character, including unicode characters: + // letters, marks, numbers, punctuation, spacing, and separators. + if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { + return null; + } + + return $summary; + } + /** * Get the request that caused the exception * diff --git a/src/Middleware.php b/src/Middleware.php index 2f165f39..85d31654 100644 --- a/src/Middleware.php +++ b/src/Middleware.php @@ -2,9 +2,7 @@ namespace GuzzleHttp; use GuzzleHttp\Cookie\CookieJarInterface; -use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Promise\RejectedPromise; use GuzzleHttp\Psr7; use Psr\Http\Message\ResponseInterface; @@ -64,9 +62,7 @@ final class Middleware if ($code < 400) { return $response; } - throw $code > 499 - ? new ServerException("Server error: $code", $request, $response) - : new ClientException("Client error: $code", $request, $response); + throw RequestException::create($request, $response); } ); }; diff --git a/tests/Exception/RequestExceptionTest.php b/tests/Exception/RequestExceptionTest.php index 61b87d8a..f252cae1 100644 --- a/tests/Exception/RequestExceptionTest.php +++ b/tests/Exception/RequestExceptionTest.php @@ -31,8 +31,12 @@ class RequestExceptionTest extends \PHPUnit_Framework_TestCase public function testCreatesClientErrorResponseException() { $e = RequestException::create(new Request('GET', '/'), new Response(400)); - $this->assertEquals( - 'Client error response [url] / [http method] GET [status code] 400 [reason phrase] Bad Request', + $this->assertContains( + 'GET /', + $e->getMessage() + ); + $this->assertContains( + '400 Bad Request', $e->getMessage() ); $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); @@ -41,8 +45,12 @@ class RequestExceptionTest extends \PHPUnit_Framework_TestCase public function testCreatesServerErrorResponseException() { $e = RequestException::create(new Request('GET', '/'), new Response(500)); - $this->assertEquals( - 'Server error response [url] / [http method] GET [status code] 500 [reason phrase] Internal Server Error', + $this->assertContains( + 'GET /', + $e->getMessage() + ); + $this->assertContains( + '500 Internal Server Error', $e->getMessage() ); $this->assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); @@ -51,8 +59,57 @@ class RequestExceptionTest extends \PHPUnit_Framework_TestCase public function testCreatesGenericErrorResponseException() { $e = RequestException::create(new Request('GET', '/'), new Response(600)); - $this->assertEquals( - 'Unsuccessful response [url] / [http method] GET [status code] 600 [reason phrase] ', + $this->assertContains( + 'GET /', + $e->getMessage() + ); + $this->assertContains( + '600 ', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function dataPrintableResponses() + { + return [ + ['You broke the test!'], + ['

zlomený zkouška

'], + ['{"tester": "Philépe Gonzalez"}'], + ["\n\tYour friendly test\n"], + ['document.body.write("here comes a test");'], + ["body:before {\n\tcontent: 'test style';\n}"], + ]; + } + + /** + * @dataProvider dataPrintableResponses + */ + public function testCreatesExceptionWithPrintableBodySummary($content) + { + $response = new Response( + 500, + [], + $content + ); + $e = RequestException::create(new Request('GET', '/'), $response); + $this->assertContains( + $content, + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testCreatesExceptionWithoutPrintableBody() + { + $response = new Response( + 500, + ['Content-Type' => 'image/gif'], + $content = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7') // 1x1 gif + ); + $e = RequestException::create(new Request('GET', '/'), $response); + $this->assertNotContains( + $content, $e->getMessage() ); $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index 9ad3378d..ed4c1abf 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -193,13 +193,14 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase $h = new MockHandler([new Response(404)]); $stack = new HandlerStack($h); $logger = new Logger(); - $formatter = new MessageFormatter('"{method} {target} HTTP/{version}\" {code} {error}'); + $formatter = new MessageFormatter('{code} {error}'); $stack->push(Middleware::log($logger, $formatter)); $stack->push(Middleware::httpErrors()); $comp = $stack->resolve(); $p = $comp(new Request('PUT', 'http://www.google.com'), ['http_errors' => true]); $p->wait(false); - $this->assertContains('"PUT / HTTP/1.1\" 404 Client error: 404', $logger->output); + $this->assertContains('PUT http://www.google.com', $logger->output); + $this->assertContains('404 Not Found', $logger->output); } }