diff --git a/examples/index.php b/examples/index.php index b8d1db3..51acd4d 100644 --- a/examples/index.php +++ b/examples/index.php @@ -205,8 +205,7 @@ }); $app->get('/error/class', 'Acme\Http\UnknownDeleteUserController'); // @phpstan-ignore-line -// OPTIONS * -$app->options('', function () { +$app->options('*', function () { return new React\Http\Message\Response(200); }); diff --git a/src/App.php b/src/App.php index b7c29c9..9fb72c7 100644 --- a/src/App.php +++ b/src/App.php @@ -180,6 +180,11 @@ public function delete(string $route, $handler, ...$handlers): void */ public function options(string $route, $handler, ...$handlers): void { + // backward compatibility: `OPTIONS * HTTP/1.1` can be matched with empty path (legacy) + if ($route === '') { + $route = '*'; + } + $this->map(['OPTIONS'], $route, $handler, ...$handlers); } diff --git a/src/Io/RouteHandler.php b/src/Io/RouteHandler.php index 0402d6e..2b0cf1c 100644 --- a/src/Io/RouteHandler.php +++ b/src/Io/RouteHandler.php @@ -76,15 +76,18 @@ public function map(array $methods, string $route, $handler, ...$handlers): void */ public function __invoke(ServerRequestInterface $request) { - if ($request->getRequestTarget()[0] !== '/' && $request->getRequestTarget() !== '*') { + $target = $request->getRequestTarget(); + if ($target[0] !== '/' && $target !== '*') { return $this->errorHandler->requestProxyUnsupported(); + } elseif ($target !== '*') { + $target = $request->getUri()->getPath(); } if ($this->routeDispatcher === null) { $this->routeDispatcher = new RouteDispatcher($this->routeCollector->getData()); } - $routeInfo = $this->routeDispatcher->dispatch($request->getMethod(), $request->getUri()->getPath()); + $routeInfo = $this->routeDispatcher->dispatch($request->getMethod(), $target); assert(\is_array($routeInfo) && isset($routeInfo[0])); // happy path: matching route found, assign route attributes and invoke request handler diff --git a/tests/AppTest.php b/tests/AppTest.php index be94e29..b5e1411 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -973,7 +973,36 @@ public function testHandleRequestWithMatchingRouteReturnsResponseFromMatchingRou $this->assertEquals("OK\n", (string) $response->getBody()); } - public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingEmptyRouteHandler(): void + public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingAsteriskRouteHandler(): void + { + $app = $this->createAppWithoutLogger(); + + $app->options('*', function () { + return new Response( + 200, + [ + 'Content-Type' => 'text/html' + ], + "OK\n" + ); + }); + + $request = new ServerRequest('OPTIONS', 'http://localhost'); + $request = $request->withRequestTarget('*'); + + // $response = $app->handleRequest($request); + $ref = new ReflectionMethod($app, 'handleRequest'); + $ref->setAccessible(true); + $response = $ref->invoke($app, $request); + + /** @var ResponseInterface $response */ + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); + $this->assertEquals("OK\n", (string) $response->getBody()); + } + + public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingDeprecatedEmptyRouteHandler(): void { $app = $this->createAppWithoutLogger(); diff --git a/tests/Io/RouteHandlerTest.php b/tests/Io/RouteHandlerTest.php index 4f15d91..abf4482 100644 --- a/tests/Io/RouteHandlerTest.php +++ b/tests/Io/RouteHandlerTest.php @@ -332,14 +332,14 @@ public function testHandleRequestWithGetRequestWithHttpUrlInPathReturnsResponseF $this->assertSame($response, $ret); } - public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingEmptyHandler(): void + public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingAsteriskHandler(): void { $request = new ServerRequest('OPTIONS', 'http://example.com'); $request = $request->withRequestTarget('*'); $response = new Response(200, [], ''); $handler = new RouteHandler(); - $handler->map(['OPTIONS'], '', function () use ($response) { return $response; }); + $handler->map(['OPTIONS'], '*', function () use ($response) { return $response; }); $ret = $handler($request);