Skip to content

Commit

Permalink
Add system date 'Date' header if none isset
Browse files Browse the repository at this point in the history
  • Loading branch information
legionth committed Mar 6, 2017
1 parent 65052b4 commit a137fc1
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 12 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,24 @@ $response->writeHead(200, array(
$response->end($data);
```

A `Date` header will be automatically added with the system date and time if none is given.
You can add a custom `Date` header yourself like this:

```php
$response->writeHead(200, array(
'Date' => date('D, d M Y H:i:s T')
));
```

If you don't have a appropriate clock to rely on, you should
unset this header with an empty array:

```php
$response->writeHead(200, array(
'Date' => array()
));
```

Note that it will automatically assume a `X-Powered-By: react/alpha` header
unless your specify a custom `X-Powered-By` header yourself:

Expand Down
6 changes: 6 additions & 0 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ public function writeHead($status = 200, array $headers = array())
);
}

// assign date header if no 'date' is given, use the current time where this code is running
if (!isset($lower['date'])) {
// IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
$headers['Date'] = gmdate('D, d M Y H:i:s') . ' GMT';
}

// assign chunked transfer-encoding if no 'content-length' is given for HTTP/1.1 responses
if (!isset($lower['content-length']) && $this->protocolVersion === '1.1') {
foreach($headers as $name => $value) {
Expand Down
91 changes: 79 additions & 12 deletions tests/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function testResponseShouldBeChunkedByDefault()
->with($expected);

$response = new Response($conn);
$response->writeHead();
$response->writeHead(200, array('Date' => array()));
}

public function testResponseShouldNotBeChunkedWhenProtocolVersionIsNot11()
Expand All @@ -44,7 +44,7 @@ public function testResponseShouldNotBeChunkedWhenProtocolVersionIsNot11()
->with($expected);

$response = new Response($conn, '1.0');
$response->writeHead();
$response->writeHead(200, array('Date' => array()));
}

public function testResponseShouldBeChunkedEvenWithOtherTransferEncoding()
Expand All @@ -65,7 +65,7 @@ public function testResponseShouldBeChunkedEvenWithOtherTransferEncoding()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array('transfer-encoding' => 'custom'));
$response->writeHead(200, array('transfer-encoding' => 'custom', 'Date' => array()));
}

public function testResponseShouldNotBeChunkedWithContentLength()
Expand All @@ -86,7 +86,7 @@ public function testResponseShouldNotBeChunkedWithContentLength()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array('Content-Length' => 22));
$response->writeHead(200, array('Content-Length' => 22, 'Date' => array()));
}

public function testResponseShouldNotBeChunkedWithContentLengthCaseInsensitive()
Expand All @@ -107,7 +107,7 @@ public function testResponseShouldNotBeChunkedWithContentLengthCaseInsensitive()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array('CONTENT-LENGTH' => 0));
$response->writeHead(200, array('CONTENT-LENGTH' => 0, 'Date' => array()));
}

public function testResponseShouldIncludeCustomByPoweredAsFirstHeaderIfGivenExplicitly()
Expand All @@ -128,7 +128,7 @@ public function testResponseShouldIncludeCustomByPoweredAsFirstHeaderIfGivenExpl
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array('Content-Length' => 0, 'X-POWERED-BY' => 'demo'));
$response->writeHead(200, array('Content-Length' => 0, 'X-POWERED-BY' => 'demo', 'Date' => array()));
}

public function testResponseShouldNotIncludePoweredByIfGivenEmptyArray()
Expand All @@ -148,7 +148,7 @@ public function testResponseShouldNotIncludePoweredByIfGivenEmptyArray()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array('Content-Length' => 0, 'X-Powered-By' => array()));
$response->writeHead(200, array('Content-Length' => 0, 'X-Powered-By' => array(), 'Date' => array()));
}

public function testResponseShouldAlwaysIncludeConnectionCloseIrrespectiveOfExplicitValue()
Expand All @@ -169,7 +169,7 @@ public function testResponseShouldAlwaysIncludeConnectionCloseIrrespectiveOfExpl
->with($expected);

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

/** @expectedException Exception */
Expand Down Expand Up @@ -399,7 +399,7 @@ public function shouldRemoveNewlinesFromHeaders()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array("Foo\nBar" => "Baz\rQux"));
$response->writeHead(200, array("Foo\nBar" => "Baz\rQux", 'Date' => array()));
}

/** @test */
Expand All @@ -421,7 +421,7 @@ public function missingStatusCodeTextShouldResultInNumberOnlyStatus()
->with($expected);

$response = new Response($conn);
$response->writeHead(700);
$response->writeHead(700, array('Date' => array()));
}

/** @test */
Expand All @@ -445,7 +445,7 @@ public function shouldAllowArrayHeaderValues()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array("Set-Cookie" => array("foo=bar", "bar=baz")));
$response->writeHead(200, array("Set-Cookie" => array("foo=bar", "bar=baz"), 'Date' => array()));
}

/** @test */
Expand All @@ -467,7 +467,7 @@ public function shouldIgnoreHeadersWithNullValues()
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array("FooBar" => null));
$response->writeHead(200, array("FooBar" => null, 'Date' => array()));
}

public function testCloseClosesInputAndEmitsCloseEvent()
Expand Down Expand Up @@ -596,4 +596,71 @@ public function testDrainEventShouldBeForwarded()

$input->emit('drain');
}

public function testDateHeaderWillUseServerTime()
{
$buffer = '';
$conn = $this
->getMockBuilder('React\Socket\ConnectionInterface')
->getMock();
$conn
->expects($this->once())
->method('write')
->will(
$this->returnCallback(
function ($data) use (&$buffer) {
$buffer .= $data;
}
)
);

$response = new Response($conn);
$response->writeHead();

$this->assertContains("HTTP/1.1 200 OK\r\n", $buffer);
$this->assertContains("Date:", $buffer);
}

public function testDateHeaderWithCustomDate()
{
$expected = '';
$expected .= "HTTP/1.1 200 OK\r\n";
$expected .= "X-Powered-By: React/alpha\r\n";
$expected .= "Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n";
$expected .= "Transfer-Encoding: chunked\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("Date" => "Tue, 15 Nov 1994 08:12:31 GMT"));
}

public function testDateHeaderWillBeRemoved()
{
$expected = '';
$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
->getMockBuilder('React\Socket\ConnectionInterface')
->getMock();
$conn
->expects($this->once())
->method('write')
->with($expected);

$response = new Response($conn);
$response->writeHead(200, array("Date" => array()));
}
}
93 changes: 93 additions & 0 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,99 @@ public function testRequestWithoutDefinedLengthWillIgnoreDataEvent()
$this->connection->emit('data', array($data));
}

public function testDateHeaderWillBeAddedWhenNoneIsGiven()
{
$server = new Server($this->socket);

$server->on('request', function (Request $request, Response $response) {
$response->writeHead(200);
});

$buffer = '';
$this->connection
->expects($this->once())
->method('write')
->will(
$this->returnCallback(
function ($data) use (&$buffer) {
$buffer .= $data;
}
)
);

$this->socket->emit('connection', array($this->connection));

$data = $this->createGetRequest();

$this->connection->emit('data', array($data));

$this->assertContains("HTTP/1.1 200 OK\r\n", $buffer);
$this->assertContains("Date:", $buffer);
$this->assertContains("\r\n\r\n", $buffer);
}

public function testAddCustomDateHeader()
{
$server = new Server($this->socket);

$server->on('request', function (Request $request, Response $response) {
$response->writeHead(200, array("Date" => "Tue, 15 Nov 1994 08:12:31 GMT"));
});

$buffer = '';
$this->connection
->expects($this->once())
->method('write')
->will(
$this->returnCallback(
function ($data) use (&$buffer) {
$buffer .= $data;
}
)
);

$this->socket->emit('connection', array($this->connection));

$data = $this->createGetRequest();

$this->connection->emit('data', array($data));

$this->assertContains("HTTP/1.1 200 OK\r\n", $buffer);
$this->assertContains("Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n", $buffer);
$this->assertContains("\r\n\r\n", $buffer);
}

public function testRemoveDateHeader()
{
$server = new Server($this->socket);

$server->on('request', function (Request $request, Response $response) {
$response->writeHead(200, array('Date' => array()));
});

$buffer = '';
$this->connection
->expects($this->once())
->method('write')
->will(
$this->returnCallback(
function ($data) use (&$buffer) {
$buffer .= $data;
}
)
);

$this->socket->emit('connection', array($this->connection));

$data = $this->createGetRequest();

$this->connection->emit('data', array($data));

$this->assertContains("HTTP/1.1 200 OK\r\n", $buffer);
$this->assertNotContains("Date:", $buffer);
$this->assertContains("\r\n\r\n", $buffer);
}

private function createGetRequest()
{
$data = "GET / HTTP/1.1\r\n";
Expand Down

0 comments on commit a137fc1

Please sign in to comment.