1414 * The constructor is internal, you SHOULD NOT call this yourself.
1515 * The `Server` is responsible for emitting `Request` and `Response` objects.
1616 *
17+ * The `Response` will automatically use the same HTTP protocol version as the
18+ * corresponding `Request`.
19+ *
20+ * HTTP/1.1 responses will automatically apply chunked transfer encoding if
21+ * no `Content-Length` header has been set.
22+ * See `writeHead()` for more details.
23+ *
1724 * See the usage examples and the class outline for details.
1825 *
1926 * @see WritableStreamInterface
2027 * @see Server
2128 */
2229class Response extends EventEmitter implements WritableStreamInterface
2330{
31+ private $ conn ;
32+ private $ protocolVersion ;
33+
2434 private $ closed = false ;
2535 private $ writable = true ;
26- private $ conn ;
2736 private $ headWritten = false ;
28- private $ chunkedEncoding = true ;
37+ private $ chunkedEncoding = false ;
2938
3039 /**
3140 * The constructor is internal, you SHOULD NOT call this yourself.
@@ -36,9 +45,11 @@ class Response extends EventEmitter implements WritableStreamInterface
3645 *
3746 * @internal
3847 */
39- public function __construct (ConnectionInterface $ conn )
48+ public function __construct (ConnectionInterface $ conn, $ protocolVersion = ' 1.1 ' )
4049 {
4150 $ this ->conn = $ conn ;
51+ $ this ->protocolVersion = $ protocolVersion ;
52+
4253 $ that = $ this ;
4354 $ this ->conn ->on ('end ' , function () use ($ that ) {
4455 $ that ->close ();
@@ -87,19 +98,25 @@ public function isWritable()
8798 * });
8899 * ```
89100 *
90- * Note that calling this method is strictly optional.
91- * If you do not use it, then the client MUST continue sending the request body
92- * after waiting some time.
101+ * Note that calling this method is strictly optional for HTTP/1.1 responses .
102+ * If you do not use it, then a HTTP/1.1 client MUST continue sending the
103+ * request body after waiting some time.
93104 *
94105 * This method MUST NOT be invoked after calling `writeHead()`.
95- * Calling this method after sending the headers will result in an `Exception`.
106+ * This method MUST NOT be invoked if this is not a HTTP/1.1 response
107+ * (please check [`expectsContinue()`] as above).
108+ * 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`.
96110 *
97111 * @return void
98112 * @throws \Exception
99113 * @see Request::expectsContinue()
100114 */
101115 public function writeContinue ()
102116 {
117+ if ($ this ->protocolVersion !== '1.1 ' ) {
118+ throw new \Exception ('Continue requires a HTTP/1.1 message ' );
119+ }
103120 if ($ this ->headWritten ) {
104121 throw new \Exception ('Response head has already been written. ' );
105122 }
@@ -122,7 +139,7 @@ public function writeContinue()
122139 *
123140 * Calling this method more than once will result in an `Exception`.
124141 *
125- * Unless you specify a `Content-Length` header yourself, the response message
142+ * Unless you specify a `Content-Length` header yourself, HTTP/1.1 responses
126143 * will automatically use chunked transfer encoding and send the respective header
127144 * (`Transfer-Encoding: chunked`) automatically. If you know the length of your
128145 * body, you MAY specify it like this instead:
@@ -167,11 +184,6 @@ public function writeHead($status = 200, array $headers = array())
167184
168185 $ lower = array_change_key_case ($ headers );
169186
170- // disable chunked encoding if content-length is given
171- if (isset ($ lower ['content-length ' ])) {
172- $ this ->chunkedEncoding = false ;
173- }
174-
175187 // assign default "X-Powered-By" header as first for history reasons
176188 if (!isset ($ lower ['x-powered-by ' ])) {
177189 $ headers = array_merge (
@@ -180,15 +192,16 @@ public function writeHead($status = 200, array $headers = array())
180192 );
181193 }
182194
183- // assign chunked transfer-encoding if chunked encoding is used
184- if ($ this ->chunkedEncoding ) {
195+ // assign chunked transfer-encoding if no 'content-length' is given for HTTP/1.1 responses
196+ if (! isset ( $ lower [ ' content-length ' ]) && $ this ->protocolVersion === ' 1.1 ' ) {
185197 foreach ($ headers as $ name => $ value ) {
186198 if (strtolower ($ name ) === 'transfer-encoding ' ) {
187199 unset($ headers [$ name ]);
188200 }
189201 }
190202
191203 $ headers ['Transfer-Encoding ' ] = 'chunked ' ;
204+ $ this ->chunkedEncoding = true ;
192205 }
193206
194207 $ data = $ this ->formatHead ($ status , $ headers );
@@ -201,7 +214,7 @@ private function formatHead($status, array $headers)
201214 {
202215 $ status = (int ) $ status ;
203216 $ text = isset (ResponseCodes::$ statusTexts [$ status ]) ? ResponseCodes::$ statusTexts [$ status ] : '' ;
204- $ data = "HTTP/1.1 $ status $ text \r\n" ;
217+ $ data = "HTTP/ $ this -> protocolVersion $ status $ text \r\n" ;
205218
206219 foreach ($ headers as $ name => $ value ) {
207220 $ name = str_replace (array ("\r" , "\n" ), '' , $ name );
0 commit comments