diff --git a/src/Http/Response.php b/src/Http/Response.php index 4d4485e3..1500d2da 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -6,7 +6,10 @@ use Throwable; use LogicException; +use function implode; use SimpleXMLElement; +use function is_array; +use function mb_strtolower; use Saloon\Traits\Macroable; use InvalidArgumentException; use Saloon\Helpers\ArrayHelpers; @@ -486,7 +489,13 @@ public function throw(): static */ public function header(string $header): string|array|null { - return $this->headers()->get($header); + if (! $this->psrResponse->hasHeader($header)) { + return null; + } + + $values = $this->psrResponse->getHeader($header); + + return count($values) === 1 ? $values[0] : $values; } /** @@ -494,13 +503,17 @@ public function header(string $header): string|array|null */ public function isJson(): bool { - $contentType = $this->psrResponse->getHeaderLine('Content-Type'); + $contentType = $this->header('Content-Type'); - if ($contentType === '') { + if (empty($contentType)) { return false; } - return str_contains($contentType, 'json'); + if (is_array($contentType)) { + $contentType = implode(',', $contentType); + } + + return str_contains(mb_strtolower($contentType), 'json'); } /** @@ -508,13 +521,17 @@ public function isJson(): bool */ public function isXml(): bool { - $contentType = $this->psrResponse->getHeaderLine('Content-Type'); + $contentType = $this->header('Content-Type'); - if ($contentType === '') { + if (empty($contentType)) { return false; } - return str_contains($contentType, 'xml'); + if (is_array($contentType)) { + $contentType = implode(',', $contentType); + } + + return str_contains(mb_strtolower($contentType), 'xml'); } /** diff --git a/tests/Unit/ResponseTest.php b/tests/Unit/ResponseTest.php index 02d0e363..95fbc452 100644 --- a/tests/Unit/ResponseTest.php +++ b/tests/Unit/ResponseTest.php @@ -472,3 +472,36 @@ $response = $connector->send(new UserRequest, $mockClient); expect($response->isXml())->toBeFalse(); }); + +test('header lookup is case-insensitive per HTTP RFC', function () { + $mockClient = new MockClient([ + MockResponse::make([], 200, ['x-my-custom-header' => 'custom-value']), + ]); + + $response = connector()->send(new UserRequest, $mockClient); + + expect($response->header('X-My-Custom-Header'))->toEqual('custom-value'); + expect($response->header('x-my-custom-header'))->toEqual('custom-value'); +}); + +test('isJson lookup can use case insensitive headers', function () { + $mockClient = new MockClient([ + MockResponse::make([], 200, ['content-type' => 'application/JSON']), + ]); + + $response = connector()->send(new UserRequest, $mockClient); + + expect($response->isJson())->toBeTrue(); + expect($response->isXml())->toBeFalse(); +}); + +test('isXml lookup can use case insensitive headers', function () { + $mockClient = new MockClient([ + MockResponse::make([], 200, ['content-type' => 'text/xml']), + ]); + + $response = connector()->send(new UserRequest, $mockClient); + + expect($response->isXml())->toBeTrue(); + expect($response->isJson())->toBeFalse(); +});