hello 404 Not Found
Al-HUWAITI Shell
Al-huwaiti


Server : Apache
System : Linux webm006.cluster103.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User : chryzalihi ( 621211)
PHP Version : 8.3.31
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
Directory :  /home/chryzalihi/www/wp-content/languages/themes/the/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/chryzalihi/www/wp-content/languages/themes/the/Plugin.tar
Log/composer.json000060400000001323152441445220010005 0ustar00{
    "name": "guzzle/plugin-log",
    "description": "Guzzle log plugin for over the wire logging",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "log", "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\\Log": "" }
    },
    "target-dir": "Guzzle/Plugin/Log",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Log/LogPlugin.php000060400000013375152441445220007706 0ustar00<?php

namespace Guzzle\Plugin\Log;

use Guzzle\Common\Event;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\MessageFormatter;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin class that will add request and response logging to an HTTP request.
 *
 * The log plugin uses a message formatter that allows custom messages via template variable substitution.
 *
 * @see MessageLogger for a list of available log template variable substitutions
 */
class LogPlugin implements EventSubscriberInterface
{
    /** @var LogAdapterInterface Adapter responsible for writing log data */
    protected $logAdapter;

    /** @var MessageFormatter Formatter used to format messages before logging */
    protected $formatter;

    /** @var bool Whether or not to wire request and response bodies */
    protected $wireBodies;

    /**
     * @param LogAdapterInterface     $logAdapter Adapter object used to log message
     * @param string|MessageFormatter $formatter  Formatter used to format log messages or the formatter template
     * @param bool                    $wireBodies Set to true to track request and response bodies using a temporary
     *                                            buffer if the bodies are not repeatable.
     */
    public function __construct(
        LogAdapterInterface $logAdapter,
        $formatter = null,
        $wireBodies = false
    ) {
        $this->logAdapter = $logAdapter;
        $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
        $this->wireBodies = $wireBodies;
    }

    /**
     * Get a log plugin that outputs full request, response, and curl error information to stderr
     *
     * @param bool     $wireBodies Set to false to disable request/response body output when they use are not repeatable
     * @param resource $stream     Stream to write to when logging. Defaults to STDERR when it is available
     *
     * @return self
     */
    public static function getDebugPlugin($wireBodies = true, $stream = null)
    {
        if ($stream === null) {
            if (defined('STDERR')) {
                $stream = STDERR;
            } else {
                $stream = fopen('php://output', 'w');
            }
        }

        return new self(new ClosureLogAdapter(function ($m) use ($stream) {
            fwrite($stream, $m . PHP_EOL);
        }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'curl.callback.write' => array('onCurlWrite', 255),
            'curl.callback.read'  => array('onCurlRead', 255),
            'request.before_send' => array('onRequestBeforeSend', 255),
            'request.sent'        => array('onRequestSent', 255)
        );
    }

    /**
     * Event triggered when curl data is read from a request
     *
     * @param Event $event
     */
    public function onCurlRead(Event $event)
    {
        // Stream the request body to the log if the body is not repeatable
        if ($wire = $event['request']->getParams()->get('request_wire')) {
            $wire->write($event['read']);
        }
    }

    /**
     * Event triggered when curl data is written to a response
     *
     * @param Event $event
     */
    public function onCurlWrite(Event $event)
    {
        // Stream the response body to the log if the body is not repeatable
        if ($wire = $event['request']->getParams()->get('response_wire')) {
            $wire->write($event['write']);
        }
    }

    /**
     * Called before a request is sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        if ($this->wireBodies) {
            $request = $event['request'];
            // Ensure that curl IO events are emitted
            $request->getCurlOptions()->set('emit_io', true);
            // We need to make special handling for content wiring and non-repeatable streams.
            if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
                && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
            ) {
                // The body of the request cannot be recalled so logging the body will require us to buffer it
                $request->getParams()->set('request_wire', EntityBody::factory());
            }
            if (!$request->getResponseBody()->isRepeatable()) {
                // The body of the response cannot be recalled so logging the body will require us to buffer it
                $request->getParams()->set('response_wire', EntityBody::factory());
            }
        }
    }

    /**
     * Triggers the actual log write when a request completes
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];
        $handle = $event['handle'];

        if ($wire = $request->getParams()->get('request_wire')) {
            $request = clone $request;
            $request->setBody($wire);
        }

        if ($wire = $request->getParams()->get('response_wire')) {
            $response = clone $response;
            $response->setBody($wire);
        }

        // Send the log message to the adapter, adding a category and host
        $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
        $message = $this->formatter->format($request, $response, $handle);
        $this->logAdapter->log($message, $priority, array(
            'request'  => $request,
            'response' => $response,
            'handle'   => $handle
        ));
    }
}
Log/.htaccess000044400000000424152441445220007064 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Cache/DefaultRevalidation.php000060400000014263152441445220012213 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\BadResponseException;

/**
 * Default revalidation strategy
 */
class DefaultRevalidation implements RevalidationInterface
{
    /** @var CacheStorageInterface Cache object storing cache data */
    protected $storage;

    /** @var CanCacheStrategyInterface */
    protected $canCache;

    /**
     * @param CacheStorageInterface     $cache    Cache storage
     * @param CanCacheStrategyInterface $canCache Determines if a message can be cached
     */
    public function __construct(CacheStorageInterface $cache, CanCacheStrategyInterface $canCache = null)
    {
        $this->storage = $cache;
        $this->canCache = $canCache ?: new DefaultCanCacheStrategy();
    }

    public function revalidate(RequestInterface $request, Response $response)
    {
        try {
            $revalidate = $this->createRevalidationRequest($request, $response);
            $validateResponse = $revalidate->send();
            if ($validateResponse->getStatusCode() == 200) {
                return $this->handle200Response($request, $validateResponse);
            } elseif ($validateResponse->getStatusCode() == 304) {
                return $this->handle304Response($request, $validateResponse, $response);
            }
        } catch (BadResponseException $e) {
            $this->handleBadResponse($e);
        }

        // Other exceptions encountered in the revalidation request are ignored
        // in hopes that sending a request to the origin server will fix it
        return false;
    }

    public function shouldRevalidate(RequestInterface $request, Response $response)
    {
        if ($request->getMethod() != RequestInterface::GET) {
            return false;
        }

        $reqCache = $request->getHeader('Cache-Control');
        $resCache = $response->getHeader('Cache-Control');

        $revalidate = $request->getHeader('Pragma') == 'no-cache' ||
            ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) ||
            ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate')));

        // Use the strong ETag validator if available and the response contains no Cache-Control directive
        if (!$revalidate && !$resCache && $response->hasHeader('ETag')) {
            $revalidate = true;
        }

        return $revalidate;
    }

    /**
     * Handles a bad response when attempting to revalidate
     *
     * @param BadResponseException $e Exception encountered
     *
     * @throws BadResponseException
     */
    protected function handleBadResponse(BadResponseException $e)
    {
        // 404 errors mean the resource no longer exists, so remove from
        // cache, and prevent an additional request by throwing the exception
        if ($e->getResponse()->getStatusCode() == 404) {
            $this->storage->delete($e->getRequest());
            throw $e;
        }
    }

    /**
     * Creates a request to use for revalidation
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response to revalidate
     *
     * @return RequestInterface returns a revalidation request
     */
    protected function createRevalidationRequest(RequestInterface $request, Response $response)
    {
        $revalidate = clone $request;
        $revalidate->removeHeader('Pragma')
            ->removeHeader('Cache-Control')
            ->setHeader('If-Modified-Since', $response->getLastModified() ?: $response->getDate());

        if ($response->getEtag()) {
            $revalidate->setHeader('If-None-Match', $response->getEtag());
        }

        // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations
        $dispatcher = $revalidate->getEventDispatcher();
        foreach ($dispatcher->getListeners() as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                if (is_array($listener) && $listener[0] instanceof CachePlugin) {
                    $dispatcher->removeListener($eventName, $listener);
                }
            }
        }

        return $revalidate;
    }

    /**
     * Handles a 200 response response from revalidating. The server does not support validation, so use this response.
     *
     * @param RequestInterface $request          Request that was sent
     * @param Response         $validateResponse Response received
     *
     * @return bool Returns true if valid, false if invalid
     */
    protected function handle200Response(RequestInterface $request, Response $validateResponse)
    {
        $request->setResponse($validateResponse);
        if ($this->canCache->canCacheResponse($validateResponse)) {
            $this->storage->cache($request, $validateResponse);
        }

        return false;
    }

    /**
     * Handle a 304 response and ensure that it is still valid
     *
     * @param RequestInterface $request          Request that was sent
     * @param Response         $validateResponse Response received
     * @param Response         $response         Original cached response
     *
     * @return bool Returns true if valid, false if invalid
     */
    protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response)
    {
        static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified');

        // Make sure that this response has the same ETag
        if ($validateResponse->getEtag() != $response->getEtag()) {
            return false;
        }

        // Replace cached headers with any of these headers from the
        // origin server that might be more up to date
        $modified = false;
        foreach ($replaceHeaders as $name) {
            if ($validateResponse->hasHeader($name)) {
                $modified = true;
                $response->setHeader($name, $validateResponse->getHeader($name));
            }
        }

        // Store the updated response in cache
        if ($modified && $this->canCache->canCacheResponse($response)) {
            $this->storage->cache($request, $response);
        }

        return true;
    }
}
Cache/SkipRevalidation.php000060400000000626152441445220011533 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Never performs cache revalidation and just assumes the request is still ok
 */
class SkipRevalidation extends DefaultRevalidation
{
    public function __construct() {}

    public function revalidate(RequestInterface $request, Response $response)
    {
        return true;
    }
}
Cache/DefaultCanCacheStrategy.php000060400000001614152441445220012736 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default strategy used to determine of an HTTP request can be cached
 */
class DefaultCanCacheStrategy implements CanCacheStrategyInterface
{
    public function canCacheRequest(RequestInterface $request)
    {
        // Only GET and HEAD requests can be cached
        if ($request->getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) {
            return false;
        }

        // Never cache requests when using no-store
        if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) {
            return false;
        }

