33namespace React \Http ;
44
55use Evenement \EventEmitter ;
6- use React \Socket \ConnectionInterface ;
76use 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