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..d0af9af 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:ct:', ['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/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/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; + } }