        return true;
    }

    public function canCacheResponse(Response $response)
    {
        return $response->isSuccessful() && $response->canCache();
    }
}
Cache/composer.json000060400000001301152441445220010263 0ustar00{
    "name": "guzzle/plugin-cache",
    "description": "Guzzle HTTP cache plugin",
    "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/cache": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Cache": "" }
    },
    "target-dir": "Guzzle/Plugin/Cache",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Cache/DefaultCacheStorage.php000060400000017462152441445220012126 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Cache\CacheAdapterFactory;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Message\MessageInterface;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default cache storage implementation
 */
class DefaultCacheStorage implements CacheStorageInterface
{
    /** @var string */
    protected $keyPrefix;

    /** @var CacheAdapterInterface Cache used to store cache data */
    protected $cache;

    /** @var int Default cache TTL */
    protected $defaultTtl;

    /**
     * @param mixed  $cache      Cache used to store cache data
     * @param string $keyPrefix  Provide an optional key prefix to prefix on all cache keys
     * @param int    $defaultTtl Default cache TTL
     */
    public function __construct($cache, $keyPrefix = '', $defaultTtl = 3600)
    {
        $this->cache = CacheAdapterFactory::fromCache($cache);
        $this->defaultTtl = $defaultTtl;
        $this->keyPrefix = $keyPrefix;
    }

    public function cache(RequestInterface $request, Response $response)
    {
        $currentTime = time();
        $ttl = $request->getParams()->get('cache.override_ttl') ?: $response->getMaxAge() ?: $this->defaultTtl;

        if ($cacheControl = $response->getHeader('Cache-Control')) {
            $stale = $cacheControl->getDirective('stale-if-error');
            $ttl += $stale == true ? $ttl : $stale;
        }

        // Determine which manifest key should be used
        $key = $this->getCacheKey($request);
        $persistedRequest = $this->persistHeaders($request);
        $entries = array();

        if ($manifest = $this->cache->fetch($key)) {
            // Determine which cache entries should still be in the cache
            $vary = $response->getVary();
            foreach (unserialize($manifest) as $entry) {
                // Check if the entry is expired
                if ($entry[4] < $currentTime) {
                    continue;
                }
                $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
                if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) {
                    $entries[] = $entry;
                }
            }
        }

        // Persist the response body if needed
        $bodyDigest = null;
        if ($response->getBody() && $response->getBody()->getContentLength() > 0) {
            $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody());
            $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl);
        }

        array_unshift($entries, array(
            $persistedRequest,
            $this->persistHeaders($response),
            $response->getStatusCode(),
            $bodyDigest,
            $currentTime + $ttl
        ));

        $this->cache->save($key, serialize($entries));
    }

    public function delete(RequestInterface $request)
    {
        $key = $this->getCacheKey($request);
        if ($entries = $this->cache->fetch($key)) {
            // Delete each cached body
            foreach (unserialize($entries) as $entry) {
                if ($entry[3]) {
                    $this->cache->delete($entry[3]);
                }
            }
            $this->cache->delete($key);
        }
    }

    public function purge($url)
    {
        foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) {
            $this->delete(new Request($method, $url));
        }
    }

    public function fetch(RequestInterface $request)
    {
        $key = $this->getCacheKey($request);
        if (!($entries = $this->cache->fetch($key))) {
            return null;
        }

        $match = null;
        $headers = $this->persistHeaders($request);
        $entries = unserialize($entries);
        foreach ($entries as $index => $entry) {
            if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) {
                $match = $entry;
                break;
            }
        }

        if (!$match) {
            return null;
        }

        // Ensure that the response is not expired
        $response = null;
        if ($match[4] < time()) {
            $response = -1;
        } else {
            $response = new Response($match[2], $match[1]);
            if ($match[3]) {
                if ($body = $this->cache->fetch($match[3])) {
                    $response->setBody($body);
                } else {
                    // The response is not valid because the body was somehow deleted
                    $response = -1;
                }
            }
        }

        if ($response === -1) {
            // Remove the entry from the metadata and update the cache
            unset($entries[$index]);
            if ($entries) {
                $this->cache->save($key, serialize($entries));
            } else {
                $this->cache->delete($key);
            }
            return null;
        }

        return $response;
    }

    /**
     * Hash a request URL into a string that returns cache metadata
     *
     * @param RequestInterface $request
     *
     * @return string
     */
    protected function getCacheKey(RequestInterface $request)
    {
        // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth)
        if ($filter = $request->getParams()->get('cache.key_filter')) {
            $url = $request->getUrl(true);
            foreach (explode(',', $filter) as $remove) {
                $url->getQuery()->remove(trim($remove));
            }
        } else {
            $url = $request->getUrl();
        }

        return $this->keyPrefix . md5($request->getMethod() . ' ' . $url);
    }

    /**
     * Create a cache key for a response's body
     *
     * @param string              $url  URL of the entry
     * @param EntityBodyInterface $body Response body
     *
     * @return string
     */
    protected function getBodyKey($url, EntityBodyInterface $body)
    {
        return $this->keyPrefix . md5($url) . $body->getContentMd5();
    }

    /**
     * Determines whether two Request HTTP header sets are non-varying
     *
     * @param string $vary Response vary header
     * @param array  $r1   HTTP header array
     * @param array  $r2   HTTP header array
     *
     * @return bool
     */
    private function requestsMatch($vary, $r1, $r2)
    {
        if ($vary) {
            foreach (explode(',', $vary) as $header) {
                $key = trim(strtolower($header));
                $v1 = isset($r1[$key]) ? $r1[$key] : null;
                $v2 = isset($r2[$key]) ? $r2[$key] : null;
                if ($v1 !== $v2) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Creates an array of cacheable and normalized message headers
     *
     * @param MessageInterface $message
     *
     * @return array
     */
    private function persistHeaders(MessageInterface $message)
    {
        // Headers are excluded from the caching (see RFC 2616:13.5.1)
        static $noCache = array(
            'age' => true,
            'connection' => true,
            'keep-alive' => true,
            'proxy-authenticate' => true,
            'proxy-authorization' => true,
            'te' => true,
            'trailers' => true,
            'transfer-encoding' => true,
            'upgrade' => true,
            'set-cookie' => true,
            'set-cookie2' => true
        );

        // Clone the response to not destroy any necessary headers when caching
        $headers = $message->getHeaders()->getAll();
        $headers = array_diff_key($headers, $noCache);
        // Cast the headers to a string
        $headers = array_map(function ($h) { return (string) $h; }, $headers);

        return $headers;
    }
}
Cache/CacheKeyProviderInterface.php000060400000000365152441445220013273 0ustar00<?php

namespace Guzzle\Plugin\Cache;

\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\CacheKeyProviderInterface is no longer used');

/**
 * @deprecated This is no longer used
 * @codeCoverageIgnore
 */
interface CacheKeyProviderInterface {}
Cache/RevalidationInterface.php000060400000001516152441445220012524 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Cache revalidation interface
 */
interface RevalidationInterface
{
    /**
     * Performs a cache revalidation
     *
     * @param RequestInterface $request    Request to revalidate
     * @param Response         $response   Response that was received
     *
     * @return bool Returns true if the request can be cached
     */
    public function revalidate(RequestInterface $request, Response $response);

    /**
     * Returns true if the response should be revalidated
     *
     * @param RequestInterface $request  Request to check
     * @param Response         $response Response to check
     *
     * @return bool
     */
    public function shouldRevalidate(RequestInterface $request, Response $response);
}
Cache/DenyRevalidation.php000060400000000626152441445220011524 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Never performs cache revalidation and just assumes the request is invalid
 */
class DenyRevalidation extends DefaultRevalidation
{
    public function __construct() {}

    public function revalidate(RequestInterface $request, Response $response)
    {
        return false;
    }
}
Cache/DefaultCacheKeyProvider.php000060400000002736152441445220012763 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;

\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\DefaultCacheKeyProvider is no longer used');

/**
 * @deprecated This class is no longer used
 * @codeCoverageIgnore
 */
class DefaultCacheKeyProvider implements CacheKeyProviderInterface
{
    public function getCacheKey(RequestInterface $request)
    {
        // See if the key has already been calculated
        $key = $request->getParams()->get(self::CACHE_KEY);

        if (!$key) {

            $cloned = clone $request;
            $cloned->removeHeader('Cache-Control');

            // Check to see how and if the key should be filtered
            foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) {
                $pieces = array_map('trim', explode('=', $part));
                if (isset($pieces[1])) {
                    foreach (array_map('trim', explode(',', $pieces[1])) as $remove) {
                        if ($pieces[0] == 'header') {
                            $cloned->removeHeader($remove);
                        } elseif ($pieces[0] == 'query') {
                            $cloned->getQuery()->remove($remove);
                        }
                    }
                }
            }

            $raw = (string) $cloned;
            $key = 'GZ' . md5($raw);
            $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw);
        }

        return $key;
    }
}
Cache/CachePlugin.php000060400000031472152441445220010450 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Cache\CacheAdapterFactory;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Common\Event;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Version;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Http\Exception\CurlException;
use Doctrine\Common\Cache\ArrayCache;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to enable the caching of GET and HEAD requests.  Caching can be done on all requests passing through this
 * plugin or only after retrieving resources with cacheable response headers.
 *
 * This is a simple implementation of RFC 2616 and should be considered a private transparent proxy cache, meaning
 * authorization and private data can be cached.
 *
 * It also implements RFC 5861's `stale-if-error` Cache-Control extension, allowing stale cache responses to be used
 * when an error is encountered (such as a `500 Internal Server Error` or DNS failure).
 */
class CachePlugin implements EventSubscriberInterface
{
    /** @var RevalidationInterface Cache revalidation strategy */
    protected $revalidation;

    /** @var CanCacheStrategyInterface Object used to determine if a request can be cached */
    protected $canCache;

    /** @var CacheStorageInterface $cache Object used to cache responses */
    protected $storage;

    /** @var bool */
    protected $autoPurge;

