From 0706a858ec06c6dc0da6fadbfe4ce1059513a06d Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 13 Jun 2017 14:40:01 -0500 Subject: [PATCH 1/2] Treat empty/missing Accept header as "*/*" Per [the Accept header specification](https://tools.ietf.org/html/rfc7231#section-5.3.2), a missing `Accept` header is equivalent to `*/*`. --- src/ProblemDetailsMiddleware.php | 2 +- src/ProblemDetailsResponseFactory.php | 2 +- test/ProblemDetailsMiddlewareTest.php | 1 + test/ProblemDetailsResponseFactoryTest.php | 15 ++++++++------- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ProblemDetailsMiddleware.php b/src/ProblemDetailsMiddleware.php index 6f15d39..f429e69 100644 --- a/src/ProblemDetailsMiddleware.php +++ b/src/ProblemDetailsMiddleware.php @@ -54,7 +54,7 @@ public function process(ServerRequestInterface $request, DelegateInterface $dele private function canActAsErrorHandler(ServerRequestInterface $request) : bool { - $accept = $request->getHeaderLine('Accept'); + $accept = $request->getHeaderLine('Accept') ?: '*/*'; if (empty($accept)) { return false; } diff --git a/src/ProblemDetailsResponseFactory.php b/src/ProblemDetailsResponseFactory.php index 7868a60..7c52ece 100644 --- a/src/ProblemDetailsResponseFactory.php +++ b/src/ProblemDetailsResponseFactory.php @@ -285,7 +285,7 @@ private function generateStream() : StreamInterface private function getResponseGenerator(ServerRequestInterface $request) : callable { - $accept = $request->getHeaderLine('Accept', 'application/xhtml+xml'); + $accept = $request->getHeaderLine('Accept') ?: '*/*'; $mediaType = (new Negotiator())->getBest($accept, self::NEGOTIATION_PRIORITIES); if (! $mediaType) { diff --git a/test/ProblemDetailsMiddlewareTest.php b/test/ProblemDetailsMiddlewareTest.php index 080e1b4..36338b5 100644 --- a/test/ProblemDetailsMiddlewareTest.php +++ b/test/ProblemDetailsMiddlewareTest.php @@ -26,6 +26,7 @@ protected function setUp() public function acceptHeaders() { return [ + 'empty' => [''], 'application/xml' => ['application/xml'], 'application/vnd.api+xml' => ['application/vnd.api+xml'], 'application/json' => ['application/json'], diff --git a/test/ProblemDetailsResponseFactoryTest.php b/test/ProblemDetailsResponseFactoryTest.php index f30372a..1293887 100644 --- a/test/ProblemDetailsResponseFactoryTest.php +++ b/test/ProblemDetailsResponseFactoryTest.php @@ -24,6 +24,7 @@ protected function setUp() public function acceptHeaders() { return [ + 'empty' => ['', 'application/problem+json'], 'application/xml' => ['application/xml', 'application/problem+xml'], 'application/vnd.api+xml' => ['application/vnd.api+xml', 'application/problem+xml'], 'application/json' => ['application/json', 'application/problem+json'], @@ -36,7 +37,7 @@ public function acceptHeaders() */ public function testCreateResponseCreatesExpectedType(string $header, string $expectedType) { - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn($header); + $this->request->getHeaderLine('Accept')->willReturn($header); $response = $this->factory->createResponse( $this->request->reveal(), @@ -53,7 +54,7 @@ public function testCreateResponseCreatesExpectedType(string $header, string $ex */ public function testCreateResponseFromThrowableCreatesExpectedType(string $header, string $expectedType) { - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn($header); + $this->request->getHeaderLine('Accept')->willReturn($header); $exception = new RuntimeException(); $response = $this->factory->createResponseFromThrowable( @@ -72,7 +73,7 @@ public function testCreateResponseFromThrowableCreatesExpectedTypeWithExtraInfor string $header, string $expectedType ) { - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn($header); + $this->request->getHeaderLine('Accept')->willReturn($header); $factory = new ProblemDetailsResponseFactory(ProblemDetailsResponseFactory::INCLUDE_THROWABLE_DETAILS); @@ -98,7 +99,7 @@ public function testCreateResponseFromThrowableWillPullDetailsFromProblemDetails $e->getType()->willReturn('https://example.com/api/doc/invalid-client-request'); $e->getAdditionalData()->willReturn(['foo' => 'bar']); - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn('application/json'); + $this->request->getHeaderLine('Accept')->willReturn('application/json'); $factory = new ProblemDetailsResponseFactory(); @@ -121,7 +122,7 @@ public function testCreateResponseFromThrowableWillPullDetailsFromProblemDetails public function testFactoryRaisesExceptionIfBodyFactoryDoesNotReturnStream() { - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn('application/json'); + $this->request->getHeaderLine('Accept')->willReturn('application/json'); $factory = new ProblemDetailsResponseFactory(false, null, null, function () { return null; @@ -133,7 +134,7 @@ public function testFactoryRaisesExceptionIfBodyFactoryDoesNotReturnStream() public function testFactoryGeneratesXmlResponseIfNegotiationFails() { - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn('text/plain'); + $this->request->getHeaderLine('Accept')->willReturn('text/plain'); $response = $this->factory->createResponse( $this->request->reveal(), @@ -147,7 +148,7 @@ public function testFactoryGeneratesXmlResponseIfNegotiationFails() public function testFactoryRendersPreviousExceptionsInDebugMode() { - $this->request->getHeaderLine('Accept', 'application/xhtml+xml')->willReturn('application/json'); + $this->request->getHeaderLine('Accept')->willReturn('application/json'); $first = new RuntimeException('first', 101010); $second = new RuntimeException('second', 101011, $first); From 7b616577f275e87d4ed5eb75f39e5aaba5f6fc58 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 13 Jun 2017 14:41:20 -0500 Subject: [PATCH 2/2] Added CHANGELOG for #5 --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1405c54..4e42c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 0.2.1 - 2017-06-13 + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#5](https://github.com/weierophinney/problem-details/pull/5) updates the + response factory and middleware to treat lack of/empty `Accept` header values + as `*/*`, per RFC-7231 section 5.3.2. + ## 0.2.0 - 2017-05-30 ### Added