Skip to content

Commit

Permalink
Merge pull request #128 from clue-labs/send-close
Browse files Browse the repository at this point in the history
Explicitly send Connection: close header for HTTP/1.1 messages
  • Loading branch information
WyriHaximus authored Feb 20, 2017
2 parents b90e080 + c72c125 commit aa47f5c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@ $response->writeHead(200, array(
));
```

Note that persistent connections (`Connection: keep-alive`) are currently
not supported.
As such, HTTP/1.1 response messages will automatically include a
`Connection: close` header, irrespective of what header values are
passed explicitly.

## Install

The recommended way to install this library is [through Composer](http://getcomposer.org).
Expand Down
18 changes: 18 additions & 0 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ public function writeContinue()
* ));
* ```
*
* Note that persistent connections (`Connection: keep-alive`) are currently
* not supported.
* As such, HTTP/1.1 response messages will automatically include a
* `Connection: close` header, irrespective of what header values are
* passed explicitly.
*
* @param int $status
* @param array $headers
* @throws \Exception
Expand Down Expand Up @@ -204,6 +210,18 @@ public function writeHead($status = 200, array $headers = array())
$this->chunkedEncoding = true;
}

// HTTP/1.1 assumes persistent connection support by default
// we do not support persistent connections, so let the client know
if ($this->protocolVersion === '1.1') {
foreach($headers as $name => $value) {
if (strtolower($name) === 'connection') {
unset($headers[$name]);
}
}

$headers['Connection'] = 'close';
}

$data = $this->formatHead($status, $headers);
$this->conn->write($data);

Expand Down
31 changes: 31 additions & 0 deletions tests/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public function testResponseShouldBeChunkedByDefault()
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Transfer-Encoding: chunked\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand Down Expand Up @@ -51,6 +52,7 @@ public function testResponseShouldBeChunkedEvenWithOtherTransferEncoding()
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Transfer-Encoding: chunked\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -71,6 +73,7 @@ public function testResponseShouldNotBeChunkedWithContentLength()
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Content-Length: 22\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -91,6 +94,7 @@ public function testResponseShouldNotBeChunkedWithContentLengthCaseInsensitive()
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "CONTENT-LENGTH: 0\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -111,6 +115,7 @@ public function testResponseShouldIncludeCustomByPoweredAsFirstHeaderIfGivenExpl
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "Content-Length: 0\r\n";
$expected .= "X-POWERED-BY: demo\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -130,6 +135,7 @@ public function testResponseShouldNotIncludePoweredByIfGivenEmptyArray()
$expected = '';
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "Content-Length: 0\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -144,6 +150,27 @@ public function testResponseShouldNotIncludePoweredByIfGivenEmptyArray()
$response->writeHead(200, array('Content-Length' => 0, 'X-Powered-By' => array()));
}

public function testResponseShouldAlwaysIncludeConnectionCloseIrrespectiveOfExplicitValue()
{
$expected = '';
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Content-Length: 0\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
->getMockBuilder('React\Socket\ConnectionInterface')
->getMock();
$conn
->expects($this->once())
->method('write')
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array('Content-Length' => 0, 'connection' => 'ignored'));
}

public function testResponseBodyShouldBeChunkedCorrectly()
{
$conn = $this
Expand Down Expand Up @@ -283,6 +310,7 @@ public function shouldRemoveNewlinesFromHeaders()
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "FooBar: BazQux\r\n";
$expected .= "Transfer-Encoding: chunked\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -304,6 +332,7 @@ public function missingStatusCodeTextShouldResultInNumberOnlyStatus()
$expected .= "HTTP/1.1 700 \r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Transfer-Encoding: chunked\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -327,6 +356,7 @@ public function shouldAllowArrayHeaderValues()
$expected .= "Set-Cookie: foo=bar\r\n";
$expected .= "Set-Cookie: bar=baz\r\n";
$expected .= "Transfer-Encoding: chunked\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand All @@ -348,6 +378,7 @@ public function shouldIgnoreHeadersWithNullValues()
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Transfer-Encoding: chunked\r\n";
$expected .= "Connection: close\r\n";
$expected .= "\r\n";

$conn = $this
Expand Down

0 comments on commit aa47f5c

Please sign in to comment.