    /**
     * @param array|CacheAdapterInterface|CacheStorageInterface $options Array of options for the cache plugin,
     *     cache adapter, or cache storage object.
     *     - CacheStorageInterface storage:      Adapter used to cache responses
     *     - RevalidationInterface revalidation: Cache revalidation strategy
     *     - CanCacheInterface     can_cache:    Object used to determine if a request can be cached
     *     - bool                  auto_purge    Set to true to automatically PURGE resources when non-idempotent
     *                                           requests are sent to a resource. Defaults to false.
     * @throws InvalidArgumentException if no cache is provided and Doctrine cache is not installed
     */
    public function __construct($options = null)
    {
        if (!is_array($options)) {
            if ($options instanceof CacheAdapterInterface) {
                $options = array('storage' => new DefaultCacheStorage($options));
            } elseif ($options instanceof CacheStorageInterface) {
                $options = array('storage' => $options);
            } elseif ($options) {
                $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options)));
            } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) {
                // @codeCoverageIgnoreStart
                throw new InvalidArgumentException('No cache was provided and Doctrine is not installed');
                // @codeCoverageIgnoreEnd
            }
        }

        $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false;

        // Add a cache storage if a cache adapter was provided
        $this->storage = isset($options['storage'])
            ? $options['storage']
            : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()));

        if (!isset($options['can_cache'])) {
            $this->canCache = new DefaultCanCacheStrategy();
        } else {
            $this->canCache = is_callable($options['can_cache'])
                ? new CallbackCanCacheStrategy($options['can_cache'])
                : $options['can_cache'];
        }

        // Use the provided revalidation strategy or the default
        $this->revalidation = isset($options['revalidation'])
            ? $options['revalidation']
            : new DefaultRevalidation($this->storage, $this->canCache);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', -255),
            'request.sent'        => array('onRequestSent', 255),
            'request.error'       => array('onRequestError', 0),
            'request.exception'   => array('onRequestException', 0),
        );
    }

    /**
     * Check if a response in cache will satisfy the request before sending
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        $request = $event['request'];
        $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));

        if (!$this->canCache->canCacheRequest($request)) {
            switch ($request->getMethod()) {
                case 'PURGE':
                    $this->purge($request);
                    $request->setResponse(new Response(200, array(), 'purged'));
                    break;
                case 'PUT':
                case 'POST':
                case 'DELETE':
                case 'PATCH':
                    if ($this->autoPurge) {
                        $this->purge($request);
                    }
            }
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $params = $request->getParams();
            $params['cache.lookup'] = true;
            $response->setHeader(
                'Age',
                time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now')
            );
            // Validate that the response satisfies the request
            if ($this->canResponseSatisfyRequest($request, $response)) {
                if (!isset($params['cache.hit'])) {
                    $params['cache.hit'] = true;
                }
                $request->setResponse($response);
            }
        }
    }

    /**
     * If possible, store a response in cache after sending
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];

        if ($request->getParams()->get('cache.hit') === null &&
            $this->canCache->canCacheRequest($request) &&
            $this->canCache->canCacheResponse($response)
        ) {
            $this->storage->cache($request, $response);
        }

        $this->addResponseHeaders($request, $response);
    }

    /**
     * If possible, return a cache response on an error
     *
     * @param Event $event
     */
    public function onRequestError(Event $event)
    {
        $request = $event['request'];

        if (!$this->canCache->canCacheRequest($request)) {
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $response->setHeader(
                'Age',
                time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now')
            );

            if ($this->canResponseSatisfyFailedRequest($request, $response)) {
                $request->getParams()->set('cache.hit', 'error');
                $this->addResponseHeaders($request, $response);
                $event['response'] = $response;
                $event->stopPropagation();
            }
        }
    }

    /**
     * If possible, set a cache response on a cURL exception
     *
     * @param Event $event
     *
     * @return null
     */
    public function onRequestException(Event $event)
    {
        if (!$event['exception'] instanceof CurlException) {
            return;
        }

        $request = $event['request'];
        if (!$this->canCache->canCacheRequest($request)) {
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now'));
            if (!$this->canResponseSatisfyFailedRequest($request, $response)) {
                return;
            }
            $request->getParams()->set('cache.hit', 'error');
            $request->setResponse($response);
            $this->addResponseHeaders($request, $response);
            $event->stopPropagation();
        }
    }

    /**
     * Check if a cache response satisfies a request's caching constraints
     *
     * @param RequestInterface $request  Request to validate
     * @param Response         $response Response to validate
     *
     * @return bool
     */
    public function canResponseSatisfyRequest(RequestInterface $request, Response $response)
    {
        $responseAge = $response->calculateAge();
        $reqc = $request->getHeader('Cache-Control');
        $resc = $response->getHeader('Cache-Control');

        // Check the request's max-age header against the age of the response
        if ($reqc && $reqc->hasDirective('max-age') &&
            $responseAge > $reqc->getDirective('max-age')) {
            return false;
        }

        // Check the response's max-age header
        if ($response->isFresh() === false) {
            $maxStale = $reqc ? $reqc->getDirective('max-stale') : null;
            if (null !== $maxStale) {
                if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) {
                    return false;
                }
            } elseif ($resc && $resc->hasDirective('max-age')
                && $responseAge > $resc->getDirective('max-age')
            ) {
                return false;
            }
        }

        if ($this->revalidation->shouldRevalidate($request, $response)) {
            try {
                return $this->revalidation->revalidate($request, $response);
            } catch (CurlException $e) {
                $request->getParams()->set('cache.hit', 'error');
                return $this->canResponseSatisfyFailedRequest($request, $response);
            }
        }

        return true;
    }

    /**
     * Check if a cache response satisfies a failed request's caching constraints
     *
     * @param RequestInterface $request  Request to validate
     * @param Response         $response Response to validate
     *
     * @return bool
     */
    public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response)
    {
        $reqc = $request->getHeader('Cache-Control');
        $resc = $response->getHeader('Cache-Control');
        $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null;
        $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null;

        if (!$requestStaleIfError && !$responseStaleIfError) {
            return false;
        }

        if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) {
            return false;
        }

        if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) {
            return false;
        }

        return true;
    }

    /**
     * Purge all cache entries for a given URL
     *
     * @param string $url URL to purge
     */
    public function purge($url)
    {
        // BC compatibility with previous version that accepted a Request object
        $url = $url instanceof RequestInterface ? $url->getUrl() : $url;
        $this->storage->purge($url);
    }

    /**
     * Add the plugin's headers to a response
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response to add headers to
     */
    protected function addResponseHeaders(RequestInterface $request, Response $response)
    {
        $params = $request->getParams();
        $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));

        $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache';
        if ($header = $response->getHeader('X-Cache-Lookup')) {
            // Don't add duplicates
            $values = $header->toArray();
            $values[] = $lookup;
            $response->setHeader('X-Cache-Lookup', array_unique($values));
        } else {
            $response->setHeader('X-Cache-Lookup', $lookup);
        }

        if ($params['cache.hit'] === true) {
            $xcache = 'HIT from GuzzleCache';
        } elseif ($params['cache.hit'] == 'error') {
            $xcache = 'HIT_ERROR from GuzzleCache';
        } else {
            $xcache = 'MISS from GuzzleCache';
        }

        if ($header = $response->getHeader('X-Cache')) {
            // Don't add duplicates
            $values = $header->toArray();
            $values[] = $xcache;
            $response->setHeader('X-Cache', array_unique($values));
        } else {
            $response->setHeader('X-Cache', $xcache);
        }

        if ($response->isFresh() === false) {
            $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION));
            if ($params['cache.hit'] === 'error') {
                $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION));
            }
        }
    }
}
Cache/.htaccess000044400000000424152441445220007346 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Cache/CallbackCanCacheStrategy.php000060400000003203152441445220013042 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Determines if a request can be cached using a callback
 */
class CallbackCanCacheStrategy extends DefaultCanCacheStrategy
{
    /** @var callable Callback for request */
    protected $requestCallback;

    /** @var callable Callback for response */
    protected $responseCallback;

    /**
     * @param \Closure|array|mixed $requestCallback  Callable method to invoke for requests
     * @param \Closure|array|mixed $responseCallback Callable method to invoke for responses
     *
     * @throws InvalidArgumentException
     */
    public function __construct($requestCallback = null, $responseCallback = null)
    {
        if ($requestCallback && !is_callable($requestCallback)) {
            throw new InvalidArgumentException('Method must be callable');
        }

        if ($responseCallback && !is_callable($responseCallback)) {
            throw new InvalidArgumentException('Method must be callable');
        }

        $this->requestCallback = $requestCallback;
        $this->responseCallback = $responseCallback;
    }

    public function canCacheRequest(RequestInterface $request)
    {
        return $this->requestCallback
            ? call_user_func($this->requestCallback, $request)
            : parent::canCacheRequest($request);
    }

    public function canCacheResponse(Response $response)
    {
        return $this->responseCallback
            ? call_user_func($this->responseCallback, $response)
            : parent::canCacheResponse($response);
    }
}
Cache/CanCacheStrategyInterface.php000060400000001211152441445220013243 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Strategy used to determine if a request can be cached
 */
interface CanCacheStrategyInterface
{
    /**
     * Determine if a request can be cached
     *
     * @param RequestInterface $request Request to determine
     *
     * @return bool
     */
    public function canCacheRequest(RequestInterface $request);

    /**
     * Determine if a response can be cached
     *
     * @param Response $response Response to determine
     *
     * @return bool
     */
    public function canCacheResponse(Response $response);
}
Cache/CacheStorageInterface.php000060400000001725152441445220012435 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to cache HTTP requests
 */
interface CacheStorageInterface
{
    /**
     * Get a Response from the cache for a request
     *
     * @param RequestInterface $request
     *
     * @return null|Response
     */
    public function fetch(RequestInterface $request);

    /**
     * Cache an HTTP request
     *
     * @param RequestInterface $request  Request being cached
     * @param Response         $response Response to cache
     */
    public function cache(RequestInterface $request, Response $response);

