diff --git a/composer.json b/composer.json index b45d1019..028f2da0 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ }, "scripts": { "test": "phpunit", - "test-coverage": "rm clover.xml && XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage --coverage-clover=clover.xml && vendor/bin/coverage-check clover.xml 100", + "test-coverage": "rm -f clover.xml && XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage --coverage-clover=clover.xml && vendor/bin/coverage-check clover.xml 100", "test-server": "echo \"Running Test Server\" && php -S localhost:8000 -t tests/server/", "test-server-v2": "echo \"Running Test Server\" && php -S localhost:8000 -t tests/server-v2/", "test-coverage:win": "del clover.xml && phpunit --coverage-html=coverage --coverage-clover=clover.xml && coverage-check clover.xml 100", diff --git a/flight/Engine.php b/flight/Engine.php index b0ae09da..bb694cb0 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -533,6 +533,11 @@ public function _start(): void $dispatched = false; } + // HEAD requests should be identical to GET requests but have no body + if ($request->method === 'HEAD') { + $response->clearBody(); + } + if ($failed_middleware_check === true) { $this->halt(403, 'Forbidden', empty(getenv('PHPUNIT_TEST'))); } elseif ($dispatched === false) { diff --git a/flight/net/Router.php b/flight/net/Router.php index f739a24e..81556cd9 100644 --- a/flight/net/Router.php +++ b/flight/net/Router.php @@ -105,6 +105,11 @@ public function map(string $pattern, callable $callback, bool $pass_route = fals [$method, $url] = explode(' ', $url, 2); $url = trim($url); $methods = explode('|', $method); + + // Add head requests to get methods, should they come in as a get request + if (in_array('GET', $methods, true) === true && in_array('HEAD', $methods, true) === false) { + $methods[] = 'HEAD'; + } } // And this finishes it off. diff --git a/tests/EngineTest.php b/tests/EngineTest.php index 400e1932..26df60fa 100644 --- a/tests/EngineTest.php +++ b/tests/EngineTest.php @@ -263,6 +263,20 @@ public function testDeleteRoute() $this->assertEquals('/someRoute', $routes[0]->pattern); } + public function testHeadRoute() + { + $engine = new Engine(); + $engine->route('GET /someRoute', function () { + echo 'i ran'; + }, true); + $engine->request()->method = 'HEAD'; + $engine->request()->url = '/someRoute'; + $engine->start(); + + // No body should be sent + $this->expectOutputString(''); + } + public function testHalt() { $engine = new class extends Engine { diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 5f5abcd2..2314426c 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -81,6 +81,10 @@ public function routeRequest() $dispatched = false; } + if ($this->request->method === 'HEAD') { + ob_clean(); + } + if (!$dispatched) { echo '404'; } @@ -122,6 +126,15 @@ public function testGetRouteShortcut() $this->check('OK'); } + public function testHeadRouteShortcut() + { + $route = $this->router->get('/path', [$this, 'ok']); + $this->assertEquals(['GET', 'HEAD'], $route->methods); + $this->request->url = '/path'; + $this->request->method = 'HEAD'; + $this->check(''); + } + // POST route public function testPostRoute() {