diff --git a/src/composer.json b/src/composer.json
index 6f2b7754..d88ad12b 100644
--- a/src/composer.json
+++ b/src/composer.json
@@ -26,7 +26,8 @@
"symfony/process": "^5",
"symfony/filesystem": "^5",
"lucatume/di52": "^3",
- "stecman/symfony-console-completion": "^0.11.0"
+ "stecman/symfony-console-completion": "^0.11.0",
+ "composer/ca-bundle": "^1.4"
},
"require-dev": {
"phpunit/phpunit": "^8",
diff --git a/src/composer.lock b/src/composer.lock
index 82f67bb5..2bd507c8 100644
--- a/src/composer.lock
+++ b/src/composer.lock
@@ -6,6 +6,82 @@
],
"content-hash": "ed84f6c3e05a35fd3f0bf19609c0d984",
"packages": [
+ {
+ "name": "composer/ca-bundle",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/ca-bundle.git",
+ "reference": "3ce240142f6d59b808dd65c1f52f7a1c252e6cfd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/3ce240142f6d59b808dd65c1f52f7a1c252e6cfd",
+ "reference": "3ce240142f6d59b808dd65c1f52f7a1c252e6cfd",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "ext-pcre": "*",
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.55",
+ "psr/log": "^1.0",
+ "symfony/phpunit-bridge": "^4.2 || ^5",
+ "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\CaBundle\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
+ "keywords": [
+ "cabundle",
+ "cacert",
+ "certificate",
+ "ssl",
+ "tls"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/ca-bundle/issues",
+ "source": "https://github.com/composer/ca-bundle/tree/1.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-23T10:16:52+00:00"
+ },
{
"name": "lucatume/di52",
"version": "3.3.5",
@@ -1291,20 +1367,21 @@
},
{
"name": "phar-io/manifest",
- "version": "2.0.3",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
- "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
- "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
"shasum": ""
},
"require": {
"ext-dom": "*",
+ "ext-libxml": "*",
"ext-phar": "*",
"ext-xmlwriter": "*",
"phar-io/version": "^3.0.1",
@@ -1345,9 +1422,15 @@
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
"support": {
"issues": "https://github.com/phar-io/manifest/issues",
- "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
},
- "time": "2021-07-20T11:28:43+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
},
{
"name": "phar-io/version",
@@ -1526,16 +1609,16 @@
},
{
"name": "phpunit/php-code-coverage",
- "version": "7.0.15",
+ "version": "7.0.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "819f92bba8b001d4363065928088de22f25a3a48"
+ "reference": "40a4ed114a4aea5afd6df8d0f0c9cd3033097f66"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48",
- "reference": "819f92bba8b001d4363065928088de22f25a3a48",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/40a4ed114a4aea5afd6df8d0f0c9cd3033097f66",
+ "reference": "40a4ed114a4aea5afd6df8d0f0c9cd3033097f66",
"shasum": ""
},
"require": {
@@ -1587,7 +1670,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.17"
},
"funding": [
{
@@ -1595,20 +1678,20 @@
"type": "github"
}
],
- "time": "2021-07-26T12:20:09+00:00"
+ "time": "2024-03-02T06:09:37+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "2.0.5",
+ "version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5"
+ "reference": "69deeb8664f611f156a924154985fbd4911eb36b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5",
- "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/69deeb8664f611f156a924154985fbd4911eb36b",
+ "reference": "69deeb8664f611f156a924154985fbd4911eb36b",
"shasum": ""
},
"require": {
@@ -1647,7 +1730,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
- "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5"
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.6"
},
"funding": [
{
@@ -1655,7 +1738,7 @@
"type": "github"
}
],
- "time": "2021-12-02T12:42:26+00:00"
+ "time": "2024-03-01T13:39:50+00:00"
},
{
"name": "phpunit/php-text-template",
@@ -1704,16 +1787,16 @@
},
{
"name": "phpunit/php-timer",
- "version": "2.1.3",
+ "version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662"
+ "reference": "a691211e94ff39a34811abd521c31bd5b305b0bb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662",
- "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a691211e94ff39a34811abd521c31bd5b305b0bb",
+ "reference": "a691211e94ff39a34811abd521c31bd5b305b0bb",
"shasum": ""
},
"require": {
@@ -1751,7 +1834,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
- "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3"
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.4"
},
"funding": [
{
@@ -1759,7 +1842,7 @@
"type": "github"
}
],
- "time": "2020-11-30T08:20:02+00:00"
+ "time": "2024-03-01T13:42:41+00:00"
},
{
"name": "phpunit/php-token-stream",
@@ -1921,16 +2004,16 @@
},
{
"name": "sebastian/code-unit-reverse-lookup",
- "version": "1.0.2",
+ "version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
+ "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
- "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
+ "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
"shasum": ""
},
"require": {
@@ -1964,7 +2047,7 @@
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
- "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3"
},
"funding": [
{
@@ -1972,7 +2055,7 @@
"type": "github"
}
],
- "time": "2020-11-30T08:15:22+00:00"
+ "time": "2024-03-01T13:45:45+00:00"
},
{
"name": "sebastian/comparator",
@@ -2116,16 +2199,16 @@
},
{
"name": "sebastian/environment",
- "version": "4.2.4",
+ "version": "4.2.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0"
+ "reference": "56932f6049a0482853056ffd617c91ffcc754205"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
- "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/56932f6049a0482853056ffd617c91ffcc754205",
+ "reference": "56932f6049a0482853056ffd617c91ffcc754205",
"shasum": ""
},
"require": {
@@ -2167,7 +2250,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
- "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4"
+ "source": "https://github.com/sebastianbergmann/environment/tree/4.2.5"
},
"funding": [
{
@@ -2175,24 +2258,24 @@
"type": "github"
}
],
- "time": "2020-11-30T07:53:42+00:00"
+ "time": "2024-03-01T13:49:59+00:00"
},
{
"name": "sebastian/exporter",
- "version": "3.1.5",
+ "version": "3.1.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6"
+ "reference": "1939bc8fd1d39adcfa88c5b35335910869214c56"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6",
- "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1939bc8fd1d39adcfa88c5b35335910869214c56",
+ "reference": "1939bc8fd1d39adcfa88c5b35335910869214c56",
"shasum": ""
},
"require": {
- "php": ">=7.0",
+ "php": ">=7.2",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
@@ -2244,7 +2327,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
- "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.6"
},
"funding": [
{
@@ -2252,7 +2335,7 @@
"type": "github"
}
],
- "time": "2022-09-14T06:00:17+00:00"
+ "time": "2024-03-02T06:21:38+00:00"
},
{
"name": "sebastian/global-state",
@@ -2320,16 +2403,16 @@
},
{
"name": "sebastian/object-enumerator",
- "version": "3.0.4",
+ "version": "3.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
+ "reference": "ac5b293dba925751b808e02923399fb44ff0d541"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
- "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/ac5b293dba925751b808e02923399fb44ff0d541",
+ "reference": "ac5b293dba925751b808e02923399fb44ff0d541",
"shasum": ""
},
"require": {
@@ -2365,7 +2448,7 @@
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
- "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.5"
},
"funding": [
{
@@ -2373,20 +2456,20 @@
"type": "github"
}
],
- "time": "2020-11-30T07:40:27+00:00"
+ "time": "2024-03-01T13:54:02+00:00"
},
{
"name": "sebastian/object-reflector",
- "version": "1.1.2",
+ "version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
- "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
+ "reference": "1d439c229e61f244ff1f211e5c99737f90c67def"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
- "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/1d439c229e61f244ff1f211e5c99737f90c67def",
+ "reference": "1d439c229e61f244ff1f211e5c99737f90c67def",
"shasum": ""
},
"require": {
@@ -2420,7 +2503,7 @@
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-reflector/issues",
- "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.3"
},
"funding": [
{
@@ -2428,20 +2511,20 @@
"type": "github"
}
],
- "time": "2020-11-30T07:37:18+00:00"
+ "time": "2024-03-01T13:56:04+00:00"
},
{
"name": "sebastian/recursion-context",
- "version": "3.0.1",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
+ "reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
- "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/9bfd3c6f1f08c026f542032dfb42813544f7d64c",
+ "reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c",
"shasum": ""
},
"require": {
@@ -2483,7 +2566,7 @@
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.2"
},
"funding": [
{
@@ -2491,20 +2574,20 @@
"type": "github"
}
],
- "time": "2020-11-30T07:34:24+00:00"
+ "time": "2024-03-01T14:07:30+00:00"
},
{
"name": "sebastian/resource-operations",
- "version": "2.0.2",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
- "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3"
+ "reference": "72a7f7674d053d548003b16ff5a106e7e0e06eee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3",
- "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/72a7f7674d053d548003b16ff5a106e7e0e06eee",
+ "reference": "72a7f7674d053d548003b16ff5a106e7e0e06eee",
"shasum": ""
},
"require": {
@@ -2534,8 +2617,7 @@
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
- "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
- "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2"
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.3"
},
"funding": [
{
@@ -2543,20 +2625,20 @@
"type": "github"
}
],
- "time": "2020-11-30T07:30:19+00:00"
+ "time": "2024-03-01T13:59:09+00:00"
},
{
"name": "sebastian/type",
- "version": "1.1.4",
+ "version": "1.1.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
- "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4"
+ "reference": "18f071c3a29892b037d35e6b20ddf3ea39b42874"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4",
- "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/18f071c3a29892b037d35e6b20ddf3ea39b42874",
+ "reference": "18f071c3a29892b037d35e6b20ddf3ea39b42874",
"shasum": ""
},
"require": {
@@ -2591,7 +2673,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
- "source": "https://github.com/sebastianbergmann/type/tree/1.1.4"
+ "source": "https://github.com/sebastianbergmann/type/tree/1.1.5"
},
"funding": [
{
@@ -2599,7 +2681,7 @@
"type": "github"
}
],
- "time": "2020-11-30T07:25:11+00:00"
+ "time": "2024-03-01T14:04:07+00:00"
},
{
"name": "sebastian/version",
diff --git a/src/src/RequestBuilder.php b/src/src/RequestBuilder.php
index 6ed8e9a9..e503cba4 100644
--- a/src/src/RequestBuilder.php
+++ b/src/src/RequestBuilder.php
@@ -2,12 +2,10 @@
namespace QIT_CLI;
+use Composer\CaBundle\CaBundle;
use QIT_CLI\Exceptions\DoingAutocompleteException;
use QIT_CLI\Exceptions\NetworkErrorException;
-use QIT_CLI\IO\Input;
use QIT_CLI\IO\Output;
-use Symfony\Component\Console\Helper\QuestionHelper;
-use Symfony\Component\Console\Question\ConfirmationQuestion;
class RequestBuilder {
/** @var string $url */
@@ -37,11 +35,6 @@ class RequestBuilder {
/** @var int */
protected $timeout_in_seconds = 15;
- /**
- * @var bool Whether we asked about CA file on this request.
- */
- protected static $asked_ca_file_override = false;
-
public function __construct( string $url = '' ) {
$this->url = $url;
}
@@ -168,7 +161,19 @@ public function request(): string {
CURLOPT_HEADER => 1,
];
- $this->maybe_set_certificate_authority_file( $curl_parameters );
+ try {
+ $ca_path_or_file = CaBundle::getSystemCaRootBundlePath();
+
+ if ( is_dir( $ca_path_or_file ) ) {
+ $curl_parameters[ CURLOPT_CAPATH ] = $ca_path_or_file;
+ } else {
+ $curl_parameters[ CURLOPT_CAINFO ] = $ca_path_or_file;
+ }
+ } catch ( \Exception $e ) {
+ if ( App::make( Output::class )->isVerbose() ) {
+ App::make( Output::class )->writeln( 'Could not set CAINFO for cURL: ' . $e->getMessage() . '' );
+ }
+ }
if ( App::make( Output::class )->isVeryVerbose() ) {
$curl_parameters[ CURLOPT_VERBOSE ] = true;
@@ -269,22 +274,13 @@ public function request(): string {
if ( $response_status_code === 429 ) {
if ( $this->retry_429 > 0 ) {
$this->retry_429 --;
- App::make( Output::class )->writeln( 'Request failed... Retrying (429 Too many Requests)' );
+ $sleep_seconds = $this->wait_after_429( $headers );
+ App::make( Output::class )->writeln( sprintf( 'Request failed... Waiting %d seconds and retrying (429 Too many Requests)', $sleep_seconds ) );
- sleep( $this->wait_after_429( $headers ) );
+ sleep( $sleep_seconds );
goto retry_request; // phpcs:ignore Generic.PHP.DiscourageGoto.Found
}
} else {
- // Is it an SSL error?
- foreach ( [ 'ssl', 'certificate', 'issuer' ] as $keyword ) {
- if ( stripos( $error_message, $keyword ) !== false ) {
- $downloaded = $this->maybe_download_certificate_authority_file();
- if ( $downloaded ) {
- goto retry_request; // phpcs:ignore Generic.PHP.DiscourageGoto.Found
- }
- break;
- }
- }
if ( $this->retry > 0 ) {
$this->retry --;
App::make( Output::class )->writeln( sprintf( 'Request failed... Retrying (HTTP Status Code %s)', $response_status_code ) );
@@ -309,113 +305,6 @@ public function request(): string {
return $body;
}
- /**
- * @param array $curl_parameters
- *
- * @return void
- */
- protected function maybe_set_certificate_authority_file( array &$curl_parameters ) {
- // Early bail: We only do this for Windows.
- if ( ! is_windows() ) {
- return;
- }
-
- $cached_ca_filepath = App::make( Cache::class )->get( 'ca_filepath' );
-
- // Cache hit.
- if ( $cached_ca_filepath !== null && file_exists( $cached_ca_filepath ) ) {
- $curl_parameters[ CURLOPT_CAINFO ] = $cached_ca_filepath;
- }
- }
-
- /**
- * @return bool Whether it downloaded the CA file or not.
- */
- protected function maybe_download_certificate_authority_file(): bool {
- $output = App::make( Output::class );
- // Early bail: We only do this for Windows.
- if ( ! is_windows() ) {
- if ( $output->isVerbose() ) {
- $output->writeln( 'Skipping certificate authority file check. Not running on Windows.' );
- }
-
- return false;
- }
-
- if ( $output->isVerbose() ) {
- $output->writeln( 'Checking if we need to download the certificate authority file...' );
- }
-
- $cached_ca_filepath = App::make( Cache::class )->get( 'ca_filepath' );
-
- // Cache hit.
- if ( $cached_ca_filepath !== null && file_exists( $cached_ca_filepath ) ) {
- return false;
- }
-
- if ( $output->isVerbose() ) {
- $output->writeln( 'No cached certificate authority file found.' );
- }
-
- if ( self::$asked_ca_file_override ) {
- if ( $output->isVerbose() ) {
- $output->writeln( 'Skipping certificate authority file check. Already asked.' );
- }
-
- return false;
- }
-
- self::$asked_ca_file_override = true;
-
- // Ask the user if he wants us to solve it for them.
- $input = App::make( Input::class );
-
- $helper = App::make( QuestionHelper::class );
- $question = new ConfirmationQuestion( "A QIT network request failed due to an SSL certificate issue on Windows. Would you like to download a CA file, used exclusively for QIT requests, to potentially fix this?\n Please answer [y/n]: ", false );
-
- if ( getenv( 'QIT_WINDOWS_DOWNLOAD_CA' ) !== 'yes' && ( ! $input->isInteractive() || ! $helper->ask( $input, $output, $question ) ) ) {
- if ( $output->isVerbose() ) {
- $output->writeln( 'Skipping certificate authority file download.' );
- }
-
- return false;
- }
-
- if ( $output->isVerbose() ) {
- $output->writeln( 'Downloading certificate authority file...' );
- }
-
- // Download it to QIT Config Dir and save it in the cache.
- $local_ca_file = Config::get_qit_dir() . 'cacert.pem';
-
- if ( ! file_exists( $local_ca_file ) ) {
- $remote_ca_file_contents = @file_get_contents( 'http://curl.se/ca/cacert.pem' );
-
- if ( empty( $remote_ca_file_contents ) ) {
- $output->writeln( "Could not download the certificate authority file. Please download it manually from http://curl.se/ca/cacert.pem and place it in $local_ca_file" );
-
- return false;
- }
-
- if ( ! file_put_contents( $local_ca_file, $remote_ca_file_contents ) ) {
- $output->writeln( "Could not write the certificate authority file. Please download it manually from http://curl.se/ca/cacert.pem and place it in $local_ca_file" );
-
- return false;
- }
- clearstatcache( true, $local_ca_file );
- }
-
- if ( $output->isVerbose() ) {
- $output->writeln( 'Certificate authority file downloaded and saved.' );
- }
-
- $year_in_seconds = 60 * 60 * 24 * 365;
-
- App::make( Cache::class )->set( 'ca_filepath', $local_ca_file, $year_in_seconds );
-
- return true;
- }
-
protected function wait_after_429( string $headers, int $max_wait = 60 ): int {
$retry_after = null;
@@ -473,6 +362,8 @@ protected function wait_after_429( string $headers, int $max_wait = 60 ): int {
// And no longer than 60 seconds.
$retry_after = min( $max_wait, $retry_after );
+ $retry_after += rand( 0, 5 ); // Add a random number of seconds to avoid all clients retrying at the same time.
+
return $retry_after;
}
diff --git a/src/tests/RequestBuilderTest.php b/src/tests/RequestBuilderTest.php
index 9473cc8c..0c98a74b 100644
--- a/src/tests/RequestBuilderTest.php
+++ b/src/tests/RequestBuilderTest.php
@@ -2,6 +2,7 @@
namespace QIT_CLI_Tests;
+use PHPUnit\Framework\AssertionFailedError;
use QIT_CLI\RequestBuilder;
use PHPUnit\Framework\TestCase;
@@ -20,10 +21,21 @@ public function wait_after_429( string $headers, int $max_wait = 60 ): int {
};
}
+ protected function assertRetryDelayWithinRange( $expected, $actual, $delta ) {
+ if ( $actual < $expected ) {
+ $this->fail( "Expected value is $expected, actual value is $actual, which is less than expected." );
+ } elseif ( $actual > $expected + $delta ) {
+ $this->fail( "Expected value is $expected, actual value is $actual, which is greater than expected + delta ($delta)." );
+ } else {
+ // If the actual value is within the acceptable range, explicitly assert true.
+ $this->assertTrue( true );
+ }
+ }
+
public function test_retry_after_seconds() {
$headers = "Retry-After: 59\r\nOther-Header: value";
- $this->assertEquals( 59, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 59, $this->sut->wait_after_429( $headers ), 5 );
}
public function test_retry_after_http_date() {
@@ -34,35 +46,56 @@ public function test_retry_after_http_date() {
// Since time will pass between the creation of the date and this calculation,
// allow a small margin in the assertion
$expected_delay = $dateTime->getTimestamp() - time();
- $this->assertEqualsWithDelta( $expected_delay, $this->sut->wait_after_429( $headers, 130 ), 5 );
+ $this->assertRetryDelayWithinRange( $expected_delay, $this->sut->wait_after_429( $headers, 130 ), 5 );
}
public function test_no_retry_after_header() {
$headers = "Other-Header: value";
- $this->assertEquals( 5, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 5, $this->sut->wait_after_429( $headers ), 5 );
}
public function test_invalid_retry_after_header() {
$headers = "Retry-After: invalid\r\nOther-Header: value";
- $this->assertEquals( 5, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 5, $this->sut->wait_after_429( $headers ), 5 );
}
public function test_exponential_backoff() {
$headers = "Retry-After: invalid\r\nOther-Header: value";
- $this->assertEquals( 5, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 5, $this->sut->wait_after_429( $headers ), 5 );
// Mimick integration-level test.
$this->sut->retry_429 --;
- $this->assertEquals( 10, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 10, $this->sut->wait_after_429( $headers ), 5 );
$this->sut->retry_429 --;
- $this->assertEquals( 20, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 20, $this->sut->wait_after_429( $headers ), 5 );
$this->sut->retry_429 --;
- $this->assertEquals( 40, $this->sut->wait_after_429( $headers ) );
+ $this->assertRetryDelayWithinRange( 40, $this->sut->wait_after_429( $headers ), 5 );
$this->sut->retry_429 --;
- $this->assertEquals( 80, $this->sut->wait_after_429( $headers, 300 ) );
+ $this->assertRetryDelayWithinRange( 80, $this->sut->wait_after_429( $headers, 200 ), 5 );
$this->sut->retry_429 --;
- $this->assertEquals( 160, $this->sut->wait_after_429( $headers, 300 ) );
+ $this->assertRetryDelayWithinRange( 160, $this->sut->wait_after_429( $headers, 200 ), 5 );
+ }
+
+ //
+ // Some tests for our custom assertion.
+ //
+ public function testExactValue() {
+ $this->assertRetryDelayWithinRange( 10, 10, 1 ); // Delta of 1
+ }
+
+ public function testWithinDelta() {
+ $this->assertRetryDelayWithinRange( 10, 11, 1 ); // Within delta of 1
+ }
+
+ public function testExceedsDelta() {
+ $this->expectException( AssertionFailedError::class );
+ $this->assertRetryDelayWithinRange( 10, 12, 1 ); // Exceeds delta of 1
+ }
+
+ public function testBelowExpected() {
+ $this->expectException( AssertionFailedError::class );
+ $this->assertRetryDelayWithinRange( 10, 9, 1 ); // Below expected
}
}