    /**
     * Deletes cache entries that match a request
     *
     * @param RequestInterface $request Request to delete from cache
     */
    public function delete(RequestInterface $request);

    /**
     * Purge all cache entries for a given URL
     *
     * @param string $url
     */
    public function purge($url);
}
Oauth/.htaccess000044400000000424152441445220007423 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Oauth/composer.json000060400000001235152441445220010346 0ustar00{
    "name": "guzzle/plugin-oauth",
    "description": "Guzzle OAuth plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["oauth", "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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Oauth": "" }
    },
    "target-dir": "Guzzle/Plugin/Oauth",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Oauth/OauthPlugin.php000060400000023757152441445220010611 0ustar00<?php

namespace Guzzle\Plugin\Oauth;

use Guzzle\Common\Event;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\Url;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * OAuth signing plugin
 * @link http://oauth.net/core/1.0/#rfc.section.9.1.1
 */
class OauthPlugin implements EventSubscriberInterface
{
    /**
     * Consumer request method constants. See http://oauth.net/core/1.0/#consumer_req_param
     */
    const REQUEST_METHOD_HEADER = 'header';
    const REQUEST_METHOD_QUERY  = 'query';

    /** @var Collection Configuration settings */
    protected $config;

    /**
     * Create a new OAuth 1.0 plugin
     *
     * @param array $config Configuration array containing these parameters:
     *     - string 'request_method'       Consumer request method. Use the class constants.
     *     - string 'callback'             OAuth callback
     *     - string 'consumer_key'         Consumer key
     *     - string 'consumer_secret'      Consumer secret
     *     - string 'token'                Token
     *     - string 'token_secret'         Token secret
     *     - string 'verifier'             OAuth verifier.
     *     - string 'version'              OAuth version.  Defaults to 1.0
     *     - string 'signature_method'     Custom signature method
     *     - bool   'disable_post_params'  Set to true to prevent POST parameters from being signed
     *     - array|Closure 'signature_callback' Custom signature callback that accepts a string to sign and a signing key
     */
    public function __construct($config)
    {
        $this->config = Collection::fromConfig($config, array(
            'version' => '1.0',
            'request_method' => self::REQUEST_METHOD_HEADER,
            'consumer_key' => 'anonymous',
            'consumer_secret' => 'anonymous',
            'signature_method' => 'HMAC-SHA1',
            'signature_callback' => function($stringToSign, $key) {
                return hash_hmac('sha1', $stringToSign, $key, true);
            }
        ), array(
            'signature_method', 'signature_callback', 'version',
            'consumer_key', 'consumer_secret'
        ));
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', -1000)
        );
    }

    /**
     * Request before-send event handler
     *
     * @param Event $event Event received
     * @return array
     * @throws \InvalidArgumentException
     */
    public function onRequestBeforeSend(Event $event)
    {
        $timestamp = $this->getTimestamp($event);
        $request = $event['request'];
        $nonce = $this->generateNonce($request);
        $authorizationParams = $this->getOauthParams($timestamp, $nonce);
        $authorizationParams['oauth_signature']  = $this->getSignature($request, $timestamp, $nonce);

        switch ($this->config['request_method']) {
            case self::REQUEST_METHOD_HEADER:
                $request->setHeader(
                    'Authorization',
                    $this->buildAuthorizationHeader($authorizationParams)
                );
                break;
            case self::REQUEST_METHOD_QUERY:
                foreach ($authorizationParams as $key => $value) {
                    $request->getQuery()->set($key, $value);
                }
                break;
            default:
                throw new \InvalidArgumentException(sprintf(
                    'Invalid consumer method "%s"',
                    $this->config['request_method']
                ));
        }

        return $authorizationParams;
    }

    /**
     * Builds the Authorization header for a request
     *
     * @param array $authorizationParams Associative array of authorization parameters
     *
     * @return string
     */
    private function buildAuthorizationHeader($authorizationParams)
    {
        $authorizationString = 'OAuth ';
        foreach ($authorizationParams as $key => $val) {
            if ($val) {
                $authorizationString .= $key . '="' . urlencode($val) . '", ';
            }
        }

        return substr($authorizationString, 0, -2);
    }

    /**
     * Calculate signature for request
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param integer          $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return string
     */
    public function getSignature(RequestInterface $request, $timestamp, $nonce)
    {
        $string = $this->getStringToSign($request, $timestamp, $nonce);
        $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);

        return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
    }

    /**
     * Calculate string to sign
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param int              $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return string
     */
    public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
    {
        $params = $this->getParamsToSign($request, $timestamp, $nonce);

        // Convert booleans to strings.
        $params = $this->prepareParameters($params);

        // Build signing string from combined params
        $parameterString = new QueryString($params);

        $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);

        return strtoupper($request->getMethod()) . '&'
             . rawurlencode($url) . '&'
             . rawurlencode((string) $parameterString);
    }

    /**
     * Get the oauth parameters as named by the oauth spec
     *
     * @param $timestamp
     * @param $nonce
     * @return Collection
     */
    protected function getOauthParams($timestamp, $nonce)
    {
        $params = new Collection(array(
            'oauth_consumer_key'     => $this->config['consumer_key'],
            'oauth_nonce'            => $nonce,
            'oauth_signature_method' => $this->config['signature_method'],
            'oauth_timestamp'        => $timestamp,
        ));

        // Optional parameters should not be set if they have not been set in the config as
        // the parameter may be considered invalid by the Oauth service.
        $optionalParams = array(
            'callback'  => 'oauth_callback',
            'token'     => 'oauth_token',
            'verifier'  => 'oauth_verifier',
            'version'   => 'oauth_version'
        );

        foreach ($optionalParams as $optionName => $oauthName) {
            if (isset($this->config[$optionName]) == true) {
                $params[$oauthName] = $this->config[$optionName];
            }
        }

        return $params;
    }

    /**
     * Get all of the parameters required to sign a request including:
     * * The oauth params
     * * The request GET params
     * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param integer          $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return array
     */
    public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
    {
        $params = $this->getOauthParams($timestamp, $nonce);

        // Add query string parameters
        $params->merge($request->getQuery());

        // Add POST fields to signing string if required
        if ($this->shouldPostFieldsBeSigned($request))
        {
            $params->merge($request->getPostFields());
        }

        // Sort params
        $params = $params->toArray();
        ksort($params);

        return $params;
    }

    /**
     * Decide whether the post fields should be added to the base string that Oauth signs.
     * This implementation is correct. Non-conformant APIs may require that this method be
     * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type
     * is 'application/x-www-form-urlencoded'
     *
     * @param $request
     * @return bool Whether the post fields should be signed or not
     */
    public function shouldPostFieldsBeSigned($request)
    {
        if (!$this->config->get('disable_post_params') &&
            $request instanceof EntityEnclosingRequestInterface &&
            false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
        {
            return true;
        }

        return false;
    }

    /**
     * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same
     * exact timestamp to use separate nonce's.
     *
     * @param RequestInterface $request Request to generate a nonce for
     *
     * @return string
     */
    public function generateNonce(RequestInterface $request)
    {
        return sha1(uniqid('', true) . $request->getUrl());
    }

    /**
     * Gets timestamp from event or create new timestamp
     *
     * @param Event $event Event containing contextual information
     *
     * @return int
     */
    public function getTimestamp(Event $event)
    {
       return $event['timestamp'] ?: time();
    }

    /**
     * Convert booleans to strings, removed unset parameters, and sorts the array
     *
     * @param array $data Data array
     *
     * @return array
     */
    protected function prepareParameters($data)
    {
        ksort($data);
        foreach ($data as $key => &$value) {
            switch (gettype($value)) {
                case 'NULL':
                    unset($data[$key]);
                    break;
                case 'array':
                    $data[$key] = self::prepareParameters($value);
                    break;
                case 'boolean':
                    $data[$key] = $value ? 'true' : 'false';
                    break;
            }
        }

        return $data;
    }
}
Md5/CommandContentMd5Plugin.php000060400000004235152441445220012343 0ustar00<?php

namespace Guzzle\Plugin\Md5;

use Guzzle\Common\Event;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to add a ContentMD5 header to the body of a command and adds ContentMD5 validation if the
 * ValidateMD5 option is not set to false on a command
 */
class CommandContentMd5Plugin  implements EventSubscriberInterface
{
    /** @var string Parameter used to check if the ContentMD5 value is being added */
    protected $contentMd5Param;

    /** @var string Parameter used to check if validation should occur on the response */
    protected $validateMd5Param;

    /**
     * @param string $contentMd5Param  Parameter used to check if the ContentMD5 value is being added
     * @param string $validateMd5Param Parameter used to check if validation should occur on the response
     */
    public function __construct($contentMd5Param = 'ContentMD5', $validateMd5Param = 'ValidateMD5')
    {
        $this->contentMd5Param = $contentMd5Param;
        $this->validateMd5Param = $validateMd5Param;
    }

    public static function getSubscribedEvents()
    {
        return array('command.before_send' => array('onCommandBeforeSend', -255));
    }

    public function onCommandBeforeSend(Event $event)
    {
        $command = $event['command'];
        $request = $command->getRequest();

        // Only add an MD5 is there is a MD5 option on the operation and it has a payload
        if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
            && $command->getOperation()->hasParam($this->contentMd5Param)) {
            // Check if an MD5 checksum value should be passed along to the request
            if ($command[$this->contentMd5Param] === true) {
                if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) {
                    $request->setHeader('Content-MD5', $md5);
                }
            }
        }

        // Check if MD5 validation should be used with the response
        if ($command[$this->validateMd5Param] === true) {
            $request->addSubscriber(new Md5ValidatorPlugin(true, false));
        }
    }
}
Md5/composer.json000060400000001215152441445220007711 0ustar00{
    "name": "guzzle/plugin-md5",
    "description": "Guzzle MD5 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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Md5": "" }
    },
    "target-dir": "Guzzle/Plugin/Md5",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Md5/.htaccess000044400000000424152441445220006770 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Md5/Md5ValidatorPlugin.php000060400000006565152441445220011367 0ustar00<?php

namespace Guzzle\Plugin\Md5;

