From 6389b9e1043f1f6710517fc9e16783360bfbd828 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 12 Mar 2019 12:07:23 +0100 Subject: [PATCH] Adding timeout options both CLI and .cigar.json (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding timeout options both CLI and .cigar.json 🎆🎆🎆 Happy New Year 🎆🎆🎆 This PR adds the timeout options discussed in #31 Closes / implements #31 Any values in the CLI are overridden by what ever value is in the `.cigar.json` * Removed short hand (ct) for --connect-timeout via https://github.com/Brunty/cigar/pull/32#discussion_r244988005 by @brunty * Test response timeout .json config and CLI flags through stubs via https://github.com/Brunty/cigar/pull/32#discussion_r244988203 by @brunty * s is now short the connect-timeout --- .cigar.json.example | 12 ++++++++++++ bin/cigar | 32 ++++++++++++++++++-------------- spec/AsyncCheckerSpec.php | 16 ++++++++++++++++ spec/CigarCliSpec.php | 7 +++++++ spec/ParserSpec.php | 7 +++++-- spec/stubs/.cigar.fail.json | 12 ++++++++++++ spec/stubs/.cigar.pass.json | 12 ++++++++++++ spec/stubs/.cigar.timeouts.json | 14 ++++++++++++++ src/AsyncChecker.php | 7 +++++++ src/Parser.php | 23 +++++++++++++++++++++-- src/Url.php | 32 ++++++++++++++++++++++++++++++-- 11 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 spec/stubs/.cigar.timeouts.json diff --git a/.cigar.json.example b/.cigar.json.example index f6ecc43..cd8e39f 100644 --- a/.cigar.json.example +++ b/.cigar.json.example @@ -21,5 +21,17 @@ "url": "http://httpbin.org/robots.txt", "status": 200, "content-type": "text/plain" + }, + { + "url": "http://httpbin.org/delay/3", + "status": 200, + "request-timeout": 1, + "response-timeout": 5 + }, + { + "url": "http://httpbin.org/drip?duration=3", + "status": 200, + "request-timeout": 1, + "response-timeout": 5 } ] diff --git a/bin/cigar b/bin/cigar index 278acb0..e81cd3d 100755 --- a/bin/cigar +++ b/bin/cigar @@ -18,7 +18,7 @@ use Brunty\Cigar\Outputter; use Brunty\Cigar\Parser; use Brunty\Cigar\Result; -$options = getopt('c:ia:u:jh:', ['version', 'help', 'quiet', 'config:', 'insecure', 'auth:', 'url:', 'json', 'header:']); +$options = getopt('c:ia:u:jh:t:s:', ['version', 'help', 'quiet', 'config:', 'insecure', 'auth:', 'url:', 'json', 'header:', 'timeout:', 'connect-timeout:']); if (isset($options['help'])) { $content = <<parse($file); + $domains = (new Parser($baseUrl, $connectTimeout, $timeout))->parse($file); } catch (\Throwable $e) { $outputter->writeErrorLine('Unable to parse .cigar.json file'); exit(1); } -$secure = ! (isset($options['i']) || isset($options['insecure'])); -$authorization = $options['a'] ?? $options['auth'] ?? null; -$headers = (array) ($options['h'] ?? $options['header'] ?? []); - $results = (new AsyncChecker($secure, $authorization, $headers))->check($domains); $passedResults = array_filter($results, function (Result $result) { return $result->hasPassed(); diff --git a/spec/AsyncCheckerSpec.php b/spec/AsyncCheckerSpec.php index 1ae8c4b..29eca6f 100644 --- a/spec/AsyncCheckerSpec.php +++ b/spec/AsyncCheckerSpec.php @@ -62,4 +62,20 @@ expect($results[0]->getStatusCode())->toEqual($expected[0]->getStatusCode()); }); }); + + context('when timeouts are set', function () { + it('checks a URL that will timeout', function () { + // Need to change for a better setup URL that doesn't default to a potentially unknown site + $domain = new Url('https://httpbin.org/delay/3', 200, null, null, 1, 1); + $domains = [$domain]; + + $results = (new AsyncChecker())->check($domains); + + $expected = [ + new Result($domain, 0), + ]; + + expect($results[0]->getStatusCode())->toEqual($expected[0]->getStatusCode()); + }); + }); }); diff --git a/spec/CigarCliSpec.php b/spec/CigarCliSpec.php index 59ad5b4..97fe9b4 100755 --- a/spec/CigarCliSpec.php +++ b/spec/CigarCliSpec.php @@ -87,4 +87,11 @@ expect($process->getExitCode())->toBe(0); }); + + it('can be passed timeout arguments and it overwrites the configured value in .cigar.json', function () { + $process = new Process('cd spec && cp stubs/.cigar.timeouts.json .cigar.json && ../bin/cigar -t 1'); + $process->run(); + + expect($process->getExitCode())->toBe(1); + }); }); diff --git a/spec/ParserSpec.php b/spec/ParserSpec.php index e560823..b106b8d 100644 --- a/spec/ParserSpec.php +++ b/spec/ParserSpec.php @@ -19,7 +19,10 @@ { "url": "http://httpbin.org/status/418", "status": 418, - "content": "teapot" + "content": "teapot", + "content-type": "kitchen/teapot", + "connect-timeout": 1, + "timeout": 2 } ] ', @@ -31,7 +34,7 @@ $expected = [ new Url('http://httpbin.org/status/418', 418), new Url('http://httpbin.org/status/200', 200), - new Url('http://httpbin.org/status/418', 418, 'teapot'), + new Url('http://httpbin.org/status/418', 418, 'teapot', 'kitchen/teapot', 1, 2), ]; expect($results)->toEqual($expected); diff --git a/spec/stubs/.cigar.fail.json b/spec/stubs/.cigar.fail.json index 333f2fb..0f3d661 100644 --- a/spec/stubs/.cigar.fail.json +++ b/spec/stubs/.cigar.fail.json @@ -16,5 +16,17 @@ { "url": "http://httpbin.org/status/500", "status": 501 + }, + { + "url": "http://httpbin.org/delay/3", + "status": 200, + "request-timeout": 1, + "response-timeout": 1 + }, + { + "url": "http://httpbin.org/drip?duration=3", + "status": 200, + "request-timeout": 1, + "response-timeout": 1 } ] diff --git a/spec/stubs/.cigar.pass.json b/spec/stubs/.cigar.pass.json index 1174c50..a422997 100644 --- a/spec/stubs/.cigar.pass.json +++ b/spec/stubs/.cigar.pass.json @@ -16,5 +16,17 @@ { "url": "http://httpbin.org/status/500", "status": 500 + }, + { + "url": "http://httpbin.org/delay/3", + "status": 200, + "request-timeout": 1, + "response-timeout": 5 + }, + { + "url": "http://httpbin.org/drip?duration=3", + "status": 200, + "request-timeout": 1, + "response-timeout": 5 } ] diff --git a/spec/stubs/.cigar.timeouts.json b/spec/stubs/.cigar.timeouts.json new file mode 100644 index 0000000..f3b12cc --- /dev/null +++ b/spec/stubs/.cigar.timeouts.json @@ -0,0 +1,14 @@ +[ + { + "url": "http://httpbin.org/delay/3", + "status": 200, + "request-timeout": 1, + "response-timeout": 5 + }, + { + "url": "http://httpbin.org/drip?duration=3", + "status": 200, + "request-timeout": 1, + "response-timeout": 5 + } +] diff --git a/src/AsyncChecker.php b/src/AsyncChecker.php index 9de1cdd..a95ac8c 100644 --- a/src/AsyncChecker.php +++ b/src/AsyncChecker.php @@ -57,6 +57,13 @@ public function check(array $urlsToCheck): array curl_setopt($channel, CURLOPT_HTTPHEADER, $this->headers); } + if ($urlToCheck->getConnectTimeout() !== null && $urlToCheck->getConnectTimeout() > 0) { + curl_setopt($channel, CURLOPT_CONNECTTIMEOUT, $urlToCheck->getConnectTimeout()); + } + if ($urlToCheck->getTimeout() !== null && $urlToCheck->getTimeout() > 0) { + curl_setopt($channel, CURLOPT_TIMEOUT, $urlToCheck->getTimeout()); + } + curl_multi_add_handle($mh, $channel); $channels[$url] = $channel; diff --git a/src/Parser.php b/src/Parser.php index 3055a78..c89e4b0 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -9,9 +9,21 @@ class Parser */ private $baseUrl; - public function __construct(string $baseUrl = null) + /** + * @var null|int + */ + private $connectTimeout; + + /** + * @var null|int + */ + private $timeout; + + public function __construct(string $baseUrl = null, int $connectTimeout = null, int $timeout = null) { $this->baseUrl = rtrim($baseUrl, '/'); + $this->connectTimeout = $connectTimeout; + $this->timeout = $timeout; } /** @@ -31,7 +43,14 @@ public function parse(string $filename): array return array_map(function($value) { $url = $this->getUrl($value['url']); - return new Url($url, $value['status'], $value['content'] ?? null, $value['content-type'] ?? null); + return new Url( + $url, + $value['status'], + $value['content'] ?? null, + $value['content-type'] ?? null, + $value['connect-timeout'] ?? $this->connectTimeout, + $value['timeout'] ?? $this->timeout + ); }, $urls); } diff --git a/src/Url.php b/src/Url.php index f51bb2c..d53ca37 100644 --- a/src/Url.php +++ b/src/Url.php @@ -26,12 +26,30 @@ class Url */ private $contentType; - public function __construct(string $url, int $status, string $content = null, string $contentType = null) - { + /** + * @var null|int + */ + private $connectTimeout; + + /** + * @var null|int + */ + private $timeout; + + public function __construct( + string $url, + int $status, + string $content = null, + string $contentType = null, + int $connectTimeout = null, + int $timeout = null + ) { $this->url = $url; $this->status = $status; $this->content = $content; $this->contentType = $contentType; + $this->connectTimeout = $connectTimeout; + $this->timeout = $timeout; } public function getUrl(): string @@ -53,4 +71,14 @@ public function getContentType() { return $this->contentType; } + + public function getConnectTimeout() + { + return $this->connectTimeout; + } + + public function getTimeout() + { + return $this->timeout; + } }