| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- <?php
- declare(strict_types=1);
- namespace App\Tests\Unit\Http;
- use App\Infrastructure\Http\Middleware\RequestBodySizeLimitMiddleware;
- use PHPUnit\Framework\TestCase;
- use Psr\Http\Message\ResponseInterface;
- use Psr\Http\Message\ServerRequestInterface;
- use Psr\Http\Server\RequestHandlerInterface;
- use Slim\Psr7\Factory\ResponseFactory;
- use Slim\Psr7\Factory\ServerRequestFactory;
- use Slim\Psr7\Factory\StreamFactory;
- /**
- * SEC_REVIEW F69 — bound inbound request body size before Slim's
- * `BodyParsingMiddleware` reads it into memory. Two layers:
- * `Content-Length` header check (catches the well-behaved client) and
- * `getSize()` fallback (catches a stream that knows its length even
- * if the header lied).
- */
- final class RequestBodySizeLimitMiddlewareTest extends TestCase
- {
- public function testRequestUnderCapPasses(): void
- {
- $mw = new RequestBodySizeLimitMiddleware(new ResponseFactory(), 1024);
- $request = (new ServerRequestFactory())
- ->createServerRequest('POST', '/x')
- ->withBody((new StreamFactory())->createStream(str_repeat('a', 512)))
- ->withHeader('Content-Length', '512');
- $response = $mw->process($request, $this->okHandler());
- self::assertSame(200, $response->getStatusCode());
- }
- public function testRequestOverCapByContentLengthIs413(): void
- {
- $mw = new RequestBodySizeLimitMiddleware(new ResponseFactory(), 1024);
- $request = (new ServerRequestFactory())
- ->createServerRequest('POST', '/x')
- ->withBody((new StreamFactory())->createStream(str_repeat('a', 4096)))
- ->withHeader('Content-Length', '4096');
- $response = $mw->process($request, $this->shouldNotRunHandler());
- self::assertSame(413, $response->getStatusCode());
- $body = json_decode((string) $response->getBody(), true);
- self::assertSame('payload_too_large', $body['error'] ?? null);
- }
- public function testRequestOverCapByStreamSizeIs413(): void
- {
- // No Content-Length header (chunked-style request); fall back
- // to the stream's reported size.
- $mw = new RequestBodySizeLimitMiddleware(new ResponseFactory(), 1024);
- $request = (new ServerRequestFactory())
- ->createServerRequest('POST', '/x')
- ->withBody((new StreamFactory())->createStream(str_repeat('a', 4096)));
- // Strip Content-Length the factory might have set.
- $request = $request->withoutHeader('Content-Length');
- $response = $mw->process($request, $this->shouldNotRunHandler());
- self::assertSame(413, $response->getStatusCode());
- }
- public function testEmptyBodyPasses(): void
- {
- $mw = new RequestBodySizeLimitMiddleware(new ResponseFactory(), 1024);
- $request = (new ServerRequestFactory())->createServerRequest('GET', '/healthz');
- $response = $mw->process($request, $this->okHandler());
- self::assertSame(200, $response->getStatusCode());
- }
- public function testNonNumericContentLengthDoesNotShortCircuitButStreamSizeStillCaught(): void
- {
- // A garbage Content-Length is ignored (not a digit string), but
- // the stream-size fallback catches the actual oversize body.
- $mw = new RequestBodySizeLimitMiddleware(new ResponseFactory(), 1024);
- $body = (new StreamFactory())->createStream(str_repeat('a', 4096));
- $request = (new ServerRequestFactory())
- ->createServerRequest('POST', '/x')
- ->withBody($body)
- ->withHeader('Content-Length', 'garbage');
- $response = $mw->process($request, $this->shouldNotRunHandler());
- self::assertSame(413, $response->getStatusCode());
- }
- private function okHandler(): RequestHandlerInterface
- {
- return new class () implements RequestHandlerInterface {
- public function handle(ServerRequestInterface $request): ResponseInterface
- {
- return (new ResponseFactory())->createResponse(200);
- }
- };
- }
- private function shouldNotRunHandler(): RequestHandlerInterface
- {
- return new class () implements RequestHandlerInterface {
- public function handle(ServerRequestInterface $request): ResponseInterface
- {
- throw new \LogicException('handler must not be invoked when middleware rejects oversize');
- }
- };
- }
- }
|