8787 * **Supports custom request headers and body**
8888 */
8989class Client {
90- protected $ concurrency = 2 ;
91- protected $ requests ;
92- protected $ onProgress ;
93- protected $ is_processing_queue = false ;
90+
91+ const STREAM_SELECT_READ = 1 ;
92+ const STREAM_SELECT_WRITE = 2 ;
93+
94+ const READ_NON_BLOCKING = 'READ_NON_BLOCKING ' ;
95+ const READ_POLL_ANY = 'READ_POLL_ANY ' ;
96+ const READ_POLL_ALL = 'READ_POLL_ALL ' ;
9497
9598 /**
9699 * Microsecond is 1 millionth of a second.
@@ -104,6 +107,11 @@ class Client {
104107 */
105108 const NONBLOCKING_TIMEOUT_MICROSECONDS = 0.05 * self ::MICROSECONDS_TO_SECONDS ;
106109
110+ protected $ concurrency = 2 ;
111+ protected $ requests ;
112+ protected $ onProgress ;
113+ protected $ is_processing_queue = false ;
114+
107115 public function __construct () {
108116 $ this ->requests = [];
109117 $ this ->onProgress = function () {
@@ -153,29 +161,6 @@ public function enqueue( $requests ) {
153161 return $ enqueued_streams ;
154162 }
155163
156- /**
157- * Returns the response stream associated with the given Request object.
158- * Reading from that stream also runs this Client's event loop.
159- *
160- * @param Request $request
161- *
162- * @return resource
163- */
164- public function get_stream ( $ request ) {
165- throw new Exception ('Not implemented yet ' );
166- // if ( ! isset( $this->requests[ $request ] ) ) {
167- // $this->enqueue_request( $request );
168- // }
169-
170- // if ( $this->queue_needs_processing ) {
171- // $this->process_queue();
172- // }
173-
174- // StreamWrapper::create_resource(
175- // new StreamData($request, $client)
176- // )
177- }
178-
179164 /**
180165 * @param \WordPress\AsyncHttp\Request $request
181166 */
@@ -184,10 +169,6 @@ protected function enqueue_request( $request ) {
184169 return $ request ->get_response ();
185170 }
186171
187-
188- const READ_NON_BLOCKING = 'READ_NON_BLOCKING ' ;
189- const READ_POLL_ANY = 'READ_POLL_ANY ' ;
190- const READ_POLL_ALL = 'READ_POLL_ALL ' ;
191172 /**
192173 * Reads $length bytes from the given request while also running
193174 * non-blocking event loop operations.
@@ -218,21 +199,74 @@ public function read_bytes( $request, $length, $mode = self::READ_NON_BLOCKING )
218199 ) {
219200 break ;
220201 }
221- } while ($ this ->event_loop_pass ());
202+ } while ($ this ->event_loop_tick ());
222203
223204 return $ buffered ;
224205 }
225206
226- public function event_loop_pass ( )
207+ public function wait_for_headers ( $ request )
227208 {
228- if (count ($ this ->get_concurrent_requests ()) === 0 ) {
209+ if (!in_array ($ request , $ this ->requests , true )) {
210+ trigger_error ('Request not found in the client ' , E_USER_WARNING );
229211 return false ;
230212 }
231- echo "event_loop_pass \n" ;
232- foreach ($ this ->requests as $ request ) {
233- echo "request state: $ request ->state \n" ;
213+
214+ while ($ this ->event_loop_tick () && $ request ->state !== Request::STATE_FAILED ) {
215+ if ($ request ->get_response ()->get_headers ()) {
216+ return true ;
217+ }
234218 }
235- sleep (1 );
219+
220+ return false ;
221+ }
222+
223+ public function wait_for_response_body_stream ( $ request )
224+ {
225+ if (!in_array ($ request , $ this ->requests , true )) {
226+ trigger_error ('Request not found in the client ' , E_USER_WARNING );
227+ return false ;
228+ }
229+
230+ while ($ this ->event_loop_tick () && $ request ->state !== Request::STATE_FAILED ) {
231+ if ($ request ->get_response ()->decoded_response_stream ) {
232+ return true ;
233+ }
234+ }
235+
236+ return false ;
237+ }
238+
239+ /**
240+ * Returns the response stream associated with the given Request object.
241+ * Reading from that stream also runs this Client's event loop.
242+ *
243+ * @param Request $request
244+ *
245+ * @return resource|bool
246+ */
247+ // public function get_response_stream( $request ) {
248+ // if(!in_array($request, $this->requests, true)) {
249+ // trigger_error('Request not found in the client', E_USER_WARNING);
250+ // return false;
251+ // }
252+
253+ // if(
254+ // $request->state !== Request::STATE_RECEIVING_BODY &&
255+ // $request->state !== Request::STATE_FINISHED
256+ // ) {
257+ // trigger_error('Request is not in a state where the response stream is available', E_USER_WARNING);
258+ // return false;
259+ // }
260+
261+ // return $request->get_response()->event_loop_decoded_response_stream;
262+ // }
263+
264+ public function event_loop_tick ()
265+ {
266+ if (count ($ this ->get_concurrent_requests ()) === 0 ) {
267+ return false ;
268+ }
269+
236270 static ::open_nonblocking_http_sockets (
237271 $ this ->get_concurrent_requests ( Request::STATE_ENQUEUED )
238272 );
@@ -275,16 +309,14 @@ protected function get_concurrent_requests($states=null)
275309 Request::STATE_RECEIVING_BODY ,
276310 Request::STATE_RECEIVED ,
277311 ]);
312+ $ available_slots = $ this ->concurrency - count ($ processed_requests );
278313 $ enqueued_requests = $ this ->get_requests (Request::STATE_ENQUEUED );
279- $ backfill_enqueued_nb = min (
280- count ($ enqueued_requests ),
281- $ this ->concurrency - count ($ processed_requests )
282- );
283-
284- for ($ i = 0 ; $ i < $ backfill_enqueued_nb ; $ i ++) {
314+ for ($ i = 0 ; $ i < $ available_slots ; $ i ++) {
315+ if (!isset ($ enqueued_requests [$ i ])) {
316+ break ;
317+ }
285318 $ processed_requests [] = $ enqueued_requests [$ i ];
286319 }
287-
288320 if ($ states !== null ) {
289321 $ processed_requests = static ::filter_requests ($ processed_requests , $ states );
290322 }
@@ -476,17 +508,13 @@ static private function receive_response_headers( $requests ) {
476508 $ response ->statusMessage = $ parsed ['status ' ]['message ' ];
477509 $ response ->protocol = $ parsed ['status ' ]['protocol ' ];
478510
479- $ content_length = $ response ->get_header ('content-length ' );
480- $ transfer_encoding = $ response ->get_header ('transfer-encoding ' );
481- // If we're expecting a body, let's start receiving it.
482- if (
483- $ transfer_encoding === 'chunked ' ||
484- ($ content_length !== null && (int ) $ content_length > 0 )
485- ) {
486- $ request ->state = Request::STATE_RECEIVING_BODY ;
487- } else {
511+ // If we're being redirected, we don't need to wait for the body.
512+ if ($ response ->statusCode >= 300 && $ response ->statusCode < 400 ) {
488513 $ request ->state = Request::STATE_RECEIVED ;
514+ break ;
489515 }
516+
517+ $ request ->state = Request::STATE_RECEIVING_BODY ;
490518 break ;
491519 }
492520 }
@@ -498,6 +526,9 @@ static private function receive_response_headers( $requests ) {
498526 * @param array $requests An array of requests.
499527 */
500528 private function receive_response_body ( $ requests ) {
529+ // @TODO: Assume body is received when either
530+ // * Content-Length is reached
531+ // * The last chunk in Transfer-Encoding: chunked is received
501532 foreach (static ::stream_select ($ requests , static ::STREAM_SELECT_READ ) as $ request ) {
502533 $ response = $ request ->get_response ();
503534 if (!$ response ->decoded_response_stream ) {
@@ -700,17 +731,15 @@ static private function filter_requests( array $requests, $states ) {
700731 $ states = [$ states ];
701732 }
702733 $ results = [];
703- foreach ($ requests as $ k => $ request ) {
734+ foreach ($ requests as $ request ) {
704735 if (in_array ($ request ->state , $ states )) {
705- $ results [$ k ] = $ request ;
736+ $ results [] = $ request ;
706737 }
707738 }
708739 return $ results ;
709740 }
710741
711742
712- const STREAM_SELECT_READ = 1 ;
713- const STREAM_SELECT_WRITE = 2 ;
714743 static private function stream_select ( $ requests , $ mode ) {
715744 if (empty ($ requests )) {
716745 return [];
0 commit comments