From eaad2207387f23ae9183d03f843fef0a902367ac Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 2 Jan 2022 21:28:28 +0100 Subject: [PATCH] Add explicit status setting for PSR/ResponseInterface & add tests for OPTIONS endpoint --- src/App.php | 3 ++- src/App/Page.php | 6 ++++++ src/Capabilities/ICanCreateResponses.php | 10 +++++++++ src/Module/Response.php | 15 +++++++++++++- src/Module/Special/Options.php | 4 ++-- tests/src/Module/Special/OptionsTest.php | 26 ++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/src/Module/Special/OptionsTest.php diff --git a/src/App.php b/src/App.php index f7c929820d..c56222b63f 100644 --- a/src/App.php +++ b/src/App.php @@ -710,7 +710,8 @@ class App $timestamp = microtime(true); $response = $module->run($input); $this->profiler->set(microtime(true) - $timestamp, 'content'); - if ($response->getHeaderLine(ICanCreateResponses::X_HEADER) === ICanCreateResponses::TYPE_HTML) { + if ($response->getHeaderLine(ICanCreateResponses::X_HEADER) === ICanCreateResponses::TYPE_HTML && + $response->getStatusCode() == 200) { $page->run($this, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig); } else { $page->exit($response); diff --git a/src/App/Page.php b/src/App/Page.php index be5f8dc1e6..57468f8e5e 100644 --- a/src/App/Page.php +++ b/src/App/Page.php @@ -378,6 +378,12 @@ class Page implements ArrayAccess */ public function exit(ResponseInterface $response) { + header(sprintf("HTTP/%s %i %s", + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase()) + ); + foreach ($response->getHeaders() as $key => $header) { if (is_array($header)) { $header_str = implode(',', $header); diff --git a/src/Capabilities/ICanCreateResponses.php b/src/Capabilities/ICanCreateResponses.php index f0dac471f5..96149bab10 100644 --- a/src/Capabilities/ICanCreateResponses.php +++ b/src/Capabilities/ICanCreateResponses.php @@ -70,6 +70,16 @@ interface ICanCreateResponses */ public function setType(string $type, ?string $content_type = null): void; + /** + * Sets the status and the reason for the response + * + * @param int $status The HTTP status code + * @param null|string $reason Reason phrase (when empty a default will be used based on the status code) + * + * @return void + */ + public function setStatus(int $status = 200, ?string $reason = null): void; + /** * Creates a PSR-7 compliant interface * @see https://www.php-fig.org/psr/psr-7/ diff --git a/src/Module/Response.php b/src/Module/Response.php index dc11c9908d..e4bfde7a3c 100644 --- a/src/Module/Response.php +++ b/src/Module/Response.php @@ -40,6 +40,10 @@ class Response implements ICanCreateResponses */ protected $type = self::TYPE_HTML; + protected $status = 200; + + protected $reason = null; + /** * {@inheritDoc} */ @@ -111,6 +115,15 @@ class Response implements ICanCreateResponses $this->type = $type; } + /** + * {@inheritDoc} + */ + public function setStatus(int $status = 200, ?string $reason = null): void + { + $this->status = $status; + $this->reason = $reason; + } + /** * {@inheritDoc} */ @@ -127,6 +140,6 @@ class Response implements ICanCreateResponses // Setting the response type as an X-header for direct usage $this->headers[static::X_HEADER] = $this->type; - return new \GuzzleHttp\Psr7\Response(200, $this->headers, $this->content); + return new \GuzzleHttp\Psr7\Response($this->status, $this->headers, $this->content, $this->reason); } } diff --git a/src/Module/Special/Options.php b/src/Module/Special/Options.php index 36bbe4fb13..327f746b0c 100644 --- a/src/Module/Special/Options.php +++ b/src/Module/Special/Options.php @@ -10,7 +10,7 @@ class Options extends BaseModule protected function options(array $request = []) { // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS - $this->response->setHeader('Allow', implode(',', Router::ALLOWED_METHODS)); - $this->response->setHeader(($this->server['SERVER_PROTOCOL'] ?? 'HTTP/1.1') . ' 204 No Content'); + $this->response->setHeader(implode(',', Router::ALLOWED_METHODS), 'Allow'); + $this->response->setStatus(204); } } diff --git a/tests/src/Module/Special/OptionsTest.php b/tests/src/Module/Special/OptionsTest.php new file mode 100644 index 0000000000..3f7024f2db --- /dev/null +++ b/tests/src/Module/Special/OptionsTest.php @@ -0,0 +1,26 @@ + Router::OPTIONS]))->run(); + + self::assertEmpty((string)$response->getBody()); + self::assertEquals(204, $response->getStatusCode()); + self::assertEquals('No Content', $response->getReasonPhrase()); + self::assertEquals([ + 'Allow' => [implode(',', Router::ALLOWED_METHODS)], + ICanCreateResponses::X_HEADER => ['html'], + ], $response->getHeaders()); + self::assertEquals(implode(',', Router::ALLOWED_METHODS), $response->getHeaderLine('Allow')); + } +}