diff --git a/src/Common/Error/Builder.php b/src/Common/Error/Builder.php index 7de83710..f2717aa1 100644 --- a/src/Common/Error/Builder.php +++ b/src/Common/Error/Builder.php @@ -19,6 +19,8 @@ */ class Builder { + public const MAX_BODY_LENGTH = 5000; + /** * The default domain to use for further link documentation. * @@ -96,8 +98,13 @@ public function str(MessageInterface $message, int $verbosity = 0): string return $msg; } - if (ini_get('memory_limit') < 0 || $message->getBody()->getSize() < ini_get('memory_limit')) { - $msg .= "\r\n\r\n".$message->getBody(); + $contentType = strtolower($message->getHeaderLine('content-type')); + if (false !== strpos($contentType, 'application/json')) { + $body = $message->getBody()->read(self::MAX_BODY_LENGTH); + $msg .= "\r\n\r\n".$body; + if ('' !== $message->getBody()->read(1)) { + $msg .= '...'; + } } return trim($msg); diff --git a/tests/sample/Compute/v2/ServerTest.php b/tests/sample/Compute/v2/ServerTest.php index 18c8f5a3..6d78a6cd 100644 --- a/tests/sample/Compute/v2/ServerTest.php +++ b/tests/sample/Compute/v2/ServerTest.php @@ -374,6 +374,9 @@ public function testSuspend() $server->waitUntil('SUSPENDED'); $this->assertEquals('SUSPENDED', $server->status); + // wait for the server to be fully suspended + sleep(5); + return $server; } diff --git a/tests/unit/Common/Error/BuilderTest.php b/tests/unit/Common/Error/BuilderTest.php index 51ce95fb..6f0abc7b 100644 --- a/tests/unit/Common/Error/BuilderTest.php +++ b/tests/unit/Common/Error/BuilderTest.php @@ -4,6 +4,8 @@ use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Psr7\NoSeekStream; +use GuzzleHttp\Psr7\PumpStream; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Utils; @@ -75,6 +77,85 @@ public function verbosityProvider(): array ]; } + public function test_it_outputs_body_for_json() + { + $value = 'foobar'; + + $request = new Request( + 'POST', + '/servers', + ['Content-Type' => 'application/json'], + json_encode(['foo' => $value]) + ); + + $str = $this->builder->str($request, 2); + $this->assertStringContainsString($value, $str); + } + + public function test_it_skips_body_for_low_verbosity() + { + $value = 'foobar'; + + $request = new Request( + 'POST', + '/servers', + ['Content-Type' => 'application/json'], + json_encode(['foo' => $value]) + ); + + $str = $this->builder->str($request, 1); + $this->assertStringNotContainsString($value, $str); + } + + public function test_it_cuts_big_body_for_json() + { + $value = str_repeat('A', Builder::MAX_BODY_LENGTH); + + $request = new Request( + 'POST', + '/servers', + ['Content-Type' => 'application/json'], + json_encode(['foo' => $value]) + ); + + $str = $this->builder->str($request, 2); + $this->assertStringNotContainsString($value, $str); + $this->assertStringContainsString('AAAAAA...', $str); + } + + public function test_it_did_not_read_full_body_for_json() + { + $value = str_repeat('A', Builder::MAX_BODY_LENGTH + 1); + + $request = new Request( + 'POST', + '/servers', + ['Content-Type' => 'application/json'], + new PumpStream(function ($size) { + return str_repeat('A', $size); + }) + ); + + $str = $this->builder->str($request, 2); + $this->assertStringNotContainsString($value, $str); + $this->assertStringContainsString('AAAAAA...', $str); + } + + public function test_it_skips_body_for_binary() + { + $value = 'foobar'; + + $request = new Request( + 'POST', + '/servers', + ['Content-Type' => 'binary/octet-stream'], + $value + ); + + $str = $this->builder->str($request, 2); + $this->assertStringNotContainsString($value, $str); + } + public function test_it_builds_user_input_errors() { $expected = 'A well-formed string';