diff options
Diffstat (limited to 'vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff')
14 files changed, 652 insertions, 0 deletions
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php new file mode 100644 index 0000000..0a85983 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php @@ -0,0 +1,91 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Abstract backoff strategy that allows for a chain of responsibility + */ +abstract class AbstractBackoffStrategy implements BackoffStrategyInterface +{ + /** @var AbstractBackoffStrategy Next strategy in the chain */ + protected $next; + + /** @param AbstractBackoffStrategy $next Next strategy in the chain */ + public function setNext(AbstractBackoffStrategy $next) + { + $this->next = $next; + } + + /** + * Get the next backoff strategy in the chain + * + * @return AbstractBackoffStrategy|null + */ + public function getNext() + { + return $this->next; + } + + public function getBackoffPeriod( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ) { + $delay = $this->getDelay($retries, $request, $response, $e); + if ($delay === false) { + // The strategy knows that this must not be retried + return false; + } elseif ($delay === null) { + // If the strategy is deferring a decision and the next strategy will not make a decision then return false + return !$this->next || !$this->next->makesDecision() + ? false + : $this->next->getBackoffPeriod($retries, $request, $response, $e); + } elseif ($delay === true) { + // if the strategy knows that it must retry but is deferring to the next to determine the delay + if (!$this->next) { + return 0; + } else { + $next = $this->next; + while ($next->makesDecision() && $next->getNext()) { + $next = $next->getNext(); + } + return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0; + } + } else { + return $delay; + } + } + + /** + * Check if the strategy does filtering and makes decisions on whether or not to retry. + * + * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff + * decision. + * + * @return bool + */ + abstract public function makesDecision(); + + /** + * Implement the concrete strategy + * + * @param int $retries Number of retries of the request + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received. Note that there may not be a response + * @param HttpException $e Exception that was encountered if any + * + * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true + * or null to defer to the next strategy if available, and if not, return 0. + */ + abstract protected function getDelay( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php new file mode 100644 index 0000000..6ebee6c --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php @@ -0,0 +1,40 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +/** + * Strategy used to retry when certain error codes are encountered + */ +abstract class AbstractErrorCodeBackoffStrategy extends AbstractBackoffStrategy +{ + /** @var array Default cURL errors to retry */ + protected static $defaultErrorCodes = array(); + + /** @var array Error codes that can be retried */ + protected $errorCodes; + + /** + * @param array $codes Array of codes that should be retried + * @param BackoffStrategyInterface $next The optional next strategy + */ + public function __construct(array $codes = null, BackoffStrategyInterface $next = null) + { + $this->errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1); + $this->next = $next; + } + + /** + * Get the default failure codes to retry + * + * @return array + */ + public static function getDefaultFailureCodes() + { + return static::$defaultErrorCodes; + } + + public function makesDecision() + { + return true; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php new file mode 100644 index 0000000..ec54c28 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php @@ -0,0 +1,76 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Common\Event; +use Guzzle\Log\LogAdapterInterface; +use Guzzle\Log\MessageFormatter; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Logs backoff retries triggered from the BackoffPlugin + * + * Format your log messages using a template that can contain template substitutions found in {@see MessageFormatter}. + * In addition to the default template substitutions, there is also: + * + * - retries: The number of times the request has been retried + * - delay: The amount of time the request is being delayed + */ +class BackoffLogger implements EventSubscriberInterface +{ + /** @var string Default log message template */ + const DEFAULT_FORMAT = '[{ts}] {method} {url} - {code} {phrase} - Retries: {retries}, Delay: {delay}, Time: {connect_time}, {total_time}, cURL: {curl_code} {curl_error}'; + + /** @var LogAdapterInterface Logger used to log retries */ + protected $logger; + + /** @var MessageFormatter Formatter used to format log messages */ + protected $formatter; + + /** + * @param LogAdapterInterface $logger Logger used to log the retries + * @param MessageFormatter $formatter Formatter used to format log messages + */ + public function __construct(LogAdapterInterface $logger, MessageFormatter $formatter = null) + { + $this->logger = $logger; + $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT); + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->formatter->setTemplate($template); + + return $this; + } + + /** + * Called when a request is being retried + * + * @param Event $event Event emitted + */ + public function onRequestRetry(Event $event) + { + $this->logger->log($this->formatter->format( + $event['request'], + $event['response'], + $event['handle'], + array( + 'retries' => $event['retries'], + 'delay' => $event['delay'] + ) + )); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php new file mode 100644 index 0000000..99ace05 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php @@ -0,0 +1,126 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Common\Event; +use Guzzle\Common\AbstractHasDispatcher; +use Guzzle\Http\Message\EntityEnclosingRequestInterface; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Curl\CurlMultiInterface; +use Guzzle\Http\Exception\CurlException; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Plugin to automatically retry failed HTTP requests using a backoff strategy + */ +class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface +{ + const DELAY_PARAM = CurlMultiInterface::BLOCKING; + const RETRY_PARAM = 'plugins.backoff.retry_count'; + const RETRY_EVENT = 'plugins.backoff.retry'; + + /** @var BackoffStrategyInterface Backoff strategy */ + protected $strategy; + + /** + * @param BackoffStrategyInterface $strategy The backoff strategy used to determine whether or not to retry and + * the amount of delay between retries. + */ + public function __construct(BackoffStrategyInterface $strategy = null) + { + $this->strategy = $strategy; + } + + /** + * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors + * + * @param int $maxRetries Maximum number of retries + * @param array $httpCodes HTTP response codes to retry + * @param array $curlCodes cURL error codes to retry + * + * @return self + */ + public static function getExponentialBackoff( + $maxRetries = 3, + array $httpCodes = null, + array $curlCodes = null + ) { + return new self(new TruncatedBackoffStrategy($maxRetries, + new HttpBackoffStrategy($httpCodes, + new CurlBackoffStrategy($curlCodes, + new ExponentialBackoffStrategy() + ) + ) + )); + } + + public static function getAllEvents() + { + return array(self::RETRY_EVENT); + } + + public static function getSubscribedEvents() + { + return array( + 'request.sent' => 'onRequestSent', + 'request.exception' => 'onRequestSent', + CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' + ); + } + + /** + * Called when a request has been sent and isn't finished processing + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $exception = $event['exception']; + + $params = $request->getParams(); + $retries = (int) $params->get(self::RETRY_PARAM); + $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception); + + if ($delay !== false) { + // Calculate how long to wait until the request should be retried + $params->set(self::RETRY_PARAM, ++$retries) + ->set(self::DELAY_PARAM, microtime(true) + $delay); + // Send the request again + $request->setState(RequestInterface::STATE_TRANSFER); + $this->dispatch(self::RETRY_EVENT, array( + 'request' => $request, + 'response' => $response, + 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null, + 'retries' => $retries, + 'delay' => $delay + )); + } + } + + /** + * Called when a request is polling in the curl multi object + * + * @param Event $event + */ + public function onRequestPoll(Event $event) + { + $request = $event['request']; + $delay = $request->getParams()->get(self::DELAY_PARAM); + + // If the duration of the delay has passed, retry the request using the pool + if (null !== $delay && microtime(true) >= $delay) { + // Remove the request from the pool and then add it back again. This is required for cURL to know that we + // want to retry sending the easy handle. + $request->getParams()->remove(self::DELAY_PARAM); + // Rewind the request body if possible + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { + $request->getBody()->seek(0); + } + $multi = $event['curl_multi']; + $multi->remove($request); + $multi->add($request); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php new file mode 100644 index 0000000..4e590db --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php @@ -0,0 +1,30 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Strategy to determine if a request should be retried and how long to delay between retries + */ +interface BackoffStrategyInterface +{ + /** + * Get the amount of time to delay in seconds before retrying a request + * + * @param int $retries Number of retries of the request + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received. Note that there may not be a response + * @param HttpException $e Exception that was encountered if any + * + * @return bool|int Returns false to not retry or the number of seconds to delay between retries + */ + public function getBackoffPeriod( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php new file mode 100644 index 0000000..b4f77c3 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php @@ -0,0 +1,47 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Strategy that will invoke a closure to determine whether or not to retry with a delay + */ +class CallbackBackoffStrategy extends AbstractBackoffStrategy +{ + /** @var \Closure|array|mixed Callable method to invoke */ + protected $callback; + + /** @var bool Whether or not this strategy makes a retry decision */ + protected $decision; + + /** + * @param \Closure|array|mixed $callback Callable method to invoke + * @param bool $decision Set to true if this strategy makes a backoff decision + * @param BackoffStrategyInterface $next The optional next strategy + * + * @throws InvalidArgumentException + */ + public function __construct($callback, $decision, BackoffStrategyInterface $next = null) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException('The callback must be callable'); + } + $this->callback = $callback; + $this->decision = (bool) $decision; + $this->next = $next; + } + + public function makesDecision() + { + return $this->decision; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return call_user_func($this->callback, $retries, $request, $response, $e); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php new file mode 100644 index 0000000..061d2a4 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php @@ -0,0 +1,34 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Will retry the request using the same amount of delay for each retry. + * + * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried + */ +class ConstantBackoffStrategy extends AbstractBackoffStrategy +{ + /** @var int Amount of time for each delay */ + protected $delay; + + /** @param int $delay Amount of time to delay between each additional backoff */ + public function __construct($delay) + { + $this->delay = $delay; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $this->delay; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php new file mode 100644 index 0000000..a584ed4 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php @@ -0,0 +1,28 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; +use Guzzle\Http\Exception\CurlException; + +/** + * Strategy used to retry when certain cURL error codes are encountered. + */ +class CurlBackoffStrategy extends AbstractErrorCodeBackoffStrategy +{ + /** @var array Default cURL errors to retry */ + protected static $defaultErrorCodes = array( + CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR, CURLE_READ_ERROR, + CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING, + CURLE_SEND_ERROR, CURLE_RECV_ERROR + ); + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + if ($e && $e instanceof CurlException) { + return isset($this->errorCodes[$e->getErrorNo()]) ? true : null; + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php new file mode 100644 index 0000000..fb2912d --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php @@ -0,0 +1,25 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Implements an exponential backoff retry strategy. + * + * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried + */ +class ExponentialBackoffStrategy extends AbstractBackoffStrategy +{ + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return (int) pow(2, $retries); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php new file mode 100644 index 0000000..9c63a14 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php @@ -0,0 +1,30 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Strategy used to retry HTTP requests based on the response code. + * + * Retries 500 and 503 error by default. + */ +class HttpBackoffStrategy extends AbstractErrorCodeBackoffStrategy +{ + /** @var array Default cURL errors to retry */ + protected static $defaultErrorCodes = array(500, 503); + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + if ($response) { + //Short circuit the rest of the checks if it was successful + if ($response->isSuccessful()) { + return false; + } else { + return isset($this->errorCodes[$response->getStatusCode()]) ? true : null; + } + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php new file mode 100644 index 0000000..b35e8a4 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php @@ -0,0 +1,36 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Implements a linear backoff retry strategy. + * + * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried + */ +class LinearBackoffStrategy extends AbstractBackoffStrategy +{ + /** @var int Amount of time to progress each delay */ + protected $step; + + /** + * @param int $step Amount of time to increase the delay each additional backoff + */ + public function __construct($step = 1) + { + $this->step = $step; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries * $this->step; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php new file mode 100644 index 0000000..4fd73fe --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php @@ -0,0 +1,25 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Strategy used to retry HTTP requests when the response's reason phrase matches one of the registered phrases. + */ +class ReasonPhraseBackoffStrategy extends AbstractErrorCodeBackoffStrategy +{ + public function makesDecision() + { + return true; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + if ($response) { + return isset($this->errorCodes[$response->getReasonPhrase()]) ? true : null; + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php new file mode 100644 index 0000000..3608f35 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php @@ -0,0 +1,36 @@ +<?php + +namespace Guzzle\Plugin\Backoff; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Exception\HttpException; + +/** + * Strategy that will not retry more than a certain number of times. + */ +class TruncatedBackoffStrategy extends AbstractBackoffStrategy +{ + /** @var int Maximum number of retries per request */ + protected $max; + + /** + * @param int $maxRetries Maximum number of retries per request + * @param BackoffStrategyInterface $next The optional next strategy + */ + public function __construct($maxRetries, BackoffStrategyInterface $next = null) + { + $this->max = $maxRetries; + $this->next = $next; + } + + public function makesDecision() + { + return true; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries < $this->max ? null : false; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json new file mode 100644 index 0000000..91c122c --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-backoff", + "description": "Guzzle backoff retry plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Backoff": "" } + }, + "target-dir": "Guzzle/Plugin/Backoff", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} |