Skip to content

Commit 4468182

Browse files
authored
Merge pull request #131 from clue-labs/response-close
Response closes after forwarding close event
2 parents 3616d16 + 3c08836 commit 4468182

4 files changed

Lines changed: 285 additions & 45 deletions

File tree

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ This method MUST NOT be invoked after calling [`writeHead()`](#writehead).
231231
This method MUST NOT be invoked if this is not a HTTP/1.1 response
232232
(please check [`expectsContinue()`](#expectscontinue) as above).
233233
Calling this method after sending the headers or if this is not a HTTP/1.1
234-
response is an error that will result in an `Exception`.
234+
response is an error that will result in an `Exception`
235+
(unless the response has ended/closed already).
236+
Calling this method after the response has ended/closed is a NOOP.
235237

236238
#### writeHead()
237239

@@ -248,7 +250,9 @@ $response->writeHead(200, array(
248250
$response->end('Hello World!');
249251
```
250252

251-
Calling this method more than once will result in an `Exception`.
253+
Calling this method more than once will result in an `Exception`
254+
(unless the response has ended/closed already).
255+
Calling this method after the response has ended/closed is a NOOP.
252256

253257
Unless you specify a `Content-Length` header yourself, HTTP/1.1 responses
254258
will automatically use chunked transfer encoding and send the respective header

src/Response.php

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace React\Http;
44

55
use Evenement\EventEmitter;
6-
use React\Socket\ConnectionInterface;
76
use React\Stream\WritableStreamInterface;
87

98
/**
@@ -45,19 +44,16 @@ class Response extends EventEmitter implements WritableStreamInterface
4544
*
4645
* @internal
4746
*/
48-
public function __construct(ConnectionInterface $conn, $protocolVersion = '1.1')
47+
public function __construct(WritableStreamInterface $conn, $protocolVersion = '1.1')
4948
{
5049
$this->conn = $conn;
5150
$this->protocolVersion = $protocolVersion;
5251

5352
$that = $this;
54-
$this->conn->on('end', function () use ($that) {
55-
$that->close();
56-
});
53+
$this->conn->on('close', array($this, 'close'));
5754

5855
$this->conn->on('error', function ($error) use ($that) {
59-
$that->emit('error', array($error, $that));
60-
$that->close();
56+
$that->emit('error', array($error));
6157
});
6258

6359
$this->conn->on('drain', function () use ($that) {
@@ -106,14 +102,19 @@ public function isWritable()
106102
* This method MUST NOT be invoked if this is not a HTTP/1.1 response
107103
* (please check [`expectsContinue()`] as above).
108104
* Calling this method after sending the headers or if this is not a HTTP/1.1
109-
* response is an error that will result in an `Exception`.
105+
* response is an error that will result in an `Exception`
106+
* (unless the response has ended/closed already).
107+
* Calling this method after the response has ended/closed is a NOOP.
110108
*
111109
* @return void
112110
* @throws \Exception
113111
* @see Request::expectsContinue()
114112
*/
115113
public function writeContinue()
116114
{
115+
if (!$this->writable) {
116+
return;
117+
}
117118
if ($this->protocolVersion !== '1.1') {
118119
throw new \Exception('Continue requires a HTTP/1.1 message');
119120
}
@@ -137,7 +138,9 @@ public function writeContinue()
137138
* $response->end('Hello World!');
138139
* ```
139140
*
140-
* Calling this method more than once will result in an `Exception`.
141+
* Calling this method more than once will result in an `Exception`
142+
* (unless the response has ended/closed already).
143+
* Calling this method after the response has ended/closed is a NOOP.
141144
*
142145
* Unless you specify a `Content-Length` header yourself, HTTP/1.1 responses
143146
* will automatically use chunked transfer encoding and send the respective header
@@ -184,6 +187,9 @@ public function writeContinue()
184187
*/
185188
public function writeHead($status = 200, array $headers = array())
186189
{
190+
if (!$this->writable) {
191+
return;
192+
}
187193
if ($this->headWritten) {
188194
throw new \Exception('Response head has already been written.');
189195
}
@@ -250,6 +256,9 @@ private function formatHead($status, array $headers)
250256

251257
public function write($data)
252258
{
259+
if (!$this->writable) {
260+
return false;
261+
}
253262
if (!$this->headWritten) {
254263
throw new \Exception('Response head has not yet been written.');
255264
}
@@ -271,6 +280,13 @@ public function write($data)
271280

272281
public function end($data = null)
273282
{
283+
if (!$this->writable) {
284+
return;
285+
}
286+
if (!$this->headWritten) {
287+
throw new \Exception('Response head has not yet been written.');
288+
}
289+
274290
if (null !== $data) {
275291
$this->write($data);
276292
}
@@ -279,8 +295,7 @@ public function end($data = null)
279295
$this->conn->write("0\r\n\r\n");
280296
}
281297

282-
$this->emit('end');
283-
$this->removeAllListeners();
298+
$this->writable = false;
284299
$this->conn->end();
285300
}
286301

@@ -291,10 +306,10 @@ public function close()
291306
}
292307

293308
$this->closed = true;
294-
295309
$this->writable = false;
310+
$this->conn->close();
311+
296312
$this->emit('close');
297313
$this->removeAllListeners();
298-
$this->conn->close();
299314
}
300315
}

0 commit comments

Comments
 (0)