use Guzzle\Common\Event;
use Guzzle\Common\Exception\UnexpectedValueException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Ensures that an the MD5 hash of an entity body matches the Content-MD5
 * header (if set) of an HTTP response.  An exception is thrown if the
 * calculated MD5 does not match the expected MD5.
 */
class Md5ValidatorPlugin implements EventSubscriberInterface
{
    /** @var int Maximum Content-Length in bytes to validate */
    protected $contentLengthCutoff;

    /** @var bool Whether or not to compare when a Content-Encoding is present */
    protected $contentEncoded;

    /**
     * @param bool     $contentEncoded      Calculating the MD5 hash of an entity body where a Content-Encoding was
     *                                      applied is a more expensive comparison because the entity body will need to
     *                                      be compressed in order to get the correct hash.  Set to FALSE to not
     *                                      validate the MD5 hash of an entity body with an applied Content-Encoding.
     * @param bool|int $contentLengthCutoff Maximum Content-Length (bytes) in which a MD5 hash will be validated. Any
     *                                      response with a Content-Length greater than this value will not be validated
     *                                      because it will be deemed too memory intensive.
     */
    public function __construct($contentEncoded = true, $contentLengthCutoff = false)
    {
        $this->contentLengthCutoff = $contentLengthCutoff;
        $this->contentEncoded = $contentEncoded;
    }

    public static function getSubscribedEvents()
    {
        return array('request.complete' => array('onRequestComplete', 255));
    }

    /**
     * {@inheritdoc}
     * @throws UnexpectedValueException
     */
    public function onRequestComplete(Event $event)
    {
        $response = $event['response'];

        if (!$contentMd5 = $response->getContentMd5()) {
            return;
        }

        $contentEncoding = $response->getContentEncoding();
        if ($contentEncoding && !$this->contentEncoded) {
            return false;
        }

        // Make sure that the size of the request is under the cutoff size
        if ($this->contentLengthCutoff) {
            $size = $response->getContentLength() ?: $response->getBody()->getSize();
            if (!$size || $size > $this->contentLengthCutoff) {
                return;
            }
        }

        if (!$contentEncoding) {
            $hash = $response->getBody()->getContentMd5();
        } elseif ($contentEncoding == 'gzip') {
            $response->getBody()->compress('zlib.deflate');
            $hash = $response->getBody()->getContentMd5();
            $response->getBody()->uncompress();
        } elseif ($contentEncoding == 'compress') {
            $response->getBody()->compress('bzip2.compress');
            $hash = $response->getBody()->getContentMd5();
            $response->getBody()->uncompress();
        } else {
            return;
        }

        if ($contentMd5 !== $hash) {
            throw new UnexpectedValueException(
                "The response entity body may have been modified over the wire.  The Content-MD5 "
                . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})."
            );
        }
    }
}
.htaccess000044400000000424152441445220006343 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>CurlAuth/CurlAuthPlugin.php000060400000002554152441445220011717 0ustar00<?php

namespace Guzzle\Plugin\CurlAuth;

use Guzzle\Common\Event;
use Guzzle\Common\Version;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds specified curl auth to all requests sent from a client. Defaults to CURLAUTH_BASIC if none supplied.
 * @deprecated Use $client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');
 */
class CurlAuthPlugin implements EventSubscriberInterface
{
    private $username;
    private $password;
    private $scheme;

    /**
     * @param string $username HTTP basic auth username
     * @param string $password Password
     * @param int    $scheme   Curl auth scheme
     */
    public function __construct($username, $password, $scheme=CURLAUTH_BASIC)
    {
        Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');");
        $this->username = $username;
        $this->password = $password;
        $this->scheme = $scheme;
    }

    public static function getSubscribedEvents()
    {
        return array('client.create_request' => array('onRequestCreate', 255));
    }

    /**
     * Add basic auth
     *
     * @param Event $event
     */
    public function onRequestCreate(Event $event)
    {
        $event['request']->setAuth($this->username, $this->password, $this->scheme);
    }
}
CurlAuth/composer.json000060400000001262152441445220011015 0ustar00{
    "name": "guzzle/plugin-curlauth",
    "description": "Guzzle cURL authorization plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "curl", "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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" }
    },
    "target-dir": "Guzzle/Plugin/CurlAuth",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
CurlAuth/.htaccess000044400000000424152441445220010072 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>History/HistoryPlugin.php000060400000007521152441445220011542 0ustar00<?php

namespace Guzzle\Plugin\History;

use Guzzle\Common\Event;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Maintains a list of requests and responses sent using a request or client
 */
class HistoryPlugin implements EventSubscriberInterface, \IteratorAggregate, \Countable
{
    /** @var int The maximum number of requests to maintain in the history */
    protected $limit = 10;

    /** @var array Requests and responses that have passed through the plugin */
    protected $transactions = array();

    public static function getSubscribedEvents()
    {
        return array('request.sent' => array('onRequestSent', 9999));
    }

    /**
     * Convert to a string that contains all request and response headers
     *
     * @return string
     */
    public function __toString()
    {
        $lines = array();
        foreach ($this->transactions as $entry) {
            $response = isset($entry['response']) ? $entry['response'] : '';
            $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n";
        }

        return implode("\n", $lines);
    }

    /**
     * Add a request to the history
     *
     * @param RequestInterface $request  Request to add
     * @param Response         $response Response of the request
     *
     * @return HistoryPlugin
     */
    public function add(RequestInterface $request, Response $response = null)
    {
        if (!$response && $request->getResponse()) {
            $response = $request->getResponse();
        }

        $this->transactions[] = array('request' => $request, 'response' => $response);
        if (count($this->transactions) > $this->getlimit()) {
            array_shift($this->transactions);
        }

        return $this;
    }

    /**
     * Set the max number of requests to store
     *
     * @param int $limit Limit
     *
     * @return HistoryPlugin
     */
    public function setLimit($limit)
    {
        $this->limit = (int) $limit;

        return $this;
    }

    /**
     * Get the request limit
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * Get all of the raw transactions in the form of an array of associative arrays containing
     * 'request' and 'response' keys.
     *
     * @return array
     */
    public function getAll()
    {
        return $this->transactions;
    }

    /**
     * Get the requests in the history
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll())
        return new \ArrayIterator(array_map(function ($entry) {
            $entry['request']->getParams()->set('actual_response', $entry['response']);
            return $entry['request'];
        }, $this->transactions));
    }

    /**
     * Get the number of requests in the history
     *
     * @return int
     */
    public function count()
    {
        return count($this->transactions);
    }

    /**
     * Get the last request sent
     *
     * @return RequestInterface
     */
    public function getLastRequest()
    {
        $last = end($this->transactions);

        return $last['request'];
    }

    /**
     * Get the last response in the history
     *
     * @return Response|null
     */
    public function getLastResponse()
    {
        $last = end($this->transactions);

        return isset($last['response']) ? $last['response'] : null;
    }

    /**
     * Clears the history
     *
     * @return HistoryPlugin
     */
    public function clear()
    {
        $this->transactions = array();

        return $this;
    }

