Skip to content

Commit ba9a582

Browse files
Merge pull request #8347 from Sesquipedalian/3.0/webfetchapi_improvments
Fixes some bugs in WebFetchApi
2 parents 39eb864 + 6c7d464 commit ba9a582

File tree

3 files changed

+81
-20
lines changed

3 files changed

+81
-20
lines changed

Sources/WebFetch/APIs/CurlFetcher.php

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ class CurlFetcher extends WebFetchApi
122122
*/
123123
public $headers;
124124

125+
/**
126+
* @var bool
127+
*
128+
* Whether to keep the connection open after the initial request.
129+
*/
130+
public bool $keep_alive = false;
131+
125132
/*********************
126133
* Internal properties
127134
*********************/
@@ -133,14 +140,14 @@ class CurlFetcher extends WebFetchApi
133140
*/
134141
private $default_options = [
135142
// Get returned value as a string (don't output it).
136-
CURLOPT_RETURNTRANSFER => 1,
143+
CURLOPT_RETURNTRANSFER => true,
137144

138145
// We need the headers to do our own redirect.
139-
CURLOPT_HEADER => 1,
146+
CURLOPT_HEADER => true,
140147

141148
// Don't follow. We will do it ourselves so safe mode and open_basedir
142149
// will dig it.
143-
CURLOPT_FOLLOWLOCATION => 0,
150+
CURLOPT_FOLLOWLOCATION => false,
144151

145152
// Set a normal looking user agent.
146153
CURLOPT_USERAGENT => SMF_USER_AGENT,
@@ -151,20 +158,17 @@ class CurlFetcher extends WebFetchApi
151158
// A page should load in this amount of time.
152159
CURLOPT_TIMEOUT => 90,
153160

154-
// Stop after this many redirects.
155-
CURLOPT_MAXREDIRS => 5,
156-
157161
// Accept gzip and decode it.
158162
CURLOPT_ENCODING => 'gzip,deflate',
159163

160164
// Stop curl from verifying the peer's certificate.
161-
CURLOPT_SSL_VERIFYPEER => 0,
165+
CURLOPT_SSL_VERIFYPEER => false,
162166

163167
// Stop curl from verifying the peer's host.
164168
CURLOPT_SSL_VERIFYHOST => 0,
165169

166170
// No post data. This will change if some is passed to request().
167-
CURLOPT_POST => 0,
171+
CURLOPT_POST => false,
168172
];
169173

170174
/****************
@@ -184,6 +188,25 @@ public function __construct(array $options = [], int $max_redirect = 3)
184188
// Initialize class variables
185189
$this->max_redirect = intval($max_redirect);
186190
$this->user_options = $options;
191+
192+
// This class handles redirections itself.
193+
if (!empty($this->user_options[CURLOPT_MAXREDIRS])) {
194+
$this->max_redirect = $this->user_options[CURLOPT_MAXREDIRS];
195+
unset($this->user_options[CURLOPT_MAXREDIRS]);
196+
}
197+
198+
if (!empty($this->user_options[CURLOPT_FOLLOWLOCATION])) {
199+
$this->max_redirect = max(3, $this->max_redirect);
200+
$this->user_options[CURLOPT_FOLLOWLOCATION] = false;
201+
}
202+
203+
// Do we want to keep the connection open after the initial request?
204+
if (
205+
version_compare(curl_version()['version'], '7.25.0', '>=')
206+
&& !empty($this->user_options[CURLOPT_TCP_KEEPALIVE])
207+
) {
208+
$this->keep_alive = true;
209+
}
187210
}
188211

189212
/**
@@ -235,6 +258,12 @@ public function request(string|Url $url, array|string $post_data = []): object
235258
}
236259

237260
// Set the options and get it.
261+
if (version_compare(curl_version()['version'], '7.25.0', '>=')) {
262+
$this->user_options[CURLOPT_TCP_KEEPALIVE] = (int) $this->keep_alive;
263+
} else {
264+
$this->keep_alive = false;
265+
}
266+
238267
$this->setOptions();
239268
$this->sendRequest(str_replace(' ', '%20', strval($url)));
240269

@@ -393,7 +422,7 @@ private function setOptions(): void
393422

394423
// POST data options, here we don't allow any override.
395424
if (isset($this->post_data)) {
396-
$this->options[CURLOPT_POST] = 1;
425+
$this->options[CURLOPT_POST] = true;
397426
$this->options[CURLOPT_POSTFIELDS] = $this->post_data;
398427
}
399428
}

Sources/WebFetch/APIs/SocketFetcher.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,16 @@ class SocketFetcher extends WebFetchApi
8484
*/
8585
public $response = [];
8686

87-
/*********************
88-
* Internal properties
89-
*********************/
90-
9187
/**
9288
* @var bool
9389
*
9490
* Whether to keep the socket connection open after the initial request.
9591
*/
96-
private bool $keep_alive;
92+
public bool $keep_alive = false;
93+
94+
/*********************
95+
* Internal properties
96+
*********************/
9797

9898
/**
9999
* @var bool

Sources/WebFetch/WebFetchApi.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ abstract class WebFetchApi implements WebFetchApiInterface
6161
'https' => ['SocketFetcher', 'CurlFetcher'],
6262
];
6363

64+
/****************************
65+
* Internal static properties
66+
****************************/
67+
68+
/**
69+
* @var array
70+
*
71+
* Fetchers that still have an open connection after the initial request.
72+
*/
73+
private static array $still_alive = [];
74+
6475
/****************
6576
* Public methods
6677
****************/
@@ -121,14 +132,35 @@ public static function fetch(Url|string $url, string|array $post_data = [], bool
121132
$data = false;
122133
}
123134

124-
foreach (self::$scheme_handlers[$url->scheme] as $class) {
125-
$class = __NAMESPACE__ . '\\APIs\\' . $class;
135+
if (isset(self::$still_alive[(string) $url])) {
136+
$fetcher = self::$still_alive[(string) $url];
137+
$fetcher->request($url, $post_data);
138+
} else {
139+
foreach (self::$scheme_handlers[$url->scheme] as $class) {
140+
// Get an instance of the desired class.
141+
$class = __NAMESPACE__ . '\\APIs\\' . $class;
126142

127-
$fetcher = new $class();
128-
$fetcher->request($url);
143+
$fetcher = new $class();
129144

130-
if ($fetcher->result('success')) {
131-
break;
145+
// Do we want to keep this connection alive, and can we do so?
146+
if ($keep_alive && property_exists($fetcher, 'keep_alive')) {
147+
$fetcher->keep_alive = $keep_alive;
148+
self::$still_alive[(string) $url] = $fetcher;
149+
}
150+
151+
// Make the request.
152+
$fetcher->request($url, $post_data);
153+
154+
// If keep_alive was turned off during the request, we don't
155+
// need to maintain this instance after we're done the request.
156+
if (!($fetcher->keep_alive ?? false)) {
157+
unset(self::$still_alive[(string) $url]);
158+
}
159+
160+
// If the request worked, we can stop looping.
161+
if ($fetcher->result('success')) {
162+
break;
163+
}
132164
}
133165
}
134166

0 commit comments

Comments
 (0)