summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2023-12-24 16:14:45 +0000
committerAndrew Dolgov <[email protected]>2023-12-24 16:14:45 +0000
commit51cd02fc3e476f9e64311a87e0b84dbd16f5a44f (patch)
tree1a6be722f06c261aa7e28dd2a4a9665bf5cb949e
parentd4ae6c67db8c966ab4998fda6df14072b103106b (diff)
parent0ea9db317038f5510a1ca875b55af770997ec148 (diff)
Merge branch 'feature/use-guzzle' into 'master'
Use Guzzle See merge request tt-rss/tt-rss!16
-rw-r--r--classes/UrlHelper.php496
-rw-r--r--composer.json3
-rw-r--r--composer.lock20
-rw-r--r--js/CommonDialogs.js2
-rw-r--r--tests/UrlHelperTest.php74
-rw-r--r--vendor/composer/installed.json18
-rw-r--r--vendor/composer/installed.php14
-rw-r--r--vendor/guzzlehttp/guzzle/CHANGELOG.md13
-rw-r--r--vendor/guzzlehttp/guzzle/README.md4
-rw-r--r--vendor/guzzlehttp/guzzle/UPGRADING.md14
-rw-r--r--vendor/guzzlehttp/guzzle/composer.json4
-rw-r--r--vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php2
-rw-r--r--vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php4
-rw-r--r--vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php2
-rw-r--r--vendor/guzzlehttp/guzzle/src/RequestOptions.php4
-rw-r--r--vendor/guzzlehttp/guzzle/src/Utils.php15
16 files changed, 295 insertions, 394 deletions
diff --git a/classes/UrlHelper.php b/classes/UrlHelper.php
index dbbde55e6..03202cff8 100644
--- a/classes/UrlHelper.php
+++ b/classes/UrlHelper.php
@@ -17,7 +17,19 @@ class UrlHelper {
static string $fetch_last_modified;
static string $fetch_effective_url;
static string $fetch_effective_ip_addr;
- static bool $fetch_curl_used;
+
+ public static ?GuzzleHttp\ClientInterface $client = null;
+
+ private static function get_client(): GuzzleHttp\ClientInterface {
+ if (self::$client == null) {
+ self::$client = new GuzzleHttp\Client([
+ GuzzleHttp\RequestOptions::COOKIES => false,
+ GuzzleHttp\RequestOptions::PROXY => Config::get(Config::HTTP_PROXY) ?: null,
+ ]);
+ }
+
+ return self::$client;
+ }
/**
* @param array<string, string|int> $parts
@@ -184,57 +196,32 @@ class UrlHelper {
/**
* @return false|string
*/
- static function resolve_redirects(string $url, int $timeout, int $nest = 0) {
+ static function resolve_redirects(string $url, int $timeout) {
$span = Tracer::start(__METHOD__);
$span->setAttribute('func.args', json_encode(func_get_args()));
-
- // too many redirects
- if ($nest > 10) {
- $span->setAttribute('error', 'too many redirects');
+ $client = self::get_client();
+
+ try {
+ $response = $client->request('HEAD', $url, [
+ GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => $timeout ?: Config::get(Config::FILE_FETCH_CONNECT_TIMEOUT),
+ GuzzleHttp\RequestOptions::TIMEOUT => $timeout ?: Config::get(Config::FILE_FETCH_TIMEOUT),
+ GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => ['max' => 10, 'track_redirects' => true],
+ GuzzleHttp\RequestOptions::HTTP_ERRORS => false,
+ GuzzleHttp\RequestOptions::HEADERS => [
+ 'User-Agent' => Config::get_user_agent(),
+ 'Connection' => 'close',
+ ],
+ ]);
+ } catch (Exception $ex) {
+ $span->setAttribute('error', (string) $ex);
$span->end();
return false;
}
- $context_options = array(
- 'http' => array(
- 'header' => array(
- 'Connection: close'
- ),
- 'method' => 'HEAD',
- 'timeout' => $timeout,
- 'protocol_version'=> 1.1)
- );
-
- if (Config::get(Config::HTTP_PROXY)) {
- $context_options['http']['request_fulluri'] = true;
- $context_options['http']['proxy'] = Config::get(Config::HTTP_PROXY);
- }
-
- $context = stream_context_create($context_options);
-
- // PHP 8 changed the second param from int to bool, but we still support PHP >= 7.4.0
- // @phpstan-ignore-next-line
- $headers = get_headers($url, 0, $context);
-
- if (is_array($headers)) {
- $headers = array_reverse($headers); // last one is the correct one
-
- foreach($headers as $header) {
- if (stripos($header, 'Location:') === 0) {
- $url = self::rewrite_relative($url, trim(substr($header, strlen('Location:'))));
-
- return self::resolve_redirects($url, $timeout, $nest + 1);
- }
- }
-
- $span->end();
- return $url;
- }
-
- $span->setAttribute('error', 'request failed');
+ // If a history header value doesn't exist there was no redirection and the original URL is fine.
+ $history_header = $response->getHeader(GuzzleHttp\RedirectMiddleware::HISTORY_HEADER);
$span->end();
- // request failed?
- return false;
+ return ($history_header ? end($history_header) : $url);
}
/**
@@ -244,13 +231,15 @@ class UrlHelper {
// TODO: max_size currently only works for CURL transfers
// TODO: multiple-argument way is deprecated, first parameter is a hash now
public static function fetch($options /* previously: 0: $url , 1: $type = false, 2: $login = false, 3: $pass = false,
- 4: $post_query = false, 5: $timeout = false, 6: $timestamp = 0, 7: $useragent = false*/) {
+ 4: $post_query = false, 5: $timeout = false, 6: $timestamp = 0, 7: $useragent = false, 8: $encoding = false,
+ 9: $auth_type = "basic" */) {
+ $span = Tracer::start(__METHOD__);
+ $span->setAttribute('func.args', json_encode(func_get_args()));
self::$fetch_last_error = "";
self::$fetch_last_error_code = -1;
self::$fetch_last_error_content = "";
self::$fetch_last_content_type = "";
- self::$fetch_curl_used = false;
self::$fetch_last_modified = "";
self::$fetch_effective_url = "";
self::$fetch_effective_ip_addr = "";
@@ -258,7 +247,7 @@ class UrlHelper {
if (!is_array($options)) {
// falling back on compatibility shim
- $option_names = [ "url", "type", "login", "pass", "post_query", "timeout", "last_modified", "useragent" ];
+ $option_names = [ "url", "type", "login", "pass", "post_query", "timeout", "last_modified", "useragent", "encoding", "auth_type" ];
$tmp = [];
for ($i = 0; $i < func_num_args(); $i++) {
@@ -275,17 +264,17 @@ class UrlHelper {
"post_query" => @func_get_arg(4),
"timeout" => @func_get_arg(5),
"timestamp" => @func_get_arg(6),
- "useragent" => @func_get_arg(7)
+ "useragent" => @func_get_arg(7),
+ "encoding" => @func_get_arg(8),
+ "auth_type" => @func_get_arg(9),
); */
}
- $url = $options["url"];
-
- $span = Tracer::start(__METHOD__);
- $span->setAttribute('func.args', json_encode(func_get_args()));
+ $url = $options["url"];
$type = isset($options["type"]) ? $options["type"] : false;
$login = isset($options["login"]) ? $options["login"] : false;
$pass = isset($options["pass"]) ? $options["pass"] : false;
+ $auth_type = isset($options["auth_type"]) ? $options["auth_type"] : "basic";
$post_query = isset($options["post_query"]) ? $options["post_query"] : false;
$timeout = isset($options["timeout"]) ? $options["timeout"] : false;
$last_modified = isset($options["last_modified"]) ? $options["last_modified"] : "";
@@ -294,6 +283,7 @@ class UrlHelper {
$max_size = isset($options["max_size"]) ? $options["max_size"] : Config::get(Config::MAX_DOWNLOAD_FILE_SIZE); // in bytes
$http_accept = isset($options["http_accept"]) ? $options["http_accept"] : false;
$http_referrer = isset($options["http_referrer"]) ? $options["http_referrer"] : false;
+ $encoding = isset($options["encoding"]) ? $options["encoding"] : false;
$url = ltrim($url, ' ');
$url = str_replace(' ', '%20', $url);
@@ -303,8 +293,7 @@ class UrlHelper {
$url = self::validate($url, true);
if (!$url) {
- self::$fetch_last_error = "Requested URL failed extended validation.";
-
+ self::$fetch_last_error = 'Requested URL failed extended validation.';
$span->setAttribute('error', self::$fetch_last_error);
$span->end();
return false;
@@ -313,319 +302,158 @@ class UrlHelper {
$url_host = parse_url($url, PHP_URL_HOST);
$ip_addr = gethostbyname($url_host);
- if (!$ip_addr || strpos($ip_addr, "127.") === 0) {
+ if (!$ip_addr || strpos($ip_addr, '127.') === 0) {
self::$fetch_last_error = "URL hostname failed to resolve or resolved to a loopback address ($ip_addr)";
-
$span->setAttribute('error', self::$fetch_last_error);
$span->end();
return false;
}
- if (function_exists('curl_init') && !ini_get("open_basedir")) {
-
- self::$fetch_curl_used = true;
-
- $ch = curl_init($url);
-
- if (!$ch) {
- self::$fetch_last_error = "curl_init() failed";
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- $curl_http_headers = [];
-
- if ($last_modified && !$post_query)
- array_push($curl_http_headers, "If-Modified-Since: $last_modified");
-
- if ($http_accept)
- array_push($curl_http_headers, "Accept: " . $http_accept);
-
- if (count($curl_http_headers) > 0)
- curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_http_headers);
-
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : Config::get(Config::FILE_FETCH_CONNECT_TIMEOUT));
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : Config::get(Config::FILE_FETCH_TIMEOUT));
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $followlocation);
- curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
- curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HEADER, true);
- curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
- curl_setopt($ch, CURLOPT_USERAGENT, $useragent ? $useragent : Config::get_user_agent());
- curl_setopt($ch, CURLOPT_ENCODING, "");
- curl_setopt($ch, CURLOPT_COOKIEJAR, "/dev/null");
-
- if ($http_referrer)
- curl_setopt($ch, CURLOPT_REFERER, $http_referrer);
-
- if ($max_size) {
- curl_setopt($ch, CURLOPT_NOPROGRESS, false);
- curl_setopt($ch, CURLOPT_BUFFERSIZE, 16384); // needed to get 5 arguments in progress function?
-
- // holy shit closures in php
- // download & upload are *expected* sizes respectively, could be zero
- curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function($curl_handle, $download_size, $downloaded, $upload_size, $uploaded) use(&$max_size, $url) {
- //Debug::log("[curl progressfunction] $downloaded $max_size", Debug::$LOG_EXTENDED);
-
- if ($downloaded > $max_size) {
- Debug::log("[UrlHelper] fetch error: curl reached max size of $max_size bytes downloading $url, aborting.", Debug::LOG_VERBOSE);
- return 1;
- }
+ $req_options = [
+ GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => $timeout ?: Config::get(Config::FILE_FETCH_CONNECT_TIMEOUT),
+ GuzzleHttp\RequestOptions::TIMEOUT => $timeout ?: Config::get(Config::FILE_FETCH_TIMEOUT),
+ GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => $followlocation ? ['max' => 20, 'track_redirects' => true] : false,
+ GuzzleHttp\RequestOptions::HEADERS => [
+ 'User-Agent' => $useragent ?: Config::get_user_agent(),
+ ],
+ 'curl' => [],
+ ];
- return 0;
- });
+ if ($last_modified && !$post_query)
+ $req_options[GuzzleHttp\RequestOptions::HEADERS]['If-Modified-Since'] = $last_modified;
- }
+ if ($http_accept)
+ $req_options[GuzzleHttp\RequestOptions::HEADERS]['Accept'] = $http_accept;
- if (Config::get(Config::HTTP_PROXY)) {
- curl_setopt($ch, CURLOPT_PROXY, Config::get(Config::HTTP_PROXY));
- }
+ if ($encoding)
+ $req_options[GuzzleHttp\RequestOptions::HEADERS]['Accept-Encoding'] = $encoding;
- if ($post_query) {
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
- }
+ if ($http_referrer)
+ $req_options[GuzzleHttp\RequestOptions::HEADERS]['Referer'] = $http_referrer;
+ if ($login && $pass && in_array($auth_type, ['basic', 'digest', 'ntlm'])) {
+ // Let Guzzle handle the details for auth types it supports
+ $req_options[GuzzleHttp\RequestOptions::AUTH] = [$login, $pass, $auth_type];
+ } elseif ($auth_type === 'any') {
+ // https://docs.guzzlephp.org/en/stable/faq.html#how-can-i-add-custom-curl-options
+ $req_options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_ANY;
if ($login && $pass)
- curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
-
- $ret = @curl_exec($ch);
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-
- // CURLAUTH_BASIC didn't work, let's retry with CURLAUTH_ANY in case it's actually something
- // unusual like NTLM...
- if ($http_code == 403 && $login && $pass) {
- curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
-
- $ret = @curl_exec($ch);
- }
-
- if (curl_errno($ch) === 23 || curl_errno($ch) === 61) {
- curl_setopt($ch, CURLOPT_ENCODING, 'none');
- $ret = @curl_exec($ch);
- }
-
- $headers_length = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
- $headers = explode("\r\n", substr($ret, 0, $headers_length));
- $contents = substr($ret, $headers_length);
+ $req_options['curl'][\CURLOPT_USERPWD] = "$login:$pass";
+ }
- foreach ($headers as $header) {
- if (strstr($header, ": ") !== false) {
- list ($key, $value) = explode(": ", $header);
+ if ($post_query)
+ $req_options[GuzzleHttp\RequestOptions::FORM_PARAMS] = $post_query;
- if (strtolower($key) == "last-modified") {
- self::$fetch_last_modified = $value;
- }
- }
+ if ($max_size) {
+ $req_options[GuzzleHttp\RequestOptions::PROGRESS] = function($download_size, $downloaded, $upload_size, $uploaded) use(&$max_size, $url) {
+ //Debug::log("[curl progressfunction] $downloaded $max_size", Debug::$LOG_EXTENDED);
- if (substr(strtolower($header), 0, 7) == 'http/1.') {
- self::$fetch_last_error_code = (int) substr($header, 9, 3);
- self::$fetch_last_error = $header;
+ if ($downloaded > $max_size) {
+ Debug::log("[UrlHelper] fetch error: max size of $max_size bytes exceeded when downloading $url . Aborting.", Debug::LOG_VERBOSE);
+ throw new \LengthException("Download exceeded size limit");
}
- }
-
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- self::$fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
-
- self::$fetch_effective_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
-
- if (!self::validate(self::$fetch_effective_url, true)) {
- self::$fetch_last_error = "URL received after redirection failed extended validation.";
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- self::$fetch_effective_ip_addr = gethostbyname(parse_url(self::$fetch_effective_url, PHP_URL_HOST));
-
- if (!self::$fetch_effective_ip_addr || strpos(self::$fetch_effective_ip_addr, "127.") === 0) {
- self::$fetch_last_error = "URL hostname received after redirection failed to resolve or resolved to a loopback address (".self::$fetch_effective_ip_addr.")";
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- self::$fetch_last_error_code = $http_code;
-
- if ($http_code != 200 || $type && strpos(self::$fetch_last_content_type, "$type") === false) {
-
- if (curl_errno($ch) != 0) {
- self::$fetch_last_error .= "; " . curl_errno($ch) . " " . curl_error($ch);
- } else {
- self::$fetch_last_error = "HTTP Code: $http_code ";
+ };
+
+ # Alternative/supplement to `progress` checking
+ $req_options[GuzzleHttp\RequestOptions::ON_HEADERS] = function(Psr\Http\Message\ResponseInterface $response) use(&$max_size, $url) {
+ $content_length = $response->getHeaderLine('Content-Length');
+ if ($content_length > $max_size) {
+ Debug::log("[UrlHelper] fetch error: server indicated (via 'Content-Length: {$content_length}') max size of $max_size bytes " .
+ "would be exceeded when downloading $url . Aborting.", Debug::LOG_VERBOSE);
+ throw new \LengthException("Server sent 'Content-Length' exceeding download limit");
}
+ };
+ }
- self::$fetch_last_error_content = $contents;
- curl_close($ch);
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- if (!$contents) {
- if (curl_errno($ch) === 0) {
- self::$fetch_last_error = 'Successful response, but no content was received.';
- } else {
- self::$fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
- }
- curl_close($ch);
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- curl_close($ch);
-
- $is_gzipped = RSSUtils::is_gzipped($contents);
-
- if ($is_gzipped && is_string($contents)) {
- $tmp = @gzdecode($contents);
-
- if ($tmp) $contents = $tmp;
- }
+ $client = self::get_client();
+ try {
+ $response = $client->request($post_query ? 'POST' : 'GET', $url, $req_options);
+ } catch (\LengthException $ex) {
+ // Either 'Content-Length' indicated the download limit would be exceeded, or the transfer actually exceeded the download limit.
+ self::$fetch_last_error = (string) $ex;
+ $span->setAttribute('error', self::$fetch_last_error);
$span->end();
-
- return $contents;
- } else {
-
- self::$fetch_curl_used = false;
-
- if ($login && $pass){
- $url_parts = array();
-
- preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
-
- $pass = urlencode($pass);
-
- if ($url_parts[1] && $url_parts[2]) {
- $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
- }
- }
-
- // TODO: should this support POST requests or not? idk
-
- $context_options = array(
- 'http' => array(
- 'header' => array(
- 'Connection: close'
- ),
- 'method' => 'GET',
- 'ignore_errors' => true,
- 'timeout' => $timeout ? $timeout : Config::get(Config::FILE_FETCH_TIMEOUT),
- 'protocol_version'=> 1.1)
- );
-
- if (!$post_query && $last_modified)
- array_push($context_options['http']['header'], "If-Modified-Since: $last_modified");
-
- if ($http_accept)
- array_push($context_options['http']['header'], "Accept: $http_accept");
-
- if ($http_referrer)
- array_push($context_options['http']['header'], "Referer: $http_referrer");
-
- if (Config::get(Config::HTTP_PROXY)) {
- $context_options['http']['request_fulluri'] = true;
- $context_options['http']['proxy'] = Config::get(Config::HTTP_PROXY);
- }
-
- $context = stream_context_create($context_options);
-
- $old_error = error_get_last();
-
- self::$fetch_effective_url = self::resolve_redirects($url, $timeout ? $timeout : Config::get(Config::FILE_FETCH_CONNECT_TIMEOUT));
-
- if (!self::validate(self::$fetch_effective_url, true)) {
- self::$fetch_last_error = "URL received after redirection failed extended validation.";
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- self::$fetch_effective_ip_addr = gethostbyname(parse_url(self::$fetch_effective_url, PHP_URL_HOST));
-
- if (!self::$fetch_effective_ip_addr || strpos(self::$fetch_effective_ip_addr, "127.") === 0) {
- self::$fetch_last_error = "URL hostname received after redirection failed to resolve or resolved to a loopback address (".self::$fetch_effective_ip_addr.")";
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- $data = @file_get_contents($url, false, $context);
-
- if ($data === false) {
- self::$fetch_last_error = "'file_get_contents' failed.";
-
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
-
- foreach ($http_response_header as $header) {
- if (strstr($header, ": ") !== false) {
- list ($key, $value) = explode(": ", $header);
-
- $key = strtolower($key);
-
- if ($key == 'content-type') {
- self::$fetch_last_content_type = $value;
- // don't abort here b/c there might be more than one
- // e.g. if we were being redirected -- last one is the right one
- } else if ($key == 'last-modified') {
- self::$fetch_last_modified = $value;
- } else if ($key == 'location') {
- self::$fetch_effective_url = $value;
+ return false;
+ } catch (GuzzleHttp\Exception\GuzzleException $ex) {
+ self::$fetch_last_error = (string) $ex;
+
+ if ($ex instanceof GuzzleHttp\Exception\RequestException) {
+ if ($ex instanceof GuzzleHttp\Exception\BadResponseException) {
+ // 4xx or 5xx
+ self::$fetch_last_error_code = $ex->getResponse()->getStatusCode();
+
+ // If credentials were provided and we got a 403 back, retry once with auth type 'any'
+ // to attempt compatibility with unusual configurations.
+ if ($login && $pass && self::$fetch_last_error_code === 403 && $auth_type !== 'any') {
+ $options['auth_type'] = 'any';
+ $span->end();
+ return self::fetch($options);
}
- }
- if (substr(strtolower($header), 0, 7) == 'http/1.') {
- self::$fetch_last_error_code = (int) substr($header, 9, 3);
- self::$fetch_last_error = $header;
+ self::$fetch_last_content_type = $ex->getResponse()->getHeaderLine('content-type');
+
+ if ($type && strpos(self::$fetch_last_content_type, "$type") === false)
+ self::$fetch_last_error_content = (string) $ex->getResponse()->getBody();
+ } elseif (array_key_exists('errno', $ex->getHandlerContext())) {
+ $errno = (int) $ex->getHandlerContext()['errno'];
+
+ // By default, all supported encoding types are sent via `Accept-Encoding` and decoding of
+ // responses with `Content-Encoding` is automatically attempted. If this fails, we do a
+ // single retry with `Accept-Encoding: none` to try and force an unencoded response.
+ if (($errno === \CURLE_WRITE_ERROR || $errno === \CURLE_BAD_CONTENT_ENCODING) &&
+ $ex->getRequest()->getHeaderLine('accept-encoding') !== 'none') {
+ $options['encoding'] = 'none';
+ $span->end();
+ return self::fetch($options);
+ }
}
}
- if (self::$fetch_last_error_code != 200) {
- $error = error_get_last();
+ $span->setAttribute('error', self::$fetch_last_error);
+ $span->end();
- if (($error['message'] ?? '') != ($old_error['message'] ?? '')) {
- self::$fetch_last_error .= "; " . $error["message"];
- }
+ return false;
+ }
- self::$fetch_last_error_content = $data;
+ // Keep setting expected 'fetch_last_error_code' and 'fetch_last_error' values
+ self::$fetch_last_error_code = $response->getStatusCode();
+ self::$fetch_last_error = "HTTP/{$response->getProtocolVersion()} {$response->getStatusCode()} {$response->getReasonPhrase()}";
+ self::$fetch_last_modified = $response->getHeaderLine('last-modified');
+ self::$fetch_last_content_type = $response->getHeaderLine('content-type');
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
+ // If a history header value doesn't exist there was no redirection and the original URL is fine.
+ $history_header = $response->getHeader(GuzzleHttp\RedirectMiddleware::HISTORY_HEADER);
+ self::$fetch_effective_url = $history_header ? end($history_header) : $url;
- if ($data) {
- $is_gzipped = RSSUtils::is_gzipped($data);
+ if (!self::validate(self::$fetch_effective_url, true)) {
+ self::$fetch_last_error = "URL received after redirection failed extended validation.";
+ $span->setAttribute('error', self::$fetch_last_error);
+ $span->end();
+ return false;
+ }
- if ($is_gzipped) {
- $tmp = @gzdecode($data);
+ self::$fetch_effective_ip_addr = gethostbyname(parse_url(self::$fetch_effective_url, PHP_URL_HOST));
- if ($tmp) $data = $tmp;
- }
+ if (!self::$fetch_effective_ip_addr || strpos(self::$fetch_effective_ip_addr, '127.') === 0) {
+ self::$fetch_last_error = 'URL hostname received after redirection failed to resolve or resolved to a loopback address (' .
+ self::$fetch_effective_ip_addr . ')';
+ $span->setAttribute('error', self::$fetch_last_error);
+ $span->end();
+ return false;
+ }
- $span->end();
- return $data;
- } else {
- self::$fetch_last_error = 'Successful response, but no content was received.';
+ $body = (string) $response->getBody();
- $span->setAttribute('error', self::$fetch_last_error);
- $span->end();
- return false;
- }
+ if (!$body) {
+ self::$fetch_last_error = 'Successful response, but no content was received.';
+ $span->setAttribute('error', self::$fetch_last_error);
+ $span->end();
+ return false;
}
+
+ $span->end();
+ return $body;
}
/**
diff --git a/composer.json b/composer.json
index 69b3a6dc1..0ce390fa8 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,8 @@
"j4mie/idiorm": "dev-master",
"open-telemetry/exporter-otlp": "^1.0",
"php-http/guzzle7-adapter": "^1.0",
- "soundasleep/html2text": "^2.1"
+ "soundasleep/html2text": "^2.1",
+ "guzzlehttp/guzzle": "^7.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.3",
diff --git a/composer.lock b/composer.lock
index 598462e90..4c45934d3 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d65a2e896d59d3d603fd6cda0db3b646",
+ "content-hash": "a0465ea624d9e79bc5d8b04a345b1ad6",
"packages": [
{
"name": "beberlei/assert",
@@ -261,16 +261,16 @@
},
{
"name": "guzzlehttp/guzzle",
- "version": "7.8.0",
+ "version": "7.8.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9"
+ "reference": "41042bc7ab002487b876a0683fc8dce04ddce104"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9",
- "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104",
+ "reference": "41042bc7ab002487b876a0683fc8dce04ddce104",
"shasum": ""
},
"require": {
@@ -285,11 +285,11 @@
"psr/http-client-implementation": "1.0"
},
"require-dev": {
- "bamarni/composer-bin-plugin": "^1.8.1",
+ "bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
- "phpunit/phpunit": "^8.5.29 || ^9.5.23",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
@@ -367,7 +367,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
- "source": "https://github.com/guzzle/guzzle/tree/7.8.0"
+ "source": "https://github.com/guzzle/guzzle/tree/7.8.1"
},
"funding": [
{
@@ -383,7 +383,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-27T10:20:53+00:00"
+ "time": "2023-12-03T20:35:24+00:00"
},
{
"name": "guzzlehttp/promises",
@@ -4405,5 +4405,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
- "plugin-api-version": "2.3.0"
+ "plugin-api-version": "2.6.0"
}
diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js
index 8d4241ef8..989a61539 100644
--- a/js/CommonDialogs.js
+++ b/js/CommonDialogs.js
@@ -268,7 +268,7 @@ const CommonDialogs = {
<table width='100%' id='error-feeds-list'>
${reply.map((row) => `
- <tr data-row-id='${row.id}'>
+ <tr data-row-id='${row.id}' style='vertical-align: top'>
<td class='checkbox'>
<input onclick='Tables.onRowChecked(this)' dojoType="dijit.form.CheckBox"
type="checkbox">
diff --git a/tests/UrlHelperTest.php b/tests/UrlHelperTest.php
index fe4eb5db2..58960add0 100644
--- a/tests/UrlHelperTest.php
+++ b/tests/UrlHelperTest.php
@@ -1,5 +1,11 @@
<?php
+use GuzzleHttp\Client;
+use GuzzleHttp\Handler\MockHandler;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\Exception\RequestException;
use PHPUnit\Framework\TestCase;
final class UrlHelperTest extends TestCase {
@@ -13,16 +19,22 @@ final class UrlHelperTest extends TestCase {
// magnet allowed because it's a href attribute
$this->assertEquals(
'magnet:?xt=urn:btih:...',
- UrlHelper::rewrite_relative('http://example.com/example/',
+ UrlHelper::rewrite_relative(
+ 'http://example.com/example/',
'magnet:?xt=urn:btih:...',
- "a", "href", "")
+ "a",
+ "href",
+ ""
+ )
);
// disallowed magnet
$this->assertEquals(
'http://example.com?xt=urn:btih:...',
- UrlHelper::rewrite_relative('http://example.com/example/',
- 'magnet:?xt=urn:btih:...')
+ UrlHelper::rewrite_relative(
+ 'http://example.com/example/',
+ 'magnet:?xt=urn:btih:...'
+ )
);
$this->assertEquals(
@@ -49,6 +61,60 @@ final class UrlHelperTest extends TestCase {
'http://www.example.com/test',
UrlHelper::rewrite_relative('http://www.example.com/test2 ', 'http://www.example.com/test')
);
+ }
+
+ public function test_fetch(): void {
+ $mock = new MockHandler();
+
+ UrlHelper::$client = new Client([
+ 'handler' => HandlerStack::create($mock),
+ ]);
+
+ $mock->append(new Response(200, [], 'Hello, World'));
+ $result = UrlHelper::fetch('https://www.example.com');
+ $this->assertEquals(200, UrlHelper::$fetch_last_error_code);
+ $this->assertEquals('Hello, World', $result);
+
+ foreach (['ftp://ftp.example.com', 'http://127.0.0.1', 'blah', '', 42, null] as $url) {
+ $result = UrlHelper::fetch($url);
+ $this->assertFalse($result);
+ }
+
+ $mock->append(new Response(200, ['Content-Length' => (string) PHP_INT_MAX]));
+ $result = UrlHelper::fetch('https://www.example.com/very-large-content-length');
+ $this->assertFalse($result);
+ $mock->append(new Response(301, ['Location' => 'https://www.example.com']));
+ $result = UrlHelper::fetch(['url' => 'https://example.com', 'followlocation' => false]);
+ $this->assertFalse($result);
+
+ $mock->append(
+ new Response(301, ['Location' => 'http://127.0.0.1']),
+ new Response(200, [], 'Hello, World'),
+ );
+ $result = UrlHelper::fetch(['url' => 'https://example.com', 'followlocation' => true]);
+ $this->assertFalse($result);
+ $this->assertEquals('URL received after redirection failed extended validation.', UrlHelper::$fetch_last_error);
+ $this->assertEquals('http://127.0.0.1', UrlHelper::$fetch_effective_url);
+
+ $mock->append(new Response(200, [], ''));
+ $result = UrlHelper::fetch('https://www.example.com');
+ $this->assertFalse($result);
+ $this->assertEquals('Successful response, but no content was received.', UrlHelper::$fetch_last_error);
+
+ // Fake a 403 for basic auth and success with `CURLAUTH_ANY` in the retry attempt
+ $mock->append(
+ new Response(403, []),
+ new Response(200, [], 'Hello, World'),
+ );
+ $result = UrlHelper::fetch([
+ 'url' => 'https://example.com/requires-credentials',
+ 'login' => 'some_username',
+ 'pass' => 'some_password',
+ 'auth_type' => 'basic',
+ ]);
+ $this->assertEquals(200, UrlHelper::$fetch_last_error_code);
+ $this->assertEquals('Hello, World', $result);
+ $this->assertEquals($mock->getLastOptions()['curl'][\CURLOPT_HTTPAUTH], \CURLAUTH_ANY);
}
}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index d09e9c70f..429e6e42f 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -340,17 +340,17 @@
},
{
"name": "guzzlehttp/guzzle",
- "version": "7.8.0",
- "version_normalized": "7.8.0.0",
+ "version": "7.8.1",
+ "version_normalized": "7.8.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9"
+ "reference": "41042bc7ab002487b876a0683fc8dce04ddce104"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9",
- "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104",
+ "reference": "41042bc7ab002487b876a0683fc8dce04ddce104",
"shasum": ""
},
"require": {
@@ -365,11 +365,11 @@
"psr/http-client-implementation": "1.0"
},
"require-dev": {
- "bamarni/composer-bin-plugin": "^1.8.1",
+ "bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
- "phpunit/phpunit": "^8.5.29 || ^9.5.23",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
@@ -377,7 +377,7 @@
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
- "time": "2023-08-27T10:20:53+00:00",
+ "time": "2023-12-03T20:35:24+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
@@ -449,7 +449,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
- "source": "https://github.com/guzzle/guzzle/tree/7.8.0"
+ "source": "https://github.com/guzzle/guzzle/tree/7.8.1"
},
"funding": [
{
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 64364b741..dd4263db6 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '2b8e34453234b8b31ebc9e7020f8677bf3889898',
+ 'reference' => 'd4ae6c67db8c966ab4998fda6df14072b103106b',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '2b8e34453234b8b31ebc9e7020f8677bf3889898',
+ 'reference' => 'd4ae6c67db8c966ab4998fda6df14072b103106b',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -65,9 +65,9 @@
'dev_requirement' => false,
),
'guzzlehttp/guzzle' => array(
- 'pretty_version' => '7.8.0',
- 'version' => '7.8.0.0',
- 'reference' => '1110f66a6530a40fe7aea0378fe608ee2b2248f9',
+ 'pretty_version' => '7.8.1',
+ 'version' => '7.8.1.0',
+ 'reference' => '41042bc7ab002487b876a0683fc8dce04ddce104',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
'aliases' => array(),
@@ -371,8 +371,8 @@
'psr/http-client-implementation' => array(
'dev_requirement' => false,
'provided' => array(
- 0 => '1.0',
- 1 => '*',
+ 0 => '*',
+ 1 => '1.0',
),
),
'psr/http-factory' => array(
diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md
index 990b86c9e..13709d1b8 100644
--- a/vendor/guzzlehttp/guzzle/CHANGELOG.md
+++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md
@@ -3,6 +3,14 @@
Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
+## 7.8.1 - 2023-12-03
+
+### Changed
+
+- Updated links in docs to their canonical versions
+- Replaced `call_user_func*` with native calls
+
+
## 7.8.0 - 2023-08-27
### Added
@@ -643,7 +651,8 @@ object).
* Note: This has been changed in 5.0.3 to now encode query string values by
default unless the `rawString` argument is provided when setting the query
string on a URL: Now allowing many more characters to be present in the
- query string without being percent encoded. See https://tools.ietf.org/html/rfc3986#appendix-A
+ query string without being percent encoded. See
+ https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
## 5.0.1 - 2014-10-16
@@ -1182,7 +1191,7 @@ interfaces.
## 3.4.0 - 2013-04-11
-* Bug fix: URLs are now resolved correctly based on https://tools.ietf.org/html/rfc3986#section-5.2. #289
+* Bug fix: URLs are now resolved correctly based on https://datatracker.ietf.org/doc/html/rfc3986#section-5.2. #289
* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
diff --git a/vendor/guzzlehttp/guzzle/README.md b/vendor/guzzlehttp/guzzle/README.md
index 0786462b3..6d78a9309 100644
--- a/vendor/guzzlehttp/guzzle/README.md
+++ b/vendor/guzzlehttp/guzzle/README.md
@@ -3,7 +3,7 @@
# Guzzle, PHP HTTP client
[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
-[![Build Status](https://img.shields.io/github/workflow/status/guzzle/guzzle/CI?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/guzzle/guzzle/ci.yml?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
@@ -66,7 +66,7 @@ composer require guzzlehttp/guzzle
| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 |
| 5.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 |
| 6.x | Security fixes only | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 |
-| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.3 |
+| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.4 |
[guzzle-3-repo]: https://github.com/guzzle/guzzle3
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
diff --git a/vendor/guzzlehttp/guzzle/UPGRADING.md b/vendor/guzzlehttp/guzzle/UPGRADING.md
index 8fa0afb5d..4efbb5962 100644
--- a/vendor/guzzlehttp/guzzle/UPGRADING.md
+++ b/vendor/guzzlehttp/guzzle/UPGRADING.md
@@ -189,11 +189,11 @@ $client = new GuzzleHttp\Client(['handler' => $handler]);
## POST Requests
-This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
+This version added the [`form_params`](https://docs.guzzlephp.org/en/latest/request-options.html#form_params)
and `multipart` request options. `form_params` is an associative array of
strings or array of strings and is used to serialize an
`application/x-www-form-urlencoded` POST request. The
-[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
+[`multipart`](https://docs.guzzlephp.org/en/latest/request-options.html#multipart)
option is now used to send a multipart/form-data POST request.
`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
@@ -209,7 +209,7 @@ The `base_url` option has been renamed to `base_uri`.
## Rewritten Adapter Layer
-Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
+Guzzle now uses [RingPHP](https://ringphp.readthedocs.org/en/latest) to send
HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
is still supported, but it has now been renamed to `handler`. Instead of
passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
@@ -575,7 +575,7 @@ You can intercept a request and inject a response using the `intercept()` event
of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
`GuzzleHttp\Event\ErrorEvent` event.
-See: http://docs.guzzlephp.org/en/latest/events.html
+See: https://docs.guzzlephp.org/en/latest/events.html
## Inflection
@@ -668,9 +668,9 @@ in separate repositories:
The service description layer of Guzzle has moved into two separate packages:
-- http://github.com/guzzle/command Provides a high level abstraction over web
+- https://github.com/guzzle/command Provides a high level abstraction over web
services by representing web service operations using commands.
-- http://github.com/guzzle/guzzle-services Provides an implementation of
+- https://github.com/guzzle/guzzle-services Provides an implementation of
guzzle/command that provides request serialization and response parsing using
Guzzle service descriptions.
@@ -870,7 +870,7 @@ HeaderInterface (e.g. toArray(), getAll(), etc.).
3.3 to 3.4
----------
-Base URLs of a client now follow the rules of https://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
+Base URLs of a client now follow the rules of https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2 when merging URLs.
3.2 to 3.3
----------
diff --git a/vendor/guzzlehttp/guzzle/composer.json b/vendor/guzzlehttp/guzzle/composer.json
index 72defd614..69583d7cc 100644
--- a/vendor/guzzlehttp/guzzle/composer.json
+++ b/vendor/guzzlehttp/guzzle/composer.json
@@ -63,10 +63,10 @@
},
"require-dev": {
"ext-curl": "*",
- "bamarni/composer-bin-plugin": "^1.8.1",
+ "bamarni/composer-bin-plugin": "^1.8.2",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
- "phpunit/phpunit": "^8.5.29 || ^9.5.23",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
index fa2b10a8c..c29b4b7e9 100644
--- a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
@@ -243,7 +243,7 @@ class CookieJar implements CookieJarInterface
/**
* Computes cookie path following RFC 6265 section 5.1.4
*
- * @see https://tools.ietf.org/html/rfc6265#section-5.1.4
+ * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
*/
private function getCookiePathFromRequest(RequestInterface $request): string
{
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
index d74915bed..c9806da88 100644
--- a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
@@ -420,7 +420,7 @@ class SetCookie
}
// Remove the leading '.' as per spec in RFC 6265.
- // https://tools.ietf.org/html/rfc6265#section-5.2.3
+ // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
$cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
$domain = \strtolower($domain);
@@ -431,7 +431,7 @@ class SetCookie
}
// Matching the subdomain according to RFC 6265.
- // https://tools.ietf.org/html/rfc6265#section-5.1.3
+ // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
return false;
}
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
index be88d9e49..16a942232 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
@@ -256,7 +256,7 @@ class CurlFactory implements CurlFactoryInterface
$method = $easy->request->getMethod();
if ($method === 'PUT' || $method === 'POST') {
- // See https://tools.ietf.org/html/rfc7230#section-3.3.2
+ // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
if (!$easy->request->hasHeader('Content-Length')) {
$conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
}
diff --git a/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/vendor/guzzlehttp/guzzle/src/RequestOptions.php
index bf3b02b6b..a38768c0c 100644
--- a/vendor/guzzlehttp/guzzle/src/RequestOptions.php
+++ b/vendor/guzzlehttp/guzzle/src/RequestOptions.php
@@ -5,9 +5,7 @@ namespace GuzzleHttp;
/**
* This class contains a list of built-in Guzzle request options.
*
- * More documentation for each option can be found at http://guzzlephp.org/.
- *
- * @see http://docs.guzzlephp.org/en/v6/request-options.html
+ * @see https://docs.guzzlephp.org/en/latest/request-options.html
*/
final class RequestOptions
{
diff --git a/vendor/guzzlehttp/guzzle/src/Utils.php b/vendor/guzzlehttp/guzzle/src/Utils.php
index fcf571d6b..93d6d39cd 100644
--- a/vendor/guzzlehttp/guzzle/src/Utils.php
+++ b/vendor/guzzlehttp/guzzle/src/Utils.php
@@ -176,14 +176,13 @@ No system CA bundle could be found in any of the the common system locations.
PHP versions earlier than 5.6 are not properly configured to use the system's
CA bundle by default. In order to verify peer certificates, you will need to
supply the path on disk to a certificate bundle to the 'verify' request
-option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
-need a specific certificate bundle, then Mozilla provides a commonly used CA
-bundle which can be downloaded here (provided by the maintainer of cURL):
-https://curl.haxx.se/ca/cacert.pem. Once
-you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
-ini setting to point to the path to the file, allowing you to omit the 'verify'
-request option. See https://curl.haxx.se/docs/sslcerts.html for more
-information.
+option: https://docs.guzzlephp.org/en/latest/request-options.html#verify. If
+you do not need a specific certificate bundle, then Mozilla provides a commonly
+used CA bundle which can be downloaded here (provided by the maintainer of
+cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available
+on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path
+to the file, allowing you to omit the 'verify' request option. See
+https://curl.haxx.se/docs/sslcerts.html for more information.
EOT
);
}