16
16
* @event response
17
17
* @event drain
18
18
* @event error
19
- * @event end
19
+ * @event close
20
20
* @internal
21
21
*/
22
22
class ClientRequestStream extends EventEmitter implements WritableStreamInterface
@@ -33,9 +33,11 @@ class ClientRequestStream extends EventEmitter implements WritableStreamInterfac
33
33
private $ request ;
34
34
35
35
/** @var ?ConnectionInterface */
36
- private $ stream ;
36
+ private $ connection ;
37
+
38
+ /** @var string */
39
+ private $ buffer = '' ;
37
40
38
- private $ buffer ;
39
41
private $ responseFactory ;
40
42
private $ state = self ::STATE_INIT ;
41
43
private $ ended = false ;
@@ -58,22 +60,22 @@ private function writeHead()
58
60
$ this ->state = self ::STATE_WRITING_HEAD ;
59
61
60
62
$ request = $ this ->request ;
61
- $ streamRef = &$ this ->stream ;
63
+ $ connectionRef = &$ this ->connection ;
62
64
$ stateRef = &$ this ->state ;
63
65
$ pendingWrites = &$ this ->pendingWrites ;
64
66
$ that = $ this ;
65
67
66
68
$ promise = $ this ->connect ();
67
69
$ promise ->then (
68
- function (ConnectionInterface $ stream ) use ($ request , &$ streamRef , &$ stateRef , &$ pendingWrites , $ that ) {
69
- $ streamRef = $ stream ;
70
- assert ($ streamRef instanceof ConnectionInterface);
70
+ function (ConnectionInterface $ connection ) use ($ request , &$ connectionRef , &$ stateRef , &$ pendingWrites , $ that ) {
71
+ $ connectionRef = $ connection ;
72
+ assert ($ connectionRef instanceof ConnectionInterface);
71
73
72
- $ stream ->on ('drain ' , array ($ that , 'handleDrain ' ));
73
- $ stream ->on ('data ' , array ($ that , 'handleData ' ));
74
- $ stream ->on ('end ' , array ($ that , 'handleEnd ' ));
75
- $ stream ->on ('error ' , array ($ that , 'handleError ' ));
76
- $ stream ->on ('close ' , array ($ that , 'handleClose ' ));
74
+ $ connection ->on ('drain ' , array ($ that , 'handleDrain ' ));
75
+ $ connection ->on ('data ' , array ($ that , 'handleData ' ));
76
+ $ connection ->on ('end ' , array ($ that , 'handleEnd ' ));
77
+ $ connection ->on ('error ' , array ($ that , 'handleError ' ));
78
+ $ connection ->on ('close ' , array ($ that , 'close ' ));
77
79
78
80
assert ($ request instanceof RequestInterface);
79
81
$ headers = "{$ request ->getMethod ()} {$ request ->getRequestTarget ()} HTTP/ {$ request ->getProtocolVersion ()}\r\n" ;
@@ -83,7 +85,7 @@ function (ConnectionInterface $stream) use ($request, &$streamRef, &$stateRef, &
83
85
}
84
86
}
85
87
86
- $ more = $ stream ->write ($ headers . "\r\n" . $ pendingWrites );
88
+ $ more = $ connection ->write ($ headers . "\r\n" . $ pendingWrites );
87
89
88
90
assert ($ stateRef === ClientRequestStream::STATE_WRITING_HEAD );
89
91
$ stateRef = ClientRequestStream::STATE_HEAD_WRITTEN ;
@@ -113,7 +115,7 @@ public function write($data)
113
115
114
116
// write directly to connection stream if already available
115
117
if (self ::STATE_HEAD_WRITTEN <= $ this ->state ) {
116
- return $ this ->stream ->write ($ data );
118
+ return $ this ->connection ->write ($ data );
117
119
}
118
120
119
121
// otherwise buffer and try to establish connection
@@ -157,26 +159,28 @@ public function handleData($data)
157
159
$ response = gPsr \parse_response ($ this ->buffer );
158
160
$ bodyChunk = (string ) $ response ->getBody ();
159
161
} catch (\InvalidArgumentException $ exception ) {
160
- $ this ->emit ('error ' , array ($ exception ));
161
- }
162
-
163
- $ this ->buffer = null ;
164
-
165
- $ this ->stream ->removeListener ('drain ' , array ($ this , 'handleDrain ' ));
166
- $ this ->stream ->removeListener ('data ' , array ($ this , 'handleData ' ));
167
- $ this ->stream ->removeListener ('end ' , array ($ this , 'handleEnd ' ));
168
- $ this ->stream ->removeListener ('error ' , array ($ this , 'handleError ' ));
169
- $ this ->stream ->removeListener ('close ' , array ($ this , 'handleClose ' ));
170
-
171
- if (!isset ($ response )) {
162
+ $ this ->closeError ($ exception );
172
163
return ;
173
164
}
174
165
175
- $ this ->stream ->on ('close ' , array ($ this , 'handleClose ' ));
176
-
177
- assert ($ response instanceof ResponseInterface);
178
- assert ($ this ->stream instanceof ConnectionInterface);
179
- $ body = $ this ->stream ;
166
+ // response headers successfully received => remove listeners for connection events
167
+ $ connection = $ this ->connection ;
168
+ assert ($ connection instanceof ConnectionInterface);
169
+ $ connection ->removeListener ('drain ' , array ($ this , 'handleDrain ' ));
170
+ $ connection ->removeListener ('data ' , array ($ this , 'handleData ' ));
171
+ $ connection ->removeListener ('end ' , array ($ this , 'handleEnd ' ));
172
+ $ connection ->removeListener ('error ' , array ($ this , 'handleError ' ));
173
+ $ connection ->removeListener ('close ' , array ($ this , 'close ' ));
174
+ $ this ->connection = null ;
175
+ $ this ->buffer = '' ;
176
+
177
+ // take control over connection handling and close connection once response body closes
178
+ $ that = $ this ;
179
+ $ input = $ body = new CloseProtectionStream ($ connection );
180
+ $ input ->on ('close ' , function () use ($ connection , $ that ) {
181
+ $ connection ->close ();
182
+ $ that ->close ();
183
+ });
180
184
181
185
// determine length of response body
182
186
$ length = null ;
@@ -194,7 +198,11 @@ public function handleData($data)
194
198
$ this ->emit ('response ' , array ($ response , $ body ));
195
199
196
200
// re-emit HTTP response body to trigger body parsing if parts of it are buffered
197
- $ this ->stream ->emit ('data ' , array ($ bodyChunk ));
201
+ if ($ bodyChunk !== '' ) {
202
+ $ input ->handleData ($ bodyChunk );
203
+ } elseif ($ length === 0 ) {
204
+ $ input ->handleEnd ();
205
+ }
198
206
}
199
207
}
200
208
@@ -216,12 +224,6 @@ public function handleError(\Exception $error)
216
224
));
217
225
}
218
226
219
- /** @internal */
220
- public function handleClose ()
221
- {
222
- $ this ->close ();
223
- }
224
-
225
227
/** @internal */
226
228
public function closeError (\Exception $ error )
227
229
{
@@ -240,9 +242,11 @@ public function close()
240
242
241
243
$ this ->state = self ::STATE_END ;
242
244
$ this ->pendingWrites = '' ;
245
+ $ this ->buffer = '' ;
243
246
244
- if ($ this ->stream ) {
245
- $ this ->stream ->close ();
247
+ if ($ this ->connection instanceof ConnectionInterface) {
248
+ $ this ->connection ->close ();
249
+ $ this ->connection = null ;
246
250
}
247
251
248
252
$ this ->emit ('close ' );
0 commit comments