Skip to content

Commit

Permalink
BUGFIX: The CORS middleware will create the response for an OPTIONS r…
Browse files Browse the repository at this point in the history
…equest without passing the request down through the process chain

Without this change the preflight lead to a 404 as the OPTIONS request could not be routed which yielded a 404 exception.
  • Loading branch information
mficzel committed Dec 10, 2024
1 parent 8ca6c1b commit 5437de6
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 9 deletions.
16 changes: 12 additions & 4 deletions Classes/Http/CorsHeaderMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Lmc\HttpConstants\Header;
use Neos\Flow\Annotations as Flow;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
Expand All @@ -14,6 +15,12 @@

class CorsHeaderMiddleware implements MiddlewareInterface
{
/**
* @Flow\Inject
* @var ResponseFactoryInterface
*/
protected $responseFactory;

/**
* @Flow\InjectConfiguration("enabled")
*/
Expand Down Expand Up @@ -73,15 +80,16 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
}

$this->initializeConfiguration();

$response = $handler->handle($request);
$method = $request->getMethod();

// method type is not options, return early
if ($method == 'OPTIONS') {
// method type is OPTIONS, return early
if ($method === 'OPTIONS') {
$this->logger->debug('CORS Component: Preflight request');
$response = $this->responseFactory->createResponse();
return $this->handlePreflight($request, $response);
}

$response = $handler->handle($request);
return $this->handleRequest($request, $response);
}

Expand Down
26 changes: 21 additions & 5 deletions Tests/Unit/Http/CorsHeaderMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
Expand All @@ -24,6 +25,7 @@ class CorsHeaderMiddlewareTest extends TestCase
private readonly ResponseInterface&MockObject $responseMock;
private readonly RequestHandlerInterface&MockObject $handlerMock;
private readonly LoggerInterface&MockObject $logger;
private readonly ResponseFactoryInterface&MockObject $responseFactoryMock;

/**
* @throws Exception
Expand All @@ -35,12 +37,12 @@ protected function setUp(): void
$this->requestMock = $this->createMock(ServerRequestInterface::class);
$this->responseMock = $this->createMock(ResponseInterface::class);
$this->handlerMock = $this->createMock(RequestHandlerInterface::class);
$this->responseFactoryMock = $this->createMock(ResponseFactoryInterface::class);
$this->logger = $this->createMock(LoggerInterface::class);

ObjectAccess::setProperty($this->middleware, 'enabled', true, true);
ObjectAccess::setProperty($this->middleware, 'logger', $this->logger, true);

$this->handlerMock->expects($this->once())->method('handle')->willReturn($this->responseMock);
ObjectAccess::setProperty($this->middleware, 'responseFactory', $this->responseFactoryMock, true);
}

public function testMiddlewareIsNotEnabled(): void
Expand All @@ -65,8 +67,11 @@ public function testMiddlewarePreflightWithConfig(): void
};
});

$this->responseMock->expects($this->exactly(5))->method('withHeader')->willReturnSelf();
// the process is not passed down the chain and a fresh response is created
$this->handlerMock->expects($this->never())->method('handle');
$this->responseFactoryMock->expects($this->once())->method('createResponse')->willReturn($this->responseMock);

$this->responseMock->expects($this->exactly(5))->method('withHeader')->willReturnSelf();
$this->middleware->process($this->requestMock, $this->handlerMock);
}

Expand All @@ -83,9 +88,12 @@ public function testMiddlewarePreflightWithWildcardConfig(): void
};
});

$this->responseMock->expects($this->exactly(4))->method('withHeader')->willReturnSelf();
// the process is not passed down the chain and a fresh response is created
$this->handlerMock->expects($this->never())->method('handle');
$this->responseFactoryMock->expects($this->once())->method('createResponse')->willReturn($this->responseMock);

$this->middleware->process($this->requestMock, $this->handlerMock);
$this->responseMock->expects($this->exactly(4))->method('withHeader')->willReturnSelf();
$this->middleware->process($this->requestMock, $this->handlerMock);
}

public function testMiddlewareActualRequestWithConfig(): void
Expand All @@ -100,6 +108,8 @@ public function testMiddlewareActualRequestWithConfig(): void
};
});

// request is passed down the chain
$this->handlerMock->expects($this->once())->method('handle')->willReturn($this->responseMock);
$this->responseMock->expects($this->exactly(4))->method('withHeader')->willReturnSelf();

$this->middleware->process($this->requestMock, $this->handlerMock);
Expand All @@ -117,6 +127,8 @@ public function testMiddlewareActualRequestWithWildcardConfig(): void
};
});

// request is passed down the chain
$this->handlerMock->expects($this->once())->method('handle')->willReturn($this->responseMock);
$this->responseMock->expects($this->exactly(4))->method('withHeader')->willReturnSelf();

$this->middleware->process($this->requestMock, $this->handlerMock);
Expand All @@ -142,6 +154,8 @@ public function testMiddlewareActualRequestWithWildcardOrigin(): void
};
});

// request is passed down the chain
$this->handlerMock->expects($this->once())->method('handle')->willReturn($this->responseMock);
$this->responseMock->expects($this->exactly(4))->method('withHeader')->willReturnSelf();

$this->middleware->process($this->requestMock, $this->handlerMock);
Expand All @@ -167,6 +181,8 @@ public function testMiddlewareActualRequestWithNotAllowedOrigin(): void
};
});

// request is passed down the chain
$this->handlerMock->expects($this->once())->method('handle')->willReturn($this->responseMock);
$this->responseMock->expects($this->never())->method('withHeader')->willReturnSelf();

$this->logger->expects($this->exactly(2))->method('debug');
Expand Down

0 comments on commit 5437de6

Please sign in to comment.