Skip to content

Commit 31b004b

Browse files
authored
Merge commit from fork
1 parent d09ca8a commit 31b004b

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

‎src/Driver/Http1Driver.php‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,14 @@ static function (int $bodySize) use (&$bodySizeLimit): void {
671671
$this->updateTimeout();
672672

673673
if ($bufferLength >= $chunkLengthRemaining + 2) {
674+
if (\substr($buffer, $chunkLengthRemaining, 2) !== "\r\n") {
675+
throw new ClientException(
676+
$this->client,
677+
"Bad Request: invalid chunk delimiter",
678+
HttpStatus::BAD_REQUEST,
679+
);
680+
}
681+
674682
try {
675683
$queue->push(\substr($buffer, 0, $chunkLengthRemaining));
676684
} catch (DisposedException) {
@@ -762,7 +770,7 @@ static function (int $bodySize) use (&$bodySizeLimit): void {
762770
$this->pendingResponseCount--;
763771
} while (!$this->upgraded);
764772
} catch (ClientException $exception) {
765-
if ($this->bodyQueue === null || !$this->pendingResponseCount) {
773+
if ($this->bodyQueue === null || $this->pendingResponseCount) {
766774
// Send an error response only if another response has not already been sent to the request.
767775
$this->sendErrorResponse($exception, $request ?? null)->await();
768776
}

‎test/Driver/Http1DriverTest.php‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,47 @@ public function testBodylessResponseDoesNotDesyncConnection(): void
991991
);
992992
}
993993

994+
public function testMalformedChunk(): void
995+
{
996+
$driver = new Http1Driver(
997+
new ClosureRequestHandler(function (Request $request) use (&$exception): Response {
998+
$body = $request->getBody()->buffer();
999+
return new Response(body: $body);
1000+
}),
1001+
$this->createMock(ErrorHandler::class),
1002+
new NullLogger,
1003+
);
1004+
1005+
$output = new WritableBuffer;
1006+
1007+
async(fn () => $driver->handleClient(
1008+
$this->createClientMock(),
1009+
new ReadableBuffer(
1010+
"POST /first HTTP/1.1\r\n" .
1011+
"Host: 127.0.0.1\r\n" .
1012+
"Connection: keep-alive\r\n" .
1013+
"Transfer-Encoding: chunked\r\n" .
1014+
"\r\n" .
1015+
"3\r\n" . // chunk size = 3
1016+
"abc" . // 3 bytes of chunk data
1017+
"AB" . // malformed delimiter (0x41 0x42) instead of CRLF (0x0D 0x0A)
1018+
"0\r\n" . // terminal chunk
1019+
"\r\n" .
1020+
"GET /admin HTTP/1.1\r\n" .
1021+
"Host: 127.0.0.1\r\n" .
1022+
"Connection: close\r\n" .
1023+
"\r\n"
1024+
),
1025+
$output,
1026+
));
1027+
1028+
delay(0.1);
1029+
1030+
$output->close();
1031+
1032+
self::assertStringStartsWith("HTTP/1.0 400 Bad Request\r\n", $output->buffer());
1033+
}
1034+
9941035
public function testWriteAbortAfterHeaders(): void
9951036
{
9961037
$driver = new Http1Driver(

0 commit comments

Comments
 (0)