Skip to content

Commit 328fe95

Browse files
WyriHaximusclue
authored andcommitted
RequestBodyParserMiddleware does not reject requests that exceed post_max_size
1 parent 6995dbe commit 328fe95

4 files changed

Lines changed: 60 additions & 27 deletions

File tree

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,7 @@ configuration.
694694
configuration in most cases.)
695695

696696
Any incoming request that has a request body that exceeds this limit will be
697-
rejected with a `413` (Request Entity Too Large) error message without calling
698-
the next middleware handlers.
697+
accepted but their request body will not be added to the request.
699698

700699
The `RequestBodyBufferMiddleware` will buffer requests with bodies of known size
701700
(i.e. with `Content-Length` header specified) as well as requests with bodies of

examples/12-upload.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119

120120
// buffer and parse HTTP request body before running our request handler
121121
$server = new StreamingServer(new MiddlewareRunner(array(
122-
new RequestBodyBufferMiddleware(100000), // 100 KB max
122+
new RequestBodyBufferMiddleware(100000), // 100 KB max, ignore body otherwise
123123
new RequestBodyParserMiddleware(),
124124
$handler
125125
)));

src/Middleware/RequestBodyBufferMiddleware.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace React\Http\Middleware;
44

5+
use OverflowException;
56
use Psr\Http\Message\ServerRequestInterface;
6-
use React\Http\Response;
7+
use React\Promise\Promise;
78
use React\Promise\Stream;
89
use React\Stream\ReadableStreamInterface;
910
use RingCentral\Psr7\BufferStream;
10-
use OverflowException;
1111

1212
final class RequestBodyBufferMiddleware
1313
{
@@ -30,27 +30,37 @@ public function __construct($sizeLimit = null)
3030

3131
public function __invoke(ServerRequestInterface $request, $stack)
3232
{
33+
$sizeLimit = $this->sizeLimit;
3334
$body = $request->getBody();
3435

3536
// request body of known size exceeding limit
3637
if ($body->getSize() > $this->sizeLimit) {
37-
return new Response(413, array('Content-Type' => 'text/plain'), 'Request body exceeds allowed limit');
38+
$sizeLimit = 0;
3839
}
3940

4041
if (!$body instanceof ReadableStreamInterface) {
4142
return $stack($request);
4243
}
4344

44-
return Stream\buffer($body, $this->sizeLimit)->then(function ($buffer) use ($request, $stack) {
45+
return Stream\buffer($body, $sizeLimit)->then(function ($buffer) use ($request, $stack) {
4546
$stream = new BufferStream(strlen($buffer));
4647
$stream->write($buffer);
4748
$request = $request->withBody($stream);
4849

4950
return $stack($request);
50-
}, function($error) {
51-
// request body of unknown size exceeding limit during buffering
51+
}, function ($error) use ($stack, $request, $body) {
52+
// On buffer overflow keep the request body stream in,
53+
// but ignore the contents and wait for the close event
54+
// before passing the request on to the next middleware.
5255
if ($error instanceof OverflowException) {
53-
return new Response(413, array('Content-Type' => 'text/plain'), 'Request body exceeds allowed limit');
56+
return new Promise(function ($resolve, $reject) use ($stack, $request, $body) {
57+
$body->on('error', function ($error) use ($reject) {
58+
$reject($error);
59+
});
60+
$body->on('close', function () use ($stack, $request, $resolve) {
61+
$resolve($stack($request));
62+
});
63+
});
5464
}
5565

5666
throw $error;

tests/Middleware/RequestBodyBufferMiddlewareTest.php

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22

33
namespace React\Tests\Http\Middleware;
44

5+
use Clue\React\Block;
56
use Psr\Http\Message\ServerRequestInterface;
67
use React\EventLoop\Factory;
78
use React\Http\Io\HttpBodyStream;
89
use React\Http\Io\ServerRequest;
910
use React\Http\Middleware\RequestBodyBufferMiddleware;
11+
use React\Http\Response;
1012
use React\Stream\ThroughStream;
1113
use React\Tests\Http\TestCase;
1214
use RingCentral\Psr7\BufferStream;
13-
use Clue\React\Block;
1415

1516
final class RequestBodyBufferMiddlewareTest extends TestCase
1617
{
@@ -65,7 +66,7 @@ function (ServerRequestInterface $request) use (&$exposedRequest) {
6566
$this->assertSame($body, $exposedRequest->getBody()->getContents());
6667
}
6768

68-
public function testExcessiveSizeImmediatelyReturnsError413ForKnownSize()
69+
public function testKnownExcessiveSizedBodyIsDisgardedTheRequestIsPassedDownToTheNextMiddleware()
6970
{
7071
$loop = Factory::create();
7172

@@ -79,17 +80,18 @@ public function testExcessiveSizeImmediatelyReturnsError413ForKnownSize()
7980
);
8081

8182
$buffer = new RequestBodyBufferMiddleware(1);
82-
$response = $buffer(
83+
$response = Block\await($buffer(
8384
$serverRequest,
8485
function (ServerRequestInterface $request) {
85-
return $request;
86+
return new Response(200, array(), $request->getBody()->getContents());
8687
}
87-
);
88+
), $loop);
8889

89-
$this->assertSame(413, $response->getStatusCode());
90+
$this->assertSame(200, $response->getStatusCode());
91+
$this->assertSame('', $response->getBody()->getContents());
9092
}
9193

92-
public function testExcessiveSizeReturnsError413()
94+
public function testExcessiveSizeBodyIsDiscardedAndTheRequestIsPassedDownToTheNextMiddleware()
9395
{
9496
$loop = Factory::create();
9597

@@ -105,23 +107,19 @@ public function testExcessiveSizeReturnsError413()
105107
$promise = $buffer(
106108
$serverRequest,
107109
function (ServerRequestInterface $request) {
108-
return $request;
110+
return new Response(200, array(), $request->getBody()->getContents());
109111
}
110112
);
111113

112114
$stream->end('aa');
113115

114-
$exposedResponse = null;
115-
$promise->then(
116-
function($response) use (&$exposedResponse) {
117-
$exposedResponse = $response;
118-
},
116+
$exposedResponse = Block\await($promise->then(
117+
null,
119118
$this->expectCallableNever()
120-
);
121-
122-
$this->assertSame(413, $exposedResponse->getStatusCode());
119+
), $loop);
123120

124-
Block\await($promise, $loop);
121+
$this->assertSame(200, $exposedResponse->getStatusCode());
122+
$this->assertSame('', $exposedResponse->getBody()->getContents());
125123
}
126124

127125
/**
@@ -151,4 +149,30 @@ function (ServerRequestInterface $request) {
151149

152150
Block\await($promise, $loop);
153151
}
152+
153+
public function testFullBodyStreamedBeforeCallingNextMiddleware()
154+
{
155+
$promiseResolved = false;
156+
$middleware = new RequestBodyBufferMiddleware(3);
157+
$stream = new ThroughStream();
158+
$serverRequest = new ServerRequest(
159+
'GET',
160+
'https://example.com/',
161+
array(),
162+
new HttpBodyStream($stream, null)
163+
);
164+
165+
$middleware($serverRequest, function () {
166+
return new Response();
167+
})->then(function () use (&$promiseResolved) {
168+
$promiseResolved = true;
169+
});
170+
171+
$stream->write('aaa');
172+
$this->assertFalse($promiseResolved);
173+
$stream->write('aaa');
174+
$this->assertFalse($promiseResolved);
175+
$stream->end('aaa');
176+
$this->assertTrue($promiseResolved);
177+
}
154178
}

0 commit comments

Comments
 (0)