    public function onRequestSent(Event $event)
    {
        $this->add($event['request'], $event['response']);
    }
}
History/.htaccess000044400000000424152441445220010004 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>History/composer.json000060400000001234152441445220010726 0ustar00{
    "name": "guzzle/plugin-history",
    "description": "Guzzle history plugin",
    "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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\History": "" }
    },
    "target-dir": "Guzzle/Plugin/History",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Backoff/TruncatedBackoffStrategy.php000060400000001717152441445220013545 0ustar00<?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;
    }
}
Backoff/BackoffLogger.php000060400000004421152441445220011303 0ustar00<?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']
            )
        ));
    }
}
Backoff/HttpBackoffStrategy.php000060400000001606152441445220012530 0ustar00<?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;
            }
        }
    }
}
Backoff/CurlBackoffStrategy.php000060400000001737152441445220012523 0ustar00<?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;
        }
    }
}
Backoff/.htaccess000044400000000424152441445220007676 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Backoff/composer.json000060400000001311152441445220010614 0ustar00{
    "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"
        }
    }
}
Backoff/AbstractBackoffStrategy.php000060400000006111152441445220013350 0ustar00<?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
    );
}
Backoff/BackoffStrategyInterface.php000060400000001743152441445220013513 0ustar00<?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
    );
}
Backoff/BackoffPlugin.php000060400000010475152441445220011330 0ustar00<?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);
        }
    }
}
Backoff/CallbackBackoffStrategy.php000060400000002764152441445220013313 0ustar00<?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);
    }
}
Backoff/AbstractErrorCodeBackoffStrategy.php000060400000002025152441445220015155 0ustar00<?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;
    }
}
Backoff/ConstantBackoffStrategy.php000060400000001607152441445220013403 0ustar00<?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;
    }
}
Backoff/LinearBackoffStrategy.php000060400000001615152441445220013023 0ustar00<?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;
    }
}
Backoff/ReasonPhraseBackoffStrategy.php000060400000001256152441445220014204 0ustar00<?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;
        }
    }
}
Backoff/ExponentialBackoffStrategy.php000060400000001216152441445220014074 0ustar00<?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);
    }
}
composer.json000060400000002524152441445220007270 0ustar00{
    "name": "guzzle/plugin",
    "description": "Guzzle plugin component containing all Guzzle HTTP plugins",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["http", "client", "plugin", "extension", "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"
    },
    "suggest": {
        "guzzle/cache": "self.version",
        "guzzle/log": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin": "" }
    },
    "target-dir": "Guzzle/Plugin",
    "replace": {
        "guzzle/plugin-async": "self.version",
        "guzzle/plugin-backoff": "self.version",
        "guzzle/plugin-cache": "self.version",
        "guzzle/plugin-cookie": "self.version",
        "guzzle/plugin-curlauth": "self.version",
        "guzzle/plugin-error-response": "self.version",
        "guzzle/plugin-history": "self.version",
        "guzzle/plugin-log": "self.version",
        "guzzle/plugin-md5": "self.version",
        "guzzle/plugin-mock": "self.version",
        "guzzle/plugin-oauth": "self.version"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Mock/composer.json000060400000001230152441445220010152 0ustar00{
    "name": "guzzle/plugin-mock",
    "description": "Guzzle Mock plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["mock", "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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Mock": "" }
    },
    "target-dir": "Guzzle/Plugin/Mock",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Mock/MockPlugin.php000060400000015353152441445220010224 0ustar00<?php

namespace Guzzle\Plugin\Mock;

use Guzzle\Common\Event;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Queues mock responses or exceptions and delivers mock responses or exceptions in a fifo order.
 */
class MockPlugin extends AbstractHasDispatcher implements EventSubscriberInterface, \Countable
{
    /** @var array Array of mock responses / exceptions */
    protected $queue = array();

    /** @var bool Whether or not to remove the plugin when the queue is empty */
    protected $temporary = false;

    /** @var array Array of requests that were mocked */
    protected $received = array();

    /** @var bool Whether or not to consume an entity body when a mock response is served */
    protected $readBodies;

    /**
     * @param array $items      Array of responses or exceptions to queue
     * @param bool  $temporary  Set to TRUE to remove the plugin when the queue is empty
     * @param bool  $readBodies Set to TRUE to consume the entity body when a mock is served
     */
    public function __construct(array $items = null, $temporary = false, $readBodies = false)
    {
        $this->readBodies = $readBodies;
        $this->temporary = $temporary;
        if ($items) {
            foreach ($items as $item) {
                if ($item instanceof \Exception) {
                    $this->addException($item);
                } else {
                    $this->addResponse($item);
                }
            }
        }
    }

    public static function getSubscribedEvents()
    {
        // Use a number lower than the CachePlugin
        return array('request.before_send' => array('onRequestBeforeSend', -999));
    }

    public static function getAllEvents()
    {
        return array('mock.request');
    }

    /**
     * Get a mock response from a file
     *
     * @param string $path File to retrieve a mock response from
     *
     * @return Response
     * @throws InvalidArgumentException if the file is not found
     */
    public static function getMockFile($path)
    {
        if (!file_exists($path)) {
            throw new InvalidArgumentException('Unable to open mock file: ' . $path);
        }

        return Response::fromMessage(file_get_contents($path));
    }

    /**
     * Set whether or not to consume the entity body of a request when a mock
     * response is used
     *
     * @param bool $readBodies Set to true to read and consume entity bodies
     *
     * @return self
     */
    public function readBodies($readBodies)
    {
        $this->readBodies = $readBodies;

        return $this;
    }

    /**
     * Returns the number of remaining mock responses
     *
     * @return int
     */
    public function count()
    {
        return count($this->queue);
    }

    /**
     * Add a response to the end of the queue
     *
     * @param string|Response $response Response object or path to response file
     *
     * @return MockPlugin
     * @throws InvalidArgumentException if a string or Response is not passed
     */
    public function addResponse($response)
    {
        if (!($response instanceof Response)) {
            if (!is_string($response)) {
                throw new InvalidArgumentException('Invalid response');
            }
            $response = self::getMockFile($response);
        }

        $this->queue[] = $response;

        return $this;
    }

    /**
     * Add an exception to the end of the queue
     *
     * @param CurlException $e Exception to throw when the request is executed
     *
     * @return MockPlugin
     */
    public function addException(CurlException $e)
    {
        $this->queue[] = $e;

        return $this;
    }

    /**
     * Clear the queue
     *
     * @return MockPlugin
     */
    public function clearQueue()
    {
        $this->queue = array();

        return $this;
    }

    /**
     * Returns an array of mock responses remaining in the queue
     *
     * @return array
     */
    public function getQueue()
    {
        return $this->queue;
    }

    /**
     * Check if this is a temporary plugin
     *
     * @return bool
     */
    public function isTemporary()
    {
        return $this->temporary;
    }

    /**
     * Get a response from the front of the list and add it to a request
     *
     * @param RequestInterface $request Request to mock
     *
     * @return self
     * @throws CurlException When request.send is called and an exception is queued
     */
    public function dequeue(RequestInterface $request)
    {
        $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request));

        $item = array_shift($this->queue);
        if ($item instanceof Response) {
            if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) {
                $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) {
                    while ($data = $event['request']->getBody()->read(8096));
                    // Remove the listener after one-time use
                    $event['request']->getEventDispatcher()->removeListener('request.sent', $f);
                });
            }
            $request->setResponse($item);
        } elseif ($item instanceof CurlException) {
            // Emulates exceptions encountered while transferring requests
            $item->setRequest($request);
            $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item));
            // Only throw if the exception wasn't handled
            if ($state == RequestInterface::STATE_ERROR) {
                throw $item;
            }
        }

        return $this;
    }

    /**
     * Clear the array of received requests
     */
    public function flush()
    {
        $this->received = array();
    }

    /**
     * Get an array of requests that were mocked by this plugin
     *
     * @return array
     */
    public function getReceivedRequests()
    {
        return $this->received;
    }

    /**
     * Called when a request is about to be sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        if ($this->queue) {
            $request = $event['request'];
            $this->received[] = $request;
            // Detach the filter from the client so it's a one-time use
            if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
                $request->getClient()->getEventDispatcher()->removeSubscriber($this);
            }
            $this->dequeue($request);
        }
    }
}
Mock/.htaccess000044400000000424152441445220007234 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>ErrorResponse/composer.json000060400000001365152441445220012102 0ustar00{
    "name": "guzzle/plugin-error-response",
    "description": "Guzzle errorResponse plugin for creating error exceptions based on a service description",
    "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/service": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" }
    },
    "target-dir": "Guzzle/Plugin/ErrorResponse",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
ErrorResponse/ErrorResponsePlugin.php000060400000005366152441445220014065 0ustar00<?php

namespace Guzzle\Plugin\ErrorResponse;

use Guzzle\Common\Event;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Converts generic Guzzle response exceptions into errorResponse exceptions
 */
class ErrorResponsePlugin implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('command.before_send' => array('onCommandBeforeSend', -1));
    }

    /**
     * Adds a listener to requests before they sent from a command
     *
     * @param Event $event Event emitted
     */
    public function onCommandBeforeSend(Event $event)
    {
        $command = $event['command'];
        if ($operation = $command->getOperation()) {
            if ($operation->getErrorResponses()) {
                $request = $command->getRequest();
                $request->getEventDispatcher()
                    ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation));
            }
        }
    }

    /**
     * @param RequestInterface $request   Request that received an error
     * @param CommandInterface $command   Command that created the request
     * @param Operation        $operation Operation that defines the request and errors
     *
     * @return \Closure Returns a closure
     * @throws ErrorResponseException
     */
    protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation)
    {
        return function (Event $event) use ($request, $command, $operation) {
            $response = $event['response'];
            foreach ($operation->getErrorResponses() as $error) {
                if (!isset($error['class'])) {
                    continue;
                }
                if (isset($error['code']) && $response->getStatusCode() != $error['code']) {
                    continue;
                }
                if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) {
                    continue;
                }
                $className = $error['class'];
                $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface';
                if (!class_exists($className)) {
                    throw new ErrorResponseException("{$className} does not exist");
                } elseif (!is_subclass_of($className, $errorClassInterface)) {
                    throw new ErrorResponseException("{$className} must implement {$errorClassInterface}");
                }
                throw $className::fromCommand($command, $response);
            }
        };
    }
}
ErrorResponse/ErrorResponseExceptionInterface.php000060400000001126152441445220016374 0ustar00<?php

namespace Guzzle\Plugin\ErrorResponse;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to create an exception from an error response
 */
interface ErrorResponseExceptionInterface
{
    /**
     * Create an exception for a command based on a command and an error response definition
     *
     * @param CommandInterface $command  Command that was sent
     * @param Response         $response The error response
     *
     * @return self
     */
    public static function fromCommand(CommandInterface $command, Response $response);
}
ErrorResponse/Exception/ErrorResponseException.php000060400000000241152441445220016506 0ustar00<?php

namespace Guzzle\Plugin\ErrorResponse\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ErrorResponseException extends RuntimeException {}
ErrorResponse/Exception/.htaccess000044400000000424152441445220013111 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>ErrorResponse/.htaccess000044400000000424152441445220011153 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Cookie/Cookie.php000060400000026617152441445220007712 0ustar00<?php

namespace Guzzle\Plugin\Cookie;

use Guzzle\Common\ToArrayInterface;

/**
 * Set-Cookie object
 */
class Cookie implements ToArrayInterface
{
    /** @var array Cookie data */
    protected $data;

    /**
     * @var string ASCII codes not valid for for use in a cookie name
     *
     * Cookie names are defined as 'token', according to RFC 2616, Section 2.2
     * A valid token may contain any CHAR except CTLs (ASCII 0 - 31 or 127)
     * or any of the following separators
     */
    protected static $invalidCharString;

    /**
     * Gets an array of invalid cookie characters
     *
     * @return array
     */
    protected static function getInvalidCharacters()
    {
        if (!self::$invalidCharString) {
            self::$invalidCharString = implode('', array_map('chr', array_merge(
                range(0, 32),
                array(34, 40, 41, 44, 47),
                array(58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 123, 125, 127)
            )));
        }

        return self::$invalidCharString;
    }

