Skip to content

Commit a078fab

Browse files
committed
Improve handling OPTIONS * requests
1 parent eecba13 commit a078fab

5 files changed

Lines changed: 43 additions & 7 deletions

File tree

examples/index.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,7 @@
205205
});
206206
$app->get('/error/class', 'Acme\Http\UnknownDeleteUserController'); // @phpstan-ignore-line
207207

208-
// OPTIONS *
209-
$app->options('', function () {
208+
$app->options('*', function () {
210209
return new React\Http\Message\Response(200);
211210
});
212211

src/App.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ public function delete(string $route, $handler, ...$handlers): void
180180
*/
181181
public function options(string $route, $handler, ...$handlers): void
182182
{
183+
// backward compatibility: `OPTIONS * HTTP/1.1` can be matched with empty path (legacy)
184+
if ($route === '') {
185+
$route = '*';
186+
}
187+
183188
$this->map(['OPTIONS'], $route, $handler, ...$handlers);
184189
}
185190

src/Io/RouteHandler.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,18 @@ public function map(array $methods, string $route, $handler, ...$handlers): void
7676
*/
7777
public function __invoke(ServerRequestInterface $request)
7878
{
79-
if ($request->getRequestTarget()[0] !== '/' && $request->getRequestTarget() !== '*') {
79+
$target = $request->getRequestTarget();
80+
if ($target[0] !== '/' && $target !== '*') {
8081
return $this->errorHandler->requestProxyUnsupported();
82+
} elseif ($target !== '*') {
83+
$target = $request->getUri()->getPath();
8184
}
8285

8386
if ($this->routeDispatcher === null) {
8487
$this->routeDispatcher = new RouteDispatcher($this->routeCollector->getData());
8588
}
8689

87-
$routeInfo = $this->routeDispatcher->dispatch($request->getMethod(), $request->getUri()->getPath());
90+
$routeInfo = $this->routeDispatcher->dispatch($request->getMethod(), $target);
8891
assert(\is_array($routeInfo) && isset($routeInfo[0]));
8992

9093
// happy path: matching route found, assign route attributes and invoke request handler

tests/AppTest.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,36 @@ public function testHandleRequestWithMatchingRouteReturnsResponseFromMatchingRou
973973
$this->assertEquals("OK\n", (string) $response->getBody());
974974
}
975975

976-
public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingEmptyRouteHandler(): void
976+
public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingAsteriskRouteHandler(): void
977+
{
978+
$app = $this->createAppWithoutLogger();
979+
980+
$app->options('*', function () {
981+
return new Response(
982+
200,
983+
[
984+
'Content-Type' => 'text/html'
985+
],
986+
"OK\n"
987+
);
988+
});
989+
990+
$request = new ServerRequest('OPTIONS', 'http://localhost');
991+
$request = $request->withRequestTarget('*');
992+
993+
// $response = $app->handleRequest($request);
994+
$ref = new ReflectionMethod($app, 'handleRequest');
995+
$ref->setAccessible(true);
996+
$response = $ref->invoke($app, $request);
997+
998+
/** @var ResponseInterface $response */
999+
$this->assertInstanceOf(ResponseInterface::class, $response);
1000+
$this->assertEquals(200, $response->getStatusCode());
1001+
$this->assertEquals('text/html', $response->getHeaderLine('Content-Type'));
1002+
$this->assertEquals("OK\n", (string) $response->getBody());
1003+
}
1004+
1005+
public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingDeprecatedEmptyRouteHandler(): void
9771006
{
9781007
$app = $this->createAppWithoutLogger();
9791008

tests/Io/RouteHandlerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,14 +332,14 @@ public function testHandleRequestWithGetRequestWithHttpUrlInPathReturnsResponseF
332332
$this->assertSame($response, $ret);
333333
}
334334

335-
public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingEmptyHandler(): void
335+
public function testHandleRequestWithOptionsAsteriskRequestReturnsResponseFromMatchingAsteriskHandler(): void
336336
{
337337
$request = new ServerRequest('OPTIONS', 'http://example.com');
338338
$request = $request->withRequestTarget('*');
339339
$response = new Response(200, [], '');
340340

341341
$handler = new RouteHandler();
342-
$handler->map(['OPTIONS'], '', function () use ($response) { return $response; });
342+
$handler->map(['OPTIONS'], '*', function () use ($response) { return $response; });
343343

344344
$ret = $handler($request);
345345

0 commit comments

Comments
 (0)