first commit
This commit is contained in:
403
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/CacheStorage.php
vendored
Normal file
403
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/CacheStorage.php
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use GuzzleHttp\Message\AbstractMessage;
|
||||
use GuzzleHttp\Message\MessageInterface;
|
||||
use GuzzleHttp\Message\Request;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
use GuzzleHttp\Stream;
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
|
||||
/**
|
||||
* Default cache storage implementation.
|
||||
*/
|
||||
class CacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/** @var string */
|
||||
private $keyPrefix;
|
||||
|
||||
/** @var int Default cache TTL */
|
||||
private $defaultTtl;
|
||||
|
||||
/** @var Cache */
|
||||
private $cache;
|
||||
|
||||
/** @var array Headers are excluded from the caching (see RFC 2616:13.5.1) */
|
||||
private static $noCache = [
|
||||
'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,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Cache $cache Cache backend.
|
||||
* @param string $keyPrefix (optional) Key prefix to add to each key.
|
||||
* @param int $defaultTtl (optional) The default TTL to set, in seconds.
|
||||
*/
|
||||
public function __construct(Cache $cache, $keyPrefix = null, $defaultTtl = 0)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->keyPrefix = $keyPrefix;
|
||||
$this->defaultTtl = $defaultTtl;
|
||||
}
|
||||
|
||||
public function cache(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$ctime = time();
|
||||
$ttl = $this->getTtl($response);
|
||||
$key = $this->getCacheKey($request, $this->normalizeVary($response));
|
||||
$headers = $this->persistHeaders($request);
|
||||
$entries = $this->getManifestEntries($key, $ctime, $response, $headers);
|
||||
$bodyDigest = null;
|
||||
|
||||
// Persist the Vary response header.
|
||||
if ($response->hasHeader('vary')) {
|
||||
$this->cacheVary($request, $response);
|
||||
}
|
||||
|
||||
// Persist the response body if needed
|
||||
if ($response->getBody() && $response->getBody()->getSize() > 0) {
|
||||
$body = $response->getBody();
|
||||
$bodyDigest = $this->getBodyKey($request->getUrl(), $body);
|
||||
$this->cache->save($bodyDigest, (string) $body, $ttl);
|
||||
}
|
||||
|
||||
array_unshift($entries, [
|
||||
$headers,
|
||||
$this->persistHeaders($response),
|
||||
$response->getStatusCode(),
|
||||
$bodyDigest,
|
||||
$ctime + $ttl
|
||||
]);
|
||||
|
||||
$this->cache->save($key, serialize($entries));
|
||||
}
|
||||
|
||||
public function delete(RequestInterface $request)
|
||||
{
|
||||
$vary = $this->fetchVary($request);
|
||||
$key = $this->getCacheKey($request, $vary);
|
||||
$entries = $this->cache->fetch($key);
|
||||
|
||||
if (!$entries) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete each cached body
|
||||
foreach (unserialize($entries) as $entry) {
|
||||
if ($entry[3]) {
|
||||
$this->cache->delete($entry[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete any cached Vary header responses.
|
||||
$this->deleteVary($request);
|
||||
|
||||
$this->cache->delete($key);
|
||||
}
|
||||
|
||||
public function purge($url)
|
||||
{
|
||||
foreach (['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PURGE'] as $m) {
|
||||
$this->delete(new Request($m, $url));
|
||||
}
|
||||
}
|
||||
|
||||
public function fetch(RequestInterface $request)
|
||||
{
|
||||
$vary = $this->fetchVary($request);
|
||||
if ($vary) {
|
||||
$key = $this->getCacheKey($request, $vary);
|
||||
} else {
|
||||
$key = $this->getCacheKey($request);
|
||||
}
|
||||
$entries = $this->cache->fetch($key);
|
||||
|
||||
if (!$entries) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$match = $matchIndex = null;
|
||||
$headers = $this->persistHeaders($request);
|
||||
$entries = unserialize($entries);
|
||||
|
||||
foreach ($entries as $index => $entry) {
|
||||
$vary = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
|
||||
if ($this->requestsMatch($vary, $headers, $entry[0])) {
|
||||
$match = $entry;
|
||||
$matchIndex = $index;
|
||||
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(Stream\Utils::create($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[$matchIndex]);
|
||||
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 The Request to generate the cache key
|
||||
* for.
|
||||
* @param array $vary (optional) An array of headers to vary
|
||||
* the cache key by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getCacheKey(RequestInterface $request, array $vary = [])
|
||||
{
|
||||
$key = $request->getMethod() . ' ' . $request->getUrl();
|
||||
|
||||
// If Vary headers have been passed in, fetch each header and add it to
|
||||
// the cache key.
|
||||
foreach ($vary as $header) {
|
||||
$key .= " $header: " . $request->getHeader($header);
|
||||
}
|
||||
|
||||
return $this->keyPrefix . md5($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cache key for a response's body.
|
||||
*
|
||||
* @param string $url URL of the entry
|
||||
* @param StreamInterface $body Response body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getBodyKey($url, StreamInterface $body)
|
||||
{
|
||||
return $this->keyPrefix . md5($url) . Stream\Utils::hash($body, 'md5');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// Clone the response to not destroy any necessary headers when caching
|
||||
$headers = array_diff_key($message->getHeaders(), self::$noCache);
|
||||
|
||||
// Cast the headers to a string
|
||||
foreach ($headers as &$value) {
|
||||
$value = implode(', ', $value);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TTL to use when caching a Response.
|
||||
*
|
||||
* @param ResponseInterface $response The response being cached.
|
||||
*
|
||||
* @return int The TTL in seconds.
|
||||
*/
|
||||
private function getTtl(ResponseInterface $response)
|
||||
{
|
||||
$ttl = 0;
|
||||
|
||||
if ($cacheControl = $response->getHeader('Cache-Control')) {
|
||||
$maxAge = Utils::getDirective($response, 'max-age');
|
||||
if (is_numeric($maxAge)) {
|
||||
$ttl += $maxAge;
|
||||
}
|
||||
|
||||
// According to RFC5861 stale headers are *in addition* to any
|
||||
// max-age values.
|
||||
$stale = Utils::getDirective($response, 'stale-if-error');
|
||||
if (is_numeric($stale)) {
|
||||
$ttl += $stale;
|
||||
}
|
||||
} elseif ($expires = $response->getHeader('Expires')) {
|
||||
$ttl += strtotime($expires) - time();
|
||||
}
|
||||
|
||||
return $ttl ?: $this->defaultTtl;
|
||||
}
|
||||
|
||||
private function getManifestEntries(
|
||||
$key,
|
||||
$currentTime,
|
||||
ResponseInterface $response,
|
||||
$persistedRequest
|
||||
) {
|
||||
$entries = [];
|
||||
$manifest = $this->cache->fetch($key);
|
||||
|
||||
if (!$manifest) {
|
||||
return $entries;
|
||||
}
|
||||
|
||||
// Determine which cache entries should still be in the cache
|
||||
$vary = $response->getHeader('Vary');
|
||||
|
||||
foreach (unserialize($manifest) as $entry) {
|
||||
// Check if the entry is expired
|
||||
if ($entry[4] < $currentTime) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$varyCmp = isset($entry[1]['vary']) ? $entries[1]['vary'] : '';
|
||||
|
||||
if ($vary != $varyCmp ||
|
||||
!$this->requestsMatch($vary, $entry[0], $persistedRequest)
|
||||
) {
|
||||
$entries[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sorted list of Vary headers.
|
||||
*
|
||||
* While headers are case-insensitive, header values are not. We can only
|
||||
* normalize the order of headers to combine cache entries.
|
||||
*
|
||||
* @param ResponseInterface $response The Response with Vary headers.
|
||||
*
|
||||
* @return array An array of sorted headers.
|
||||
*/
|
||||
private function normalizeVary(ResponseInterface $response)
|
||||
{
|
||||
$parts = AbstractMessage::normalizeHeader($response, 'vary');
|
||||
sort($parts);
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the Vary headers from a response.
|
||||
*
|
||||
* @param RequestInterface $request The Request that generated the Vary
|
||||
* headers.
|
||||
* @param ResponseInterface $response The Response with Vary headers.
|
||||
*/
|
||||
private function cacheVary(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$key = $this->getVaryKey($request);
|
||||
$this->cache->save($key, $this->normalizeVary($response), $this->getTtl($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the Vary headers associated with a request, if they exist.
|
||||
*
|
||||
* Only responses, and not requests, contain Vary headers. However, we need
|
||||
* to be able to determine what Vary headers were set for a given URL and
|
||||
* request method on a future request.
|
||||
*
|
||||
* @param RequestInterface $request The Request to fetch headers for.
|
||||
*
|
||||
* @return array An array of headers.
|
||||
*/
|
||||
private function fetchVary(RequestInterface $request)
|
||||
{
|
||||
$key = $this->getVaryKey($request);
|
||||
$varyHeaders = $this->cache->fetch($key);
|
||||
|
||||
return is_array($varyHeaders) ? $varyHeaders : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the headers associated with a Vary request.
|
||||
*
|
||||
* @param RequestInterface $request The Request to delete headers for.
|
||||
*/
|
||||
private function deleteVary(RequestInterface $request)
|
||||
{
|
||||
$key = $this->getVaryKey($request);
|
||||
$this->cache->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache key for Vary headers.
|
||||
*
|
||||
* @param RequestInterface $request The Request to fetch the key for.
|
||||
*
|
||||
* @return string The generated key.
|
||||
*/
|
||||
private function getVaryKey(RequestInterface $request)
|
||||
{
|
||||
$key = $this->keyPrefix . md5('vary ' . $this->getCacheKey($request));
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
45
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/CacheStorageInterface.php
vendored
Normal file
45
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/CacheStorageInterface.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Interface used to cache HTTP responses.
|
||||
*/
|
||||
interface CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* Get a Response from the cache for a request.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return null|ResponseInterface
|
||||
*/
|
||||
public function fetch(RequestInterface $request);
|
||||
|
||||
/**
|
||||
* Cache an HTTP request.
|
||||
*
|
||||
* @param RequestInterface $request Request being cached
|
||||
* @param ResponseInterface $response Response to cache
|
||||
*/
|
||||
public function cache(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $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);
|
||||
}
|
||||
275
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/CacheSubscriber.php
vendored
Normal file
275
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/CacheSubscriber.php
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Event\BeforeEvent;
|
||||
use GuzzleHttp\Event\CompleteEvent;
|
||||
use GuzzleHttp\Event\ErrorEvent;
|
||||
use GuzzleHttp\Event\HasEmitterInterface;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Event\SubscriberInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 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 CacheSubscriber implements SubscriberInterface
|
||||
{
|
||||
/** @var CacheStorageInterface $cache Object used to cache responses */
|
||||
private $storage;
|
||||
|
||||
/** @var callable Determines if a request is cacheable */
|
||||
private $canCache;
|
||||
|
||||
/**
|
||||
* @param CacheStorageInterface $cache Cache storage
|
||||
* @param callable $canCache Callable used to determine if a
|
||||
* request can be cached. Accepts a
|
||||
* RequestInterface and returns a
|
||||
* boolean value.
|
||||
*/
|
||||
public function __construct(
|
||||
CacheStorageInterface $cache,
|
||||
callable $canCache
|
||||
) {
|
||||
$this->storage = $cache;
|
||||
$this->canCache = $canCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used to easily attach a cache to a request or client.
|
||||
*
|
||||
* This method accepts an array of options that are used to control the
|
||||
* caching behavior:
|
||||
*
|
||||
* - storage: An optional GuzzleHttp\Subscriber\Cache\CacheStorageInterface.
|
||||
* If no value is not provided, an in-memory array cache will be used.
|
||||
* - validate: Boolean value that determines if cached response are ever
|
||||
* validated against the origin server. Defaults to true but can be
|
||||
* disabled by passing false.
|
||||
* - purge: Boolean value that determines if cached responses are purged
|
||||
* when non-idempotent requests are sent to their URI. Defaults to true
|
||||
* but can be disabled by passing false.
|
||||
* - can_cache: An optional callable used to determine if a request can be
|
||||
* cached. The callable accepts a RequestInterface and returns a boolean
|
||||
* value. If no value is provided, the default behavior is utilized.
|
||||
*
|
||||
* @param HasEmitterInterface $subject Client or request to attach to,
|
||||
* @param array $options Options used to control the cache.
|
||||
*
|
||||
* @return array Returns an associative array containing a 'subscriber' key
|
||||
* that holds the created CacheSubscriber, and a 'storage'
|
||||
* key that contains the cache storage used by the subscriber.
|
||||
*/
|
||||
public static function attach(
|
||||
HasEmitterInterface $subject,
|
||||
array $options = []
|
||||
) {
|
||||
if (!isset($options['storage'])) {
|
||||
$options['storage'] = new CacheStorage(new ArrayCache());
|
||||
}
|
||||
|
||||
if (!isset($options['can_cache'])) {
|
||||
$options['can_cache'] = [
|
||||
'GuzzleHttp\Subscriber\Cache\Utils',
|
||||
'canCacheRequest',
|
||||
];
|
||||
}
|
||||
|
||||
$emitter = $subject->getEmitter();
|
||||
$cache = new self($options['storage'], $options['can_cache']);
|
||||
$emitter->attach($cache);
|
||||
|
||||
if (!isset($options['validate']) || $options['validate'] === true) {
|
||||
$emitter->attach(new ValidationSubscriber(
|
||||
$options['storage'],
|
||||
$options['can_cache'])
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($options['purge']) || $options['purge'] === true) {
|
||||
$emitter->attach(new PurgeSubscriber($options['storage']));
|
||||
}
|
||||
|
||||
return ['subscriber' => $cache, 'storage' => $options['storage']];
|
||||
}
|
||||
|
||||
public function getEvents()
|
||||
{
|
||||
return [
|
||||
'before' => ['onBefore', RequestEvents::LATE],
|
||||
'complete' => ['onComplete', RequestEvents::EARLY],
|
||||
'error' => ['onError', RequestEvents::EARLY]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a request can be cached, and if so, intercepts with a cached
|
||||
* response is available.
|
||||
*
|
||||
* @param BeforeEvent $event
|
||||
*/
|
||||
public function onBefore(BeforeEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$this->canCacheRequest($request)) {
|
||||
$this->cacheMiss($request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!($response = $this->storage->fetch($request))) {
|
||||
$this->cacheMiss($request);
|
||||
return;
|
||||
}
|
||||
|
||||
$response->setHeader('Age', Utils::getResponseAge($response));
|
||||
$valid = $this->validate($request, $response);
|
||||
|
||||
// Validate that the response satisfies the request
|
||||
if ($valid) {
|
||||
$request->getConfig()->set('cache_lookup', 'HIT');
|
||||
$request->getConfig()->set('cache_hit', true);
|
||||
$event->intercept($response);
|
||||
} else {
|
||||
$this->cacheMiss($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the request and response can be cached, and if so, store it.
|
||||
*
|
||||
* @param CompleteEvent $event
|
||||
*/
|
||||
public function onComplete(CompleteEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
$response = $event->getResponse();
|
||||
|
||||
// Cache the response if it can be cached and isn't already
|
||||
if ($request->getConfig()->get('cache_lookup') === 'MISS'
|
||||
&& call_user_func($this->canCache, $request)
|
||||
&& Utils::canCacheResponse($response)
|
||||
) {
|
||||
// Store the date when the response was cached
|
||||
$response->setHeader('X-Guzzle-Cache-Date', gmdate('D, d M Y H:i:s T', time()));
|
||||
$this->storage->cache($request, $response);
|
||||
}
|
||||
|
||||
$this->addResponseHeaders($request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the request failed, then check if a cached response would suffice.
|
||||
*
|
||||
* @param ErrorEvent $event
|
||||
*/
|
||||
public function onError(ErrorEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!call_user_func($this->canCache, $request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $this->storage->fetch($request);
|
||||
|
||||
// Intercept the failed response if possible
|
||||
if ($response && $this->validateFailed($request, $response)) {
|
||||
$request->getConfig()->set('cache_hit', 'error');
|
||||
$response->setHeader('Age', Utils::getResponseAge($response));
|
||||
$event->intercept($response);
|
||||
}
|
||||
}
|
||||
|
||||
private function cacheMiss(RequestInterface $request)
|
||||
{
|
||||
$request->getConfig()->set('cache_lookup', 'MISS');
|
||||
}
|
||||
|
||||
private function validate(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
// Validation is handled in another subscriber and can be optionally
|
||||
// enabled/disabled.
|
||||
if (Utils::getDirective($response, 'must-revalidate')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Utils::isResponseValid($request, $response);
|
||||
}
|
||||
|
||||
private function validateFailed(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$req = Utils::getDirective($request, 'stale-if-error');
|
||||
$res = Utils::getDirective($response, 'stale-if-error');
|
||||
|
||||
if (!$req && !$res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$responseAge = Utils::getResponseAge($response);
|
||||
$maxAge = Utils::getMaxAge($response);
|
||||
|
||||
if (($req && $responseAge - $maxAge > $req) ||
|
||||
($responseAge - $maxAge > $res)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canCacheRequest(RequestInterface $request)
|
||||
{
|
||||
return !$request->getConfig()->get('cache.disable')
|
||||
&& call_user_func($this->canCache, $request);
|
||||
}
|
||||
|
||||
private function addResponseHeaders(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$params = $request->getConfig();
|
||||
$lookup = $params['cache_lookup'] . ' from GuzzleCache';
|
||||
$response->addHeader('X-Cache-Lookup', $lookup);
|
||||
|
||||
if ($params['cache_hit'] === true) {
|
||||
$response->addHeader('X-Cache', 'HIT from GuzzleCache');
|
||||
} elseif ($params['cache_hit'] == 'error') {
|
||||
$response->addHeader('X-Cache', 'HIT_ERROR from GuzzleCache');
|
||||
} else {
|
||||
$response->addHeader('X-Cache', 'MISS from GuzzleCache');
|
||||
}
|
||||
|
||||
$freshness = Utils::getFreshness($response);
|
||||
|
||||
// Only add a Warning header if we are returning a stale response.
|
||||
if ($params['cache_hit'] && $freshness !== null && $freshness <= 0) {
|
||||
$response->addHeader(
|
||||
'Warning',
|
||||
sprintf(
|
||||
'%d GuzzleCache/' . ClientInterface::VERSION . ' "%s"',
|
||||
110,
|
||||
'Response is stale'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/PurgeSubscriber.php
vendored
Normal file
51
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/PurgeSubscriber.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use GuzzleHttp\Event\BeforeEvent;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Event\SubscriberInterface;
|
||||
use GuzzleHttp\Message\Response;
|
||||
|
||||
/**
|
||||
* Automatically purges a URL when a non-idempotent request is made to it.
|
||||
*/
|
||||
class PurgeSubscriber implements SubscriberInterface
|
||||
{
|
||||
/** @var CacheStorageInterface */
|
||||
private $storage;
|
||||
|
||||
/** @var array */
|
||||
private static $purgeMethods = [
|
||||
'PUT' => true,
|
||||
'POST' => true,
|
||||
'DELETE' => true,
|
||||
'PATCH' => true,
|
||||
'PURGE' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param CacheStorageInterface $storage Storage to modify if purging
|
||||
*/
|
||||
public function __construct($storage)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
}
|
||||
|
||||
public function getEvents()
|
||||
{
|
||||
return ['before' => ['onBefore', RequestEvents::LATE]];
|
||||
}
|
||||
|
||||
public function onBefore(BeforeEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (isset(self::$purgeMethods[$request->getMethod()])) {
|
||||
$this->storage->purge($request->getUrl());
|
||||
|
||||
if ('PURGE' === $request->getMethod()) {
|
||||
$event->intercept(new Response(204));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
202
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/Utils.php
vendored
Normal file
202
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/Utils.php
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use GuzzleHttp\Message\AbstractMessage;
|
||||
use GuzzleHttp\Message\MessageInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Cache utility functions.
|
||||
*/
|
||||
class Utils
|
||||
{
|
||||
/**
|
||||
* Get a cache control directive from a message.
|
||||
*
|
||||
* @param MessageInterface $message Message to retrieve
|
||||
* @param string $part Cache directive to retrieve
|
||||
*
|
||||
* @return mixed|bool|null
|
||||
*/
|
||||
public static function getDirective(MessageInterface $message, $part)
|
||||
{
|
||||
$parts = AbstractMessage::parseHeader($message, 'Cache-Control');
|
||||
|
||||
foreach ($parts as $line) {
|
||||
if (isset($line[$part])) {
|
||||
return $line[$part];
|
||||
} elseif (in_array($part, $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the age of a response in seconds.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getResponseAge(ResponseInterface $response)
|
||||
{
|
||||
if ($response->hasHeader('Age')) {
|
||||
return (int) $response->getHeader('Age');
|
||||
}
|
||||
|
||||
$date = strtotime($response->getHeader('Date') ?: 'now');
|
||||
|
||||
return time() - $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of seconds from the current time in which a response
|
||||
* is still considered fresh.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return int|null Returns the number of seconds
|
||||
*/
|
||||
public static function getMaxAge(ResponseInterface $response)
|
||||
{
|
||||
$smaxage = Utils::getDirective($response, 's-maxage');
|
||||
if (is_numeric($smaxage)) {
|
||||
return (int) $smaxage;
|
||||
}
|
||||
|
||||
$maxage = Utils::getDirective($response, 'max-age');
|
||||
if (is_numeric($maxage)) {
|
||||
return (int) $maxage;
|
||||
}
|
||||
|
||||
if ($response->hasHeader('Expires')) {
|
||||
return strtotime($response->getHeader('Expires')) - time();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the freshness of a response by returning the difference of the
|
||||
* maximum lifetime of the response and the age of the response.
|
||||
*
|
||||
* Freshness values less than 0 mean that the response is no longer fresh
|
||||
* and is ABS(freshness) seconds expired. Freshness values of greater than
|
||||
* zero is the number of seconds until the response is no longer fresh.
|
||||
* A NULL result means that no freshness information is available.
|
||||
*
|
||||
* @param ResponseInterface $response Response to get freshness of
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public static function getFreshness(ResponseInterface $response)
|
||||
{
|
||||
$maxAge = self::getMaxAge($response);
|
||||
$age = self::getResponseAge($response);
|
||||
|
||||
return is_int($maxAge) && is_int($age) ? ($maxAge - $age) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default function used to determine if a request can be cached.
|
||||
*
|
||||
* @param RequestInterface $request Request to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function canCacheRequest(RequestInterface $request)
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
|
||||
// Only GET and HEAD requests can be cached
|
||||
if ($method !== 'GET' && $method !== 'HEAD') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't fool with Range requests for now
|
||||
if ($request->hasHeader('Range')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::getDirective($request, 'no-store') === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a response can be cached.
|
||||
*
|
||||
* @param ResponseInterface $response Response to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function canCacheResponse(ResponseInterface $response)
|
||||
{
|
||||
static $cacheCodes = [200, 203, 300, 301, 410];
|
||||
|
||||
// Check if the response is cacheable based on the code
|
||||
if (!in_array((int) $response->getStatusCode(), $cacheCodes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure a valid body was returned and can be cached
|
||||
$body = $response->getBody();
|
||||
if ($body && (!$body->isReadable() || !$body->isSeekable())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never cache no-store resources (this is a private cache, so private
|
||||
// can be cached)
|
||||
if (self::getDirective($response, 'no-store')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't fool with Content-Range requests for now
|
||||
if ($response->hasHeader('Content-Range')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$freshness = self::getFreshness($response);
|
||||
|
||||
return $freshness === null // No freshness info.
|
||||
|| $freshness >= 0 // It's fresh
|
||||
|| $response->hasHeader('ETag') // Can validate
|
||||
|| $response->hasHeader('Last-Modified'); // Can validate
|
||||
}
|
||||
|
||||
public static function isResponseValid(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$responseAge = Utils::getResponseAge($response);
|
||||
$maxAge = Utils::getDirective($response, 'max-age');
|
||||
|
||||
// Increment the age based on the X-Guzzle-Cache-Date
|
||||
if ($cacheDate = $response->getHeader('X-Guzzle-Cache-Date')) {
|
||||
$responseAge += (time() - strtotime($cacheDate));
|
||||
$response->setHeader('Age', $responseAge);
|
||||
}
|
||||
|
||||
// Check the request's max-age header against the age of the response
|
||||
if ($maxAge !== null && $responseAge > $maxAge) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the response's max-age header against the freshness level
|
||||
$freshness = Utils::getFreshness($response);
|
||||
|
||||
if ($freshness !== null) {
|
||||
$maxStale = Utils::getDirective($request, 'max-stale');
|
||||
if ($maxStale !== null) {
|
||||
if ($freshness < (-1 * $maxStale)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($maxAge !== null && $responseAge > $maxAge) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
207
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/ValidationSubscriber.php
vendored
Normal file
207
modules/psaddonsconnect/vendor/guzzlehttp/cache-subscriber/src/ValidationSubscriber.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use GuzzleHttp\Event\CompleteEvent;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Event\SubscriberInterface;
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Validates cached responses as needed.
|
||||
*
|
||||
* @Link http://tools.ietf.org/html/rfc7234#section-4.3
|
||||
*/
|
||||
class ValidationSubscriber implements SubscriberInterface
|
||||
{
|
||||
/** @var CacheStorageInterface Cache object storing cache data */
|
||||
private $storage;
|
||||
|
||||
/** @var callable */
|
||||
private $canCache;
|
||||
|
||||
/** @var array */
|
||||
private static $gone = [404 => true, 410 => true];
|
||||
|
||||
/** @var array */
|
||||
private static $replaceHeaders = [
|
||||
'Date',
|
||||
'Expires',
|
||||
'Cache-Control',
|
||||
'ETag',
|
||||
'Last-Modified',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param CacheStorageInterface $cache Cache storage
|
||||
* @param callable $canCache Callable used to determine if a
|
||||
* request can be cached. Accepts a
|
||||
* RequestInterface and returns a
|
||||
* boolean value.
|
||||
*/
|
||||
public function __construct(
|
||||
CacheStorageInterface $cache,
|
||||
callable $canCache
|
||||
) {
|
||||
$this->storage = $cache;
|
||||
$this->canCache = $canCache;
|
||||
}
|
||||
|
||||
public function getEvents()
|
||||
{
|
||||
return ['complete' => ['onComplete', RequestEvents::EARLY]];
|
||||
}
|
||||
|
||||
public function onComplete(CompleteEvent $e)
|
||||
{
|
||||
$lookup = $e->getRequest()->getConfig()->get('cache_lookup');
|
||||
|
||||
if ($lookup == 'HIT' &&
|
||||
$this->shouldvalidate($e->getRequest(), $e->getResponse())
|
||||
) {
|
||||
$this->validate($e->getRequest(), $e->getResponse(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function validate(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
CompleteEvent $event
|
||||
) {
|
||||
try {
|
||||
$validate = $this->createRevalidationRequest($request, $response);
|
||||
$validated = $event->getClient()->send($validate);
|
||||
} catch (BadResponseException $e) {
|
||||
$this->handleBadResponse($e);
|
||||
}
|
||||
|
||||
if ($validated->getStatusCode() == 200) {
|
||||
$this->handle200Response($request, $validated, $event);
|
||||
} elseif ($validated->getStatusCode() == 304) {
|
||||
$this->handle304Response($request, $response, $validated, $event);
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldValidate(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
if ($request->getMethod() != 'GET'
|
||||
|| $request->getConfig()->get('cache.disable')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$validate = Utils::getDirective($request, 'Pragma') === 'no-cache'
|
||||
|| Utils::getDirective($response, 'Pragma') === 'no-cache'
|
||||
|| Utils::getDirective($request, 'must-revalidate')
|
||||
|| Utils::getDirective($response, 'must-revalidate')
|
||||
|| Utils::getDirective($request, 'no-cache')
|
||||
|| Utils::getDirective($response, 'no-cache')
|
||||
|| Utils::getDirective($response, 'max-age') === '0'
|
||||
|| Utils::getDirective($response, 's-maxage') === '0';
|
||||
|
||||
// Use the strong ETag validator if available and the response contains
|
||||
// no Cache-Control directive
|
||||
if (!$validate
|
||||
&& !$response->hasHeader('Cache-Control')
|
||||
&& $response->hasHeader('ETag')
|
||||
) {
|
||||
$validate = true;
|
||||
}
|
||||
|
||||
return $validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a bad response when attempting to validate.
|
||||
*
|
||||
* If the resource no longer exists, then remove from the cache.
|
||||
*
|
||||
* @param BadResponseException $e Exception encountered
|
||||
*
|
||||
* @throws BadResponseException
|
||||
*/
|
||||
private function handleBadResponse(BadResponseException $e)
|
||||
{
|
||||
if (isset(self::$gone[$e->getResponse()->getStatusCode()])) {
|
||||
$this->storage->delete($e->getRequest());
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request to use for revalidation.
|
||||
*
|
||||
* @param RequestInterface $request Request
|
||||
* @param ResponseInterface $response Response to validate
|
||||
*
|
||||
* @return RequestInterface returns a revalidation request
|
||||
*/
|
||||
private function createRevalidationRequest(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$validate = clone $request;
|
||||
$validate->getConfig()->set('cache.disable', true);
|
||||
$validate->removeHeader('Pragma');
|
||||
$validate->removeHeader('Cache-Control');
|
||||
$responseDate = $response->getHeader('Last-Modified')
|
||||
?: $response->getHeader('Date');
|
||||
$validate->setHeader('If-Modified-Since', $responseDate);
|
||||
|
||||
if ($etag = $response->getHeader('ETag')) {
|
||||
$validate->setHeader('If-None-Match', $etag);
|
||||
}
|
||||
|
||||
return $validate;
|
||||
}
|
||||
|
||||
private function handle200Response(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $validateResponse,
|
||||
CompleteEvent $event
|
||||
) {
|
||||
// Store the 200 response in the cache if possible
|
||||
if (Utils::canCacheResponse($validateResponse)) {
|
||||
$this->storage->cache($request, $validateResponse);
|
||||
}
|
||||
|
||||
$event->intercept($validateResponse);
|
||||
}
|
||||
|
||||
private function handle304Response(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
ResponseInterface $validated,
|
||||
CompleteEvent $event
|
||||
) {
|
||||
// Make sure that this response has the same ETag
|
||||
if ($validated->getHeader('ETag') !== $response->getHeader('ETag')) {
|
||||
// Revalidation failed, so remove from cache and retry.
|
||||
$this->storage->delete($request);
|
||||
$event->intercept($event->getClient()->send($request));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace cached headers with any of these headers from the
|
||||
// origin server that might be more up to date
|
||||
$modified = false;
|
||||
foreach (self::$replaceHeaders as $name) {
|
||||
if ($validated->hasHeader($name)
|
||||
&& $validated->getHeader($name) != $response->getHeader($name)
|
||||
) {
|
||||
$modified = true;
|
||||
$response->setHeader($name, $validated->getHeader($name));
|
||||
}
|
||||
}
|
||||
|
||||
// Store the updated response in cache
|
||||
if ($modified) {
|
||||
$this->storage->cache($request, $response);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user