    /**
     * @param array $data Array of cookie data provided by a Cookie parser
     */
    public function __construct(array $data = array())
    {
        static $defaults = array(
            'name'        => '',
            'value'       => '',
            'domain'      => '',
            'path'        => '/',
            'expires'     => null,
            'max_age'     => 0,
            'comment'     => null,
            'comment_url' => null,
            'port'        => array(),
            'version'     => null,
            'secure'      => false,
            'discard'     => false,
            'http_only'   => false
        );

        $this->data = array_merge($defaults, $data);
        // Extract the expires value and turn it into a UNIX timestamp if needed
        if (!$this->getExpires() && $this->getMaxAge()) {
            // Calculate the expires date
            $this->setExpires(time() + (int) $this->getMaxAge());
        } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
            $this->setExpires(strtotime($this->getExpires()));
        }
    }

    /**
     * Get the cookie as an array
     *
     * @return array
     */
    public function toArray()
    {
        return $this->data;
    }

    /**
     * Get the cookie name
     *
     * @return string
     */
    public function getName()
    {
        return $this->data['name'];
    }

    /**
     * Set the cookie name
     *
     * @param string $name Cookie name
     *
     * @return Cookie
     */
    public function setName($name)
    {
        return $this->setData('name', $name);
    }

    /**
     * Get the cookie value
     *
     * @return string
     */
    public function getValue()
    {
        return $this->data['value'];
    }

    /**
     * Set the cookie value
     *
     * @param string $value Cookie value
     *
     * @return Cookie
     */
    public function setValue($value)
    {
        return $this->setData('value', $value);
    }

    /**
     * Get the domain
     *
     * @return string|null
     */
    public function getDomain()
    {
        return $this->data['domain'];
    }

    /**
     * Set the domain of the cookie
     *
     * @param string $domain
     *
     * @return Cookie
     */
    public function setDomain($domain)
    {
        return $this->setData('domain', $domain);
    }

    /**
     * Get the path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->data['path'];
    }

    /**
     * Set the path of the cookie
     *
     * @param string $path Path of the cookie
     *
     * @return Cookie
     */
    public function setPath($path)
    {
        return $this->setData('path', $path);
    }

    /**
     * Maximum lifetime of the cookie in seconds
     *
     * @return int|null
     */
    public function getMaxAge()
    {
        return $this->data['max_age'];
    }

    /**
     * Set the max-age of the cookie
     *
     * @param int $maxAge Max age of the cookie in seconds
     *
     * @return Cookie
     */
    public function setMaxAge($maxAge)
    {
        return $this->setData('max_age', $maxAge);
    }

    /**
     * The UNIX timestamp when the cookie expires
     *
     * @return mixed
     */
    public function getExpires()
    {
        return $this->data['expires'];
    }

    /**
     * Set the unix timestamp for which the cookie will expire
     *
     * @param int $timestamp Unix timestamp
     *
     * @return Cookie
     */
    public function setExpires($timestamp)
    {
        return $this->setData('expires', $timestamp);
    }

    /**
     * Version of the cookie specification. RFC 2965 is 1
     *
     * @return mixed
     */
    public function getVersion()
    {
        return $this->data['version'];
    }

    /**
     * Set the cookie version
     *
     * @param string|int $version Version to set
     *
     * @return Cookie
     */
    public function setVersion($version)
    {
        return $this->setData('version', $version);
    }

    /**
     * Get whether or not this is a secure cookie
     *
     * @return null|bool
     */
    public function getSecure()
    {
        return $this->data['secure'];
    }

    /**
     * Set whether or not the cookie is secure
     *
     * @param bool $secure Set to true or false if secure
     *
     * @return Cookie
     */
    public function setSecure($secure)
    {
        return $this->setData('secure', (bool) $secure);
    }

    /**
     * Get whether or not this is a session cookie
     *
     * @return null|bool
     */
    public function getDiscard()
    {
        return $this->data['discard'];
    }

    /**
     * Set whether or not this is a session cookie
     *
     * @param bool $discard Set to true or false if this is a session cookie
     *
     * @return Cookie
     */
    public function setDiscard($discard)
    {
        return $this->setData('discard', $discard);
    }

    /**
     * Get the comment
     *
     * @return string|null
     */
    public function getComment()
    {
        return $this->data['comment'];
    }

    /**
     * Set the comment of the cookie
     *
     * @param string $comment Cookie comment
     *
     * @return Cookie
     */
    public function setComment($comment)
    {
        return $this->setData('comment', $comment);
    }

    /**
     * Get the comment URL of the cookie
     *
     * @return string|null
     */
    public function getCommentUrl()
    {
        return $this->data['comment_url'];
    }

    /**
     * Set the comment URL of the cookie
     *
     * @param string $commentUrl Cookie comment URL for more information
     *
     * @return Cookie
     */
    public function setCommentUrl($commentUrl)
    {
        return $this->setData('comment_url', $commentUrl);
    }

    /**
     * Get an array of acceptable ports this cookie can be used with
     *
     * @return array
     */
    public function getPorts()
    {
        return $this->data['port'];
    }

    /**
     * Set a list of acceptable ports this cookie can be used with
     *
     * @param array $ports Array of acceptable ports
     *
     * @return Cookie
     */
    public function setPorts(array $ports)
    {
        return $this->setData('port', $ports);
    }

    /**
     * Get whether or not this is an HTTP only cookie
     *
     * @return bool
     */
    public function getHttpOnly()
    {
        return $this->data['http_only'];
    }

    /**
     * Set whether or not this is an HTTP only cookie
     *
     * @param bool $httpOnly Set to true or false if this is HTTP only
     *
     * @return Cookie
     */
    public function setHttpOnly($httpOnly)
    {
        return $this->setData('http_only', $httpOnly);
    }

    /**
     * Get an array of extra cookie data
     *
     * @return array
     */
    public function getAttributes()
    {
        return $this->data['data'];
    }

    /**
     * Get a specific data point from the extra cookie data
     *
     * @param string $name Name of the data point to retrieve
     *
     * @return null|string
     */
    public function getAttribute($name)
    {
        return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null;
    }

    /**
     * Set a cookie data attribute
     *
     * @param string $name  Name of the attribute to set
     * @param string $value Value to set
     *
     * @return Cookie
     */
    public function setAttribute($name, $value)
    {
        $this->data['data'][$name] = $value;

        return $this;
    }

    /**
     * Check if the cookie matches a path value
     *
     * @param string $path Path to check against
     *
     * @return bool
     */
    public function matchesPath($path)
    {
        return !$this->getPath() || 0 === stripos($path, $this->getPath());
    }

    /**
     * Check if the cookie matches a domain value
     *
     * @param string $domain Domain to check against
     *
     * @return bool
     */
    public function matchesDomain($domain)
    {
        // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
        $cookieDomain = ltrim($this->getDomain(), '.');

        // Domain not set or exact match.
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
            return true;
        }

        // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
            return false;
        }

        return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain);
    }

    /**
     * Check if the cookie is compatible with a specific port
     *
     * @param int $port Port to check
     *
     * @return bool
     */
    public function matchesPort($port)
    {
        return count($this->getPorts()) == 0 || in_array($port, $this->getPorts());
    }

    /**
     * Check if the cookie is expired
     *
     * @return bool
     */
    public function isExpired()
    {
        return $this->getExpires() && time() > $this->getExpires();
    }

    /**
     * Check if the cookie is valid according to RFC 6265
     *
     * @return bool|string Returns true if valid or an error message if invalid
     */
    public function validate()
    {
        // Names must not be empty, but can be 0
        $name = $this->getName();
        if (empty($name) && !is_numeric($name)) {
            return 'The cookie name must not be empty';
        }

        // Check if any of the invalid characters are present in the cookie name
        if (strpbrk($name, self::getInvalidCharacters()) !== false) {
            return 'The cookie name must not contain invalid characters: ' . $name;
        }

        // Value must not be empty, but can be 0
        $value = $this->getValue();
        if (empty($value) && !is_numeric($value)) {
            return 'The cookie value must not be empty';
        }

        // Domains must not be empty, but can be 0
        // A "0" is not a valid internet domain, but may be used as server name in a private network
        $domain = $this->getDomain();
        if (empty($domain) && !is_numeric($domain)) {
            return 'The cookie domain must not be empty';
        }

        return true;
    }

    /**
     * Set a value and return the cookie object
     *
     * @param string $key   Key to set
     * @param string $value Value to set
     *
     * @return Cookie
     */
    private function setData($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }
}
Cookie/.htaccess000044400000000424152441445220007554 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Cookie/CookieJar/.htaccess000044400000000424152441445220011422 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Cookie/CookieJar/ArrayCookieJar.php000060400000015464152441445220013212 0ustar00<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Plugin\Cookie\Cookie;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Plugin\Cookie\Exception\InvalidCookieException;

/**
 * Cookie cookieJar that stores cookies an an array
 */
class ArrayCookieJar implements CookieJarInterface, \Serializable
{
    /** @var array Loaded cookie data */
    protected $cookies = array();

    /** @var bool Whether or not strict mode is enabled. When enabled, exceptions will be thrown for invalid cookies */
    protected $strictMode;

    /**
     * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added to the cookie jar
     */
    public function __construct($strictMode = false)
    {
        $this->strictMode = $strictMode;
    }

    /**
     * Enable or disable strict mode on the cookie jar
     *
     * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them.
     *
     * @return self
     */
    public function setStrictMode($strictMode)
    {
        $this->strictMode = $strictMode;
    }

