Skip to content
This repository was archived by the owner on May 30, 2025. It is now read-only.

Commit 49167bf

Browse files
committed
Add RedirectingClient to handle http redirections as a middelware
1 parent 09a3c4d commit 49167bf

File tree

6 files changed

+1214
-198
lines changed

6 files changed

+1214
-198
lines changed

components/HttpClient/CacheClient.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function get_request(): ?Request {
9999

100100
public function get_response(): ?Response {
101101
return $this->response;
102-
}
102+
}
103103

104104
public function get_response_body_chunk(): ?string {
105105
return $this->cache_key;

components/HttpClient/Client.php

Lines changed: 26 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
* @package WordPress
5858
* @subpackage Async_HTTP
5959
*/
60-
class Client {
60+
class Client implements ClientInterface {
6161

6262
/**
6363
* The maximum number of concurrent connections allowed.
@@ -71,16 +71,6 @@ class Client {
7171
*/
7272
protected $concurrency;
7373

74-
/**
75-
* The maximum number of redirects to follow for a single request.
76-
*
77-
* This prevents infinite redirect loops and provides a degree of control over the client's behavior.
78-
* Setting it too high might lead to unexpected navigation paths.
79-
*
80-
* @var int
81-
*/
82-
protected $max_redirects = 3;
83-
8474
/**
8575
* All the HTTP requests ever enqueued with this Client.
8676
*
@@ -112,34 +102,35 @@ class Client {
112102
protected $requests_started_at = array();
113103

114104
public function __construct( $options = array() ) {
115-
$this->concurrency = $options['concurrency'] ?? 10;
116-
$this->max_redirects = $options['max_redirects'] ?? 3;
105+
$this->concurrency = $options['concurrency'] ?? 10;
117106
$this->request_timeout_ms = $options['timeout_ms'] ?? 30000;
118-
$this->requests = array();
107+
$this->requests = array();
119108
}
120109

121110
/**
122111
* Returns a RemoteFileReader that streams the response body of the
123112
* given request.
124113
*
125114
* @param Request $request The request to stream.
115+
* @param array $options Options for the request.
126116
*
127117
* @return RequestReadStream
128118
*/
129-
public function fetch( $request, $options = array() ) {
119+
public function fetch( Request $request, array $options = [] ) {
130120
return new RequestReadStream( $request,
131-
array_merge( [ 'client' => $this ], is_array( $options ) ? $options : iterator_to_array( $options ) ) );
121+
array_merge( [ 'client' => $this ], $options ) );
132122
}
133123

134124
/**
135125
* Returns an array of RemoteFileReader instances that stream the response bodies
136126
* of the given requests.
137127
*
138128
* @param Request[] $requests The requests to stream.
129+
* @param array $options Options for the requests.
139130
*
140131
* @return RequestReadStream[]
141132
*/
142-
public function fetch_many( array $requests, $options = array() ) {
133+
public function fetch_many( array $requests, array $options = [] ) {
143134
$streams = array();
144135

145136
foreach ( $requests as $request ) {
@@ -155,11 +146,11 @@ public function fetch_many( array $requests, $options = array() ) {
155146
* an internal queue. Network transmission is delayed until one of the returned
156147
* streams is read from.
157148
*
158-
* @param Request|Request[] $requests The HTTP request(s) to enqueue. Can be a single request or an array of requests.
149+
* @param Request[] $requests The HTTP request(s) to enqueue.
159150
*/
160151
public function enqueue( $requests ) {
161-
if ( ! is_array( $requests ) ) {
162-
$requests = array( $requests );
152+
if(!is_array($requests)) {
153+
$requests = [$requests];
163154
}
164155

165156
foreach ( $requests as $request ) {
@@ -199,7 +190,6 @@ public function enqueue( $requests ) {
199190
*
200191
* * `Client::EVENT_GOT_HEADERS`
201192
* * `Client::EVENT_BODY_CHUNK_AVAILABLE`
202-
* * `Client::EVENT_REDIRECT`
203193
* * `Client::EVENT_FAILED`
204194
* * `Client::EVENT_FINISHED`
205195
*
@@ -227,7 +217,7 @@ public function enqueue( $requests ) {
227217
* $request = new Request( "https://w.org" );
228218
*
229219
* $client = new HttpClientClient();
230-
* $client->enqueue( $request );
220+
* $client->enqueue( [$request] );
231221
* $event = $client->await_next_event( [
232222
* 'request_id' => $request->id,
233223
* ] );
@@ -239,15 +229,14 @@ public function enqueue( $requests ) {
239229
* request #1 has finished before you started awaiting
240230
* events for request #2.
241231
*
242-
* @param $query
232+
* @param array $query Query parameters for filtering events.
243233
*
244234
* @return bool
245235
*/
246-
public function await_next_event( $query = array() ) {
236+
public function await_next_event( array $query = [] ) {
247237
$ordered_events = array(
248238
self::EVENT_GOT_HEADERS,
249239
self::EVENT_BODY_CHUNK_AVAILABLE,
250-
self::EVENT_REDIRECT,
251240
self::EVENT_FAILED,
252241
self::EVENT_FINISHED,
253242
);
@@ -269,10 +258,6 @@ public function await_next_event( $query = array() ) {
269258
$events = array();
270259
foreach ( $query['requests'] as $query_request ) {
271260
$events[] = $query_request->id;
272-
while ( $query_request->redirected_to ) {
273-
$query_request = $query_request->redirected_to;
274-
$events[] = $query_request->id;
275-
}
276261
}
277262
}
278263

@@ -308,7 +293,7 @@ public function await_next_event( $query = array() ) {
308293
return false;
309294
}
310295

311-
public function has_pending_event( $request, $event_type ) {
296+
public function has_pending_event( Request $request, string $event_type ): bool {
312297
return $this->events[ $request->id ][ $event_type ] ?? false;
313298
}
314299

@@ -403,7 +388,11 @@ protected function event_loop_tick() {
403388
$this->get_active_requests( Request::STATE_RECEIVING_HEADERS )
404389
);
405390

406-
$this->handle_redirects(
391+
$this->receive_response_body(
392+
$this->get_active_requests( Request::STATE_RECEIVING_BODY )
393+
);
394+
395+
$this->mark_requests_finished(
407396
$this->get_active_requests( Request::STATE_RECEIVED )
408397
);
409398

@@ -427,11 +416,6 @@ protected function event_loop_tick() {
427416
return true;
428417
}
429418

430-
$this->receive_response_body(
431-
$this->get_active_requests( Request::STATE_RECEIVING_BODY )
432-
);
433-
434-
435419
return true;
436420
}
437421

@@ -779,8 +763,6 @@ protected function receive_response_headers( $requests ) {
779763
}
780764

781765
/**
782-
* Reads the next received portion of HTTP response headers for multiple requests.
783-
*
784766
* @param array $requests An array of requests.
785767
*/
786768
protected function receive_response_body( $requests ) {
@@ -807,60 +789,6 @@ protected function receive_response_body( $requests ) {
807789
}
808790
}
809791

810-
/**
811-
* @param array $requests An array of requests.
812-
*/
813-
protected function handle_redirects( $requests ) {
814-
foreach ( $requests as $request ) {
815-
$response = $request->response;
816-
if ( ! $response ) {
817-
continue;
818-
}
819-
$code = $response->status_code;
820-
$this->mark_finished( $request );
821-
if ( ! ( $code >= 300 && $code < 400 ) ) {
822-
continue;
823-
}
824-
825-
$location = $response->get_header( 'location' );
826-
if ( null === $location ) {
827-
continue;
828-
}
829-
830-
$redirects_so_far = 0;
831-
$cause = $request;
832-
while ( $cause->redirected_from ) {
833-
++ $redirects_so_far;
834-
$cause = $cause->redirected_from;
835-
}
836-
837-
if ( $redirects_so_far >= $this->max_redirects ) {
838-
$this->set_error( $request, new HttpError( 'Too many redirects' ) );
839-
continue;
840-
}
841-
842-
$redirect_url = $location;
843-
$parsed = WPURL::parse($redirect_url, $request->url);
844-
if(false === $parsed) {
845-
$this->set_error( $request, new HttpError( sprintf( 'Invalid redirect URL: %s', $redirect_url ) ) );
846-
continue;
847-
}
848-
$redirect_url = $parsed->toString();
849-
850-
$this->events[ $request->id ][ self::EVENT_REDIRECT ] = true;
851-
$this->enqueue(
852-
new Request(
853-
$redirect_url,
854-
array(
855-
// Redirects are always GET requests
856-
'method' => 'GET',
857-
'redirected_from' => $request,
858-
)
859-
)
860-
);
861-
}
862-
}
863-
864792
/**
865793
* Parses an HTTP headers string into an array containing the status and headers.
866794
*
@@ -1082,7 +1010,6 @@ protected function stream_select( $requests, $mode ) {
10821010

10831011
const EVENT_GOT_HEADERS = 'EVENT_GOT_HEADERS';
10841012
const EVENT_BODY_CHUNK_AVAILABLE = 'EVENT_BODY_CHUNK_AVAILABLE';
1085-
const EVENT_REDIRECT = 'EVENT_REDIRECT';
10861013
const EVENT_FAILED = 'EVENT_FAILED';
10871014
const EVENT_FINISHED = 'EVENT_FINISHED';
10881015

@@ -1097,4 +1024,10 @@ protected function stream_select( $requests, $mode ) {
10971024
* 5/100th of a second
10981025
*/
10991026
const NONBLOCKING_TIMEOUT_MICROSECONDS = 0.05 * self::MICROSECONDS_TO_SECONDS;
1027+
1028+
protected function mark_requests_finished( $requests ) {
1029+
foreach ( $requests as $request ) {
1030+
$this->mark_finished( $request );
1031+
}
1032+
}
11001033
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace WordPress\HttpClient;
4+
5+
interface ClientInterface {
6+
public function fetch( Request $request, array $options = [] );
7+
public function fetch_many( array $requests, array $options = [] );
8+
public function enqueue( array $requests );
9+
public function await_next_event( array $query = [] );
10+
public function has_pending_event( Request $request, string $event_type );
11+
public function get_event();
12+
public function get_request();
13+
public function get_response();
14+
public function get_response_body_chunk();
15+
}

0 commit comments

Comments
 (0)