Skip to content

Commit 3e2caeb

Browse files
authored
Merge pull request #429 from clue-labs/close-unused-body
Explicitly close streaming response body when body MUST be empty
2 parents 80e4593 + eba1e1e commit 3e2caeb

File tree

2 files changed

+109
-2
lines changed

2 files changed

+109
-2
lines changed

src/Io/StreamingServer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ public function handleResponse(ConnectionInterface $connection, ServerRequestInt
330330
// response to HEAD and 1xx, 204 and 304 responses MUST NOT include a body
331331
// exclude status 101 (Switching Protocols) here for Upgrade request handling above
332332
if ($method === 'HEAD' || $code === 100 || ($code > 101 && $code < 200) || $code === 204 || $code === 304) {
333+
$body->close();
333334
$body = '';
334335
}
335336

tests/Io/StreamingServerTest.php

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ function ($data) use (&$buffer) {
765765
$this->assertEquals('', $buffer);
766766
}
767767

768-
public function testRespomseBodyStreamAlreadyClosedWillSendEmptyBodyChunkedEncoded()
768+
public function testResponseBodyStreamAlreadyClosedWillSendEmptyBodyChunkedEncoded()
769769
{
770770
$stream = new ThroughStream();
771771
$stream->close();
@@ -1255,9 +1255,45 @@ function ($data) use (&$buffer) {
12551255
$this->connection->emit('data', array($data));
12561256

12571257
$this->assertContainsString("HTTP/1.1 200 OK\r\n", $buffer);
1258+
$this->assertContainsString("\r\nContent-Length: 3\r\n", $buffer);
12581259
$this->assertNotContainsString("bye", $buffer);
12591260
}
12601261

1262+
public function testResponseContainsNoResponseBodyForHeadRequestWithStreamingResponse()
1263+
{
1264+
$stream = new ThroughStream();
1265+
$stream->on('close', $this->expectCallableOnce());
1266+
1267+
$server = new StreamingServer(Factory::create(), function (ServerRequestInterface $request) use ($stream) {
1268+
return new Response(
1269+
200,
1270+
array('Content-Length' => '3'),
1271+
$stream
1272+
);
1273+
});
1274+
1275+
$buffer = '';
1276+
$this->connection
1277+
->expects($this->any())
1278+
->method('write')
1279+
->will(
1280+
$this->returnCallback(
1281+
function ($data) use (&$buffer) {
1282+
$buffer .= $data;
1283+
}
1284+
)
1285+
);
1286+
1287+
$server->listen($this->socket);
1288+
$this->socket->emit('connection', array($this->connection));
1289+
1290+
$data = "HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n";
1291+
$this->connection->emit('data', array($data));
1292+
1293+
$this->assertContainsString("HTTP/1.1 200 OK\r\n", $buffer);
1294+
$this->assertContainsString("\r\nContent-Length: 3\r\n", $buffer);
1295+
}
1296+
12611297
public function testResponseContainsNoResponseBodyAndNoContentLengthForNoContentStatus()
12621298
{
12631299
$server = new StreamingServer(Factory::create(), function (ServerRequestInterface $request) {
@@ -1287,10 +1323,45 @@ function ($data) use (&$buffer) {
12871323
$this->connection->emit('data', array($data));
12881324

12891325
$this->assertContainsString("HTTP/1.1 204 No Content\r\n", $buffer);
1290-
$this->assertNotContainsString("\r\n\Content-Length: 3\r\n", $buffer);
1326+
$this->assertNotContainsString("\r\nContent-Length: 3\r\n", $buffer);
12911327
$this->assertNotContainsString("bye", $buffer);
12921328
}
12931329

1330+
public function testResponseContainsNoResponseBodyAndNoContentLengthForNoContentStatusResponseWithStreamingBody()
1331+
{
1332+
$stream = new ThroughStream();
1333+
$stream->on('close', $this->expectCallableOnce());
1334+
1335+
$server = new StreamingServer(Factory::create(), function (ServerRequestInterface $request) use ($stream) {
1336+
return new Response(
1337+
204,
1338+
array('Content-Length' => '3'),
1339+
$stream
1340+
);
1341+
});
1342+
1343+
$buffer = '';
1344+
$this->connection
1345+
->expects($this->any())
1346+
->method('write')
1347+
->will(
1348+
$this->returnCallback(
1349+
function ($data) use (&$buffer) {
1350+
$buffer .= $data;
1351+
}
1352+
)
1353+
);
1354+
1355+
$server->listen($this->socket);
1356+
$this->socket->emit('connection', array($this->connection));
1357+
1358+
$data = "HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n";
1359+
$this->connection->emit('data', array($data));
1360+
1361+
$this->assertContainsString("HTTP/1.1 204 No Content\r\n", $buffer);
1362+
$this->assertNotContainsString("\r\nContent-Length: 3\r\n", $buffer);
1363+
}
1364+
12941365
public function testResponseContainsNoResponseBodyForNotModifiedStatus()
12951366
{
12961367
$server = new StreamingServer(Factory::create(), function (ServerRequestInterface $request) {
@@ -1324,6 +1395,41 @@ function ($data) use (&$buffer) {
13241395
$this->assertNotContainsString("bye", $buffer);
13251396
}
13261397

1398+
public function testResponseContainsNoResponseBodyForNotModifiedStatusWithStreamingBody()
1399+
{
1400+
$stream = new ThroughStream();
1401+
$stream->on('close', $this->expectCallableOnce());
1402+
1403+
$server = new StreamingServer(Factory::create(), function (ServerRequestInterface $request) use ($stream) {
1404+
return new Response(
1405+
304,
1406+
array('Content-Length' => '3'),
1407+
$stream
1408+
);
1409+
});
1410+
1411+
$buffer = '';
1412+
$this->connection
1413+
->expects($this->any())
1414+
->method('write')
1415+
->will(
1416+
$this->returnCallback(
1417+
function ($data) use (&$buffer) {
1418+
$buffer .= $data;
1419+
}
1420+
)
1421+
);
1422+
1423+
$server->listen($this->socket);
1424+
$this->socket->emit('connection', array($this->connection));
1425+
1426+
$data = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
1427+
$this->connection->emit('data', array($data));
1428+
1429+
$this->assertContainsString("HTTP/1.1 304 Not Modified\r\n", $buffer);
1430+
$this->assertContainsString("\r\nContent-Length: 3\r\n", $buffer);
1431+
}
1432+
13271433
public function testRequestInvalidHttpProtocolVersionWillEmitErrorAndSendErrorResponse()
13281434
{
13291435
$error = null;

0 commit comments

Comments
 (0)