    public function remove($domain = null, $path = null, $name = null)
    {
        $cookies = $this->all($domain, $path, $name, false, false);
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) {
            return !in_array($cookie, $cookies, true);
        });

        return $this;
    }

    public function removeTemporary()
    {
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) {
            return !$cookie->getDiscard() && $cookie->getExpires();
        });

        return $this;
    }

    public function removeExpired()
    {
        $currentTime = time();
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) {
            return !$cookie->getExpires() || $currentTime < $cookie->getExpires();
        });

        return $this;
    }

    public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true)
    {
        return array_values(array_filter($this->cookies, function (Cookie $cookie) use (
            $domain,
            $path,
            $name,
            $skipDiscardable,
            $skipExpired
        ) {
            return false === (($name && $cookie->getName() != $name) ||
                ($skipExpired && $cookie->isExpired()) ||
                ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) ||
                ($path && !$cookie->matchesPath($path)) ||
                ($domain && !$cookie->matchesDomain($domain)));
        }));
    }

    public function add(Cookie $cookie)
    {
        // Only allow cookies with set and valid domain, name, value
        $result = $cookie->validate();
        if ($result !== true) {
            if ($this->strictMode) {
                throw new InvalidCookieException($result);
            } else {
                return false;
            }
        }

        // Resolve conflicts with previously set cookies
        foreach ($this->cookies as $i => $c) {

            // Two cookies are identical, when their path, domain, port and name are identical
            if ($c->getPath() != $cookie->getPath() ||
                $c->getDomain() != $cookie->getDomain() ||
                $c->getPorts() != $cookie->getPorts() ||
                $c->getName() != $cookie->getName()
            ) {
                continue;
            }

            // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set
            if (!$cookie->getDiscard() && $c->getDiscard()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the new cookie's expiration is further into the future, then replace the old cookie
            if ($cookie->getExpires() > $c->getExpires()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the value has changed, we better change it
            if ($cookie->getValue() !== $c->getValue()) {
                unset($this->cookies[$i]);
                continue;
            }

            // The cookie exists, so no need to continue
            return false;
        }

        $this->cookies[] = $cookie;

        return true;
    }

    /**
     * Serializes the cookie cookieJar
     *
     * @return string
     */
    public function serialize()
    {
        // Only serialize long term cookies and unexpired cookies
        return json_encode(array_map(function (Cookie $cookie) {
            return $cookie->toArray();
        }, $this->all(null, null, null, true, true)));
    }

    /**
     * Unserializes the cookie cookieJar
     */
    public function unserialize($data)
    {
        $data = json_decode($data, true);
        if (empty($data)) {
            $this->cookies = array();
        } else {
            $this->cookies = array_map(function (array $cookie) {
                return new Cookie($cookie);
            }, $data);
        }
    }

    /**
     * Returns the total number of stored cookies
     *
     * @return int
     */
    public function count()
    {
        return count($this->cookies);
    }

    /**
     * Returns an iterator
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->cookies);
    }

    public function addCookiesFromResponse(Response $response, RequestInterface $request = null)
    {
        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
            $parser = ParserRegistry::getInstance()->getParser('cookie');
            foreach ($cookieHeader as $cookie) {
                if ($parsed = $request
                    ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath())
                    : $parser->parseCookie($cookie)
                ) {
                    // Break up cookie v2 into multiple cookies
                    foreach ($parsed['cookies'] as $key => $value) {
                        $row = $parsed;
                        $row['name'] = $key;
                        $row['value'] = $value;
                        unset($row['cookies']);
                        $this->add(new Cookie($row));
                    }
                }
            }
        }
    }

    public function getMatchingCookies(RequestInterface $request)
    {
        // Find cookies that match this request
        $cookies = $this->all($request->getHost(), $request->getPath());
        // Remove ineligible cookies
        foreach ($cookies as $index => $cookie) {
            if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) {
                unset($cookies[$index]);
            }
        };

        return $cookies;
    }
}
Cookie/CookieJar/CookieJarInterface.php000060400000005552152441445220014031 0ustar00<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Plugin\Cookie\Cookie;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface for persisting cookies
 */
interface CookieJarInterface extends \Countable, \IteratorAggregate
{
    /**
     * Remove cookies currently held in the Cookie cookieJar.
     *
     * Invoking this method without arguments will empty the whole Cookie cookieJar.  If given a $domain argument only
     * cookies belonging to that domain will be removed. If given a $domain and $path argument, cookies belonging to
     * the specified path within that domain are removed. If given all three arguments, then the cookie with the
     * specified name, path and domain is removed.
     *
     * @param string $domain Set to clear only cookies matching a domain
     * @param string $path   Set to clear only cookies matching a domain and path
     * @param string $name   Set to clear only cookies matching a domain, path, and name
     *
     * @return CookieJarInterface
     */
    public function remove($domain = null, $path = null, $name = null);

    /**
     * Discard all temporary cookies.
     *
     * Scans for all cookies in the cookieJar with either no expire field or a true discard flag. To be called when the
     * user agent shuts down according to RFC 2965.
     *
     * @return CookieJarInterface
     */
    public function removeTemporary();

    /**
     * Delete any expired cookies
     *
     * @return CookieJarInterface
     */
    public function removeExpired();

    /**
     * Add a cookie to the cookie cookieJar
     *
     * @param Cookie $cookie Cookie to add
     *
     * @return bool Returns true on success or false on failure
     */
    public function add(Cookie $cookie);

    /**
     * Add cookies from a {@see Guzzle\Http\Message\Response} object
     *
     * @param Response         $response Response object
     * @param RequestInterface $request  Request that received the response
     */
    public function addCookiesFromResponse(Response $response, RequestInterface $request = null);

    /**
     * Get cookies matching a request object
     *
     * @param RequestInterface $request Request object to match
     *
     * @return array
     */
    public function getMatchingCookies(RequestInterface $request);

    /**
     * Get all of the matching cookies
     *
     * @param string $domain          Domain of the cookie
     * @param string $path            Path of the cookie
     * @param string $name            Name of the cookie
     * @param bool   $skipDiscardable Set to TRUE to skip cookies with the Discard attribute.
     * @param bool   $skipExpired     Set to FALSE to include expired
     *
     * @return array Returns an array of Cookie objects
     */
    public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true);
}
Cookie/CookieJar/FileCookieJar.php000060400000003206152441445220013002 0ustar00<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Persists non-session cookies using a JSON formatted file
 */
class FileCookieJar extends ArrayCookieJar
{
    /** @var string filename */
    protected $filename;

    /**
     * Create a new FileCookieJar object
     *
     * @param string $cookieFile File to store the cookie data
     *
     * @throws RuntimeException if the file cannot be found or created
     */
    public function __construct($cookieFile)
    {
        $this->filename = $cookieFile;
        $this->load();
    }

    /**
     * Saves the file when shutting down
     */
    public function __destruct()
    {
        $this->persist();
    }

    /**
     * Save the contents of the data array to the file
     *
     * @throws RuntimeException if the file cannot be found or created
     */
    protected function persist()
    {
        if (false === file_put_contents($this->filename, $this->serialize())) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to open file ' . $this->filename);
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * Load the contents of the json formatted file into the data array and discard any unsaved state
     */
    protected function load()
    {
        $json = file_get_contents($this->filename);
        if (false === $json) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to open file ' . $this->filename);
            // @codeCoverageIgnoreEnd
        }

        $this->unserialize($json);
        $this->cookies = $this->cookies ?: array();
    }
}
Cookie/Exception/InvalidCookieException.php000060400000000252152441445220015021 0ustar00<?php

namespace Guzzle\Plugin\Cookie\Exception;

use Guzzle\Common\Exception\InvalidArgumentException;

class InvalidCookieException extends InvalidArgumentException {}
Cookie/Exception/.htaccess000044400000000424152441445220011512 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Cookie/CookiePlugin.php000060400000003611152441445220011056 0ustar00<?php

namespace Guzzle\Plugin\Cookie;

use Guzzle\Common\Event;
use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
use Guzzle\Plugin\Cookie\CookieJar\CookieJarInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds, extracts, and persists cookies between HTTP requests
 */
class CookiePlugin implements EventSubscriberInterface
{
    /** @var CookieJarInterface Cookie cookieJar used to hold cookies */
    protected $cookieJar;

    /**
     * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies. Creates an ArrayCookieJar by default.
     */
    public function __construct(CookieJarInterface $cookieJar = null)
    {
        $this->cookieJar = $cookieJar ?: new ArrayCookieJar();
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', 125),
            'request.sent'        => array('onRequestSent', 125)
        );
    }

    /**
     * Get the cookie cookieJar
     *
     * @return CookieJarInterface
     */
    public function getCookieJar()
    {
        return $this->cookieJar;
    }

    /**
     * Add cookies before a request is sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        $request = $event['request'];
        if (!$request->getParams()->get('cookies.disable')) {
            $request->removeHeader('Cookie');
            // Find cookies that match this request
            foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) {
                $request->addCookie($cookie->getName(), $cookie->getValue());
            }
        }
    }

    /**
     * Extract cookies from a sent request
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']);
    }
}
Cookie/composer.json000060400000001230152441445220010472 0ustar00{
    "name": "guzzle/plugin-cookie",
    "description": "Guzzle cookie plugin",
    "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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Cookie": "" }
    },
    "target-dir": "Guzzle/Plugin/Cookie",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Async/.htaccess000044400000000424152441445220007420 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Async/composer.json000060400000001234152441445220010342 0ustar00{
    "name": "guzzle/plugin-async",
    "description": "Guzzle async request plugin",
    "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"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Async": "" }
    },
    "target-dir": "Guzzle/Plugin/Async",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
Async/AsyncPlugin.php000060400000005631152441445220010572 0ustar00<?php

namespace Guzzle\Plugin\Async;

use Guzzle\Common\Event;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\CurlException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Sends requests but does not wait for the response
 */
class AsyncPlugin implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send'    => 'onBeforeSend',
            'request.exception'      => 'onRequestTimeout',
            'request.sent'           => 'onRequestSent',
            'curl.callback.progress' => 'onCurlProgress'
        );
    }

    /**
     * Event used to ensure that progress callback are emitted from the curl handle's request mediator.
     *
     * @param Event $event
     */
    public function onBeforeSend(Event $event)
    {
        // Ensure that progress callbacks are dispatched
        $event['request']->getCurlOptions()->set('progress', true);
    }

    /**
     * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to
     * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with
     * transmitting the request, and tell curl not download a body.
     *
     * @param Event $event
     */
    public function onCurlProgress(Event $event)
    {
        if ($event['handle'] &&
            ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded']))
        ) {
            // Timeout after 1ms
            curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1);
            // Even if the response is quick, tell curl not to download the body.
            // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the
            //   request method is not converted to a HEAD request before the request was sent via curl.
            if ($event['uploaded']) {
                curl_setopt($event['handle'], CURLOPT_NOBODY, true);
            }
        }
    }

    /**
     * Event emitted when a curl exception occurs. Ignore the exception and set a mock response.
     *
     * @param Event $event
     */
    public function onRequestTimeout(Event $event)
    {
        if ($event['exception'] instanceof CurlException) {
            $event['request']->setResponse(new Response(200, array(
                'X-Guzzle-Async' => 'Did not wait for the response'
            )));
        }
    }

    /**
     * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the
     * caller that there is no body in the message.
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        // Let the caller know this was meant to be async
        $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response');
    }
}

Al-HUWAITI Shell