From d81fca15cb94c13995217781670ac2a999d448d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 31 Mar 2017 01:21:36 +0200 Subject: [PATCH] Certain status codes never contain a body length See https://tools.ietf.org/html/rfc7230#section-3.3.1 and https://tools.ietf.org/html/rfc7230#section-3.3.2 --- README.md | 24 +++++++++++++++++------- src/Server.php | 7 ++++++- tests/ServerTest.php | 4 +++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 03b037d8..8ff210e5 100644 --- a/README.md +++ b/README.md @@ -356,12 +356,14 @@ This is just a example you could use of the streaming, you could also send a big amount of data via little chunks or use it for body data that needs to calculated. -If the response body is a `string` a `Content-Length` header will be added automatically. -Unless you specify a `Content-Length` header for a ReactPHP `ReadableStreamInterface` -response body yourself, HTTP/1.1 responses will automatically use chunked transfer encoding -and send the respective header -(`Transfer-Encoding: chunked`) automatically. The server is responsible for handling -`Transfer-Encoding` so you SHOULD NOT pass it yourself. +If the response body is a `string`, a `Content-Length` header will be added +automatically. +If the response body is a ReactPHP `ReadableStreamInterface` and you do not +specify a `Content-Length` header, HTTP/1.1 responses will automatically use +chunked transfer encoding and send the respective header +(`Transfer-Encoding: chunked`) automatically. +The server is responsible for handling `Transfer-Encoding` so you SHOULD NOT +pass this header yourself. If you know the length of your stream body, you MAY specify it like this instead: ```php @@ -387,10 +389,18 @@ The `Server` will add the protocol version of the request, so you don't have to. Any response to a `HEAD` request and any response with a `1xx` (Informational), `204` (No Content) or `304` (Not Modified) status code will *not* include a -message body as per the HTTP spec. +message body as per the HTTP specs. This means that your callback does not have to take special care of this and any response body will simply be ignored. +Similarly, any response with a `1xx` (Informational), `204` (No Content) status +code will *not* include `Content-Length` or `Transfer-Encoding` header as +these do not apply to these messages. +Note that a response to a `HEAD` request and any response with a `304` (Not +Modified) status code MAY include these headers even though +the message does not contain a response body, because these header would apply +to the message if the same request would have used an (unconditional) `GET`. + 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: diff --git a/src/Server.php b/src/Server.php index f8eb4e8b..b9f421b7 100644 --- a/src/Server.php +++ b/src/Server.php @@ -336,8 +336,13 @@ public function handleResponse(ConnectionInterface $connection, RequestInterface $response = $response->withHeader('Connection', 'close'); } - // response to HEAD and 1xx, 204 and 304 responses MUST NOT include a body + // response code 1xx and 204 MUST NOT include Content-Length or Transfer-Encoding header $code = $response->getStatusCode(); + if (($code >= 100 && $code < 200) || $code === 204) { + $response = $response->withoutHeader('Content-Length')->withoutHeader('Transfer-Encoding'); + } + + // response to HEAD and 1xx, 204 and 304 responses MUST NOT include a body if ($request->getMethod() === 'HEAD' || ($code >= 100 && $code < 200) || $code === 204 || $code === 304) { $response = $response->withBody(Psr7Implementation\stream_for('')); } diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 280ad702..9bab5c89 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -358,7 +358,7 @@ function ($data) use (&$buffer) { $this->assertNotContains("bye", $buffer); } - public function testResponseContainsNoResponseBodyForNoContentStatus() + public function testResponseContainsNoResponseBodyAndNoContentLengthForNoContentStatus() { $server = new Server($this->socket, function (RequestInterface $request) { return new Response(204, array(), 'bye'); @@ -382,6 +382,7 @@ function ($data) use (&$buffer) { $this->connection->emit('data', array($data)); $this->assertContains("HTTP/1.1 204 No Content\r\n", $buffer); + $this->assertNotContains("\r\n\Content-Length: 3\r\n", $buffer); $this->assertNotContains("bye", $buffer); } @@ -409,6 +410,7 @@ function ($data) use (&$buffer) { $this->connection->emit('data', array($data)); $this->assertContains("HTTP/1.1 304 Not Modified\r\n", $buffer); + $this->assertContains("\r\nContent-Length: 3\r\n", $buffer); $this->assertNotContains("bye", $buffer); }