first commit
This commit is contained in:
25
modules/productcomments/vendor/guzzlehttp/cache-subscriber/CHANGELOG.md
vendored
Normal file
25
modules/productcomments/vendor/guzzlehttp/cache-subscriber/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.2.0 - 2019-09-16
|
||||
|
||||
* Improvement: Tests for expired items without must-revalidate #20
|
||||
* Improvement: Support for including Vary headers in cache keys #21
|
||||
* Improvement: Test for the can_cache option #23
|
||||
* Bugfix: Not adding timezone to dates #24
|
||||
* Improvement: Add $defaultTtl to CacheStorage constructor #25
|
||||
* Bugfix: Error caches responses for without vary headers #26
|
||||
* Bugfix: stale-if-header not being added to max-age #27
|
||||
* Bugfix: max-age and freshness confusing zero and null #28
|
||||
* Bugfix: Use date() method to fix missing GMT #29
|
||||
* Improvement: Tests for stale-if-error behaviour #30
|
||||
* Improvement: Delete cache entries on both 404 and 410 responses #33
|
||||
* Refactoring: Minor ValidationSubscriber.php cleanup #35
|
||||
* Refactoring: Minor ValidationSubscriber.php cleanup #35
|
||||
* Improvement: Extend caching ttl considerations #56
|
||||
* Improvement: Add purge method #57
|
||||
* Improvement: Integration test for the calculation of the "resident_time" #60
|
||||
* Improvement: Support for PHP 7.0 & 7.1 #73
|
||||
|
||||
## 0.1.0 - 2014-10-29
|
||||
|
||||
* Initial release.
|
||||
19
modules/productcomments/vendor/guzzlehttp/cache-subscriber/LICENSE
vendored
Normal file
19
modules/productcomments/vendor/guzzlehttp/cache-subscriber/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
14
modules/productcomments/vendor/guzzlehttp/cache-subscriber/Makefile
vendored
Normal file
14
modules/productcomments/vendor/guzzlehttp/cache-subscriber/Makefile
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
all: clean coverage
|
||||
|
||||
test:
|
||||
vendor/bin/phpunit
|
||||
|
||||
coverage:
|
||||
vendor/bin/phpunit --coverage-html=artifacts/coverage
|
||||
open artifacts/coverage/index.html
|
||||
|
||||
view-coverage:
|
||||
open artifacts/coverage/index.html
|
||||
|
||||
clean:
|
||||
rm -rf artifacts/*
|
||||
138
modules/productcomments/vendor/guzzlehttp/cache-subscriber/README.rst
vendored
Normal file
138
modules/productcomments/vendor/guzzlehttp/cache-subscriber/README.rst
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
=======================
|
||||
Guzzle Cache Subscriber
|
||||
=======================
|
||||
|
||||
.. important::
|
||||
|
||||
**This repo has not been updated for Guzzle 6 and only supports Guzzle 5.**
|
||||
|
||||
See https://github.com/Kevinrob/guzzle-cache-middleware for a nice Guzzle 6
|
||||
compatible Cache middleware.
|
||||
|
||||
Provides a private transparent proxy cache for caching HTTP responses.
|
||||
|
||||
Here's a simple example of how it's used:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
|
||||
|
||||
$client = new Client(['defaults' => ['debug' => true]]);
|
||||
|
||||
// Use the helper method to attach a cache to the client.
|
||||
CacheSubscriber::attach($client);
|
||||
|
||||
// Send the first request
|
||||
$a = $client->get('http://en.wikipedia.org/wiki/Main_Page');
|
||||
|
||||
// Send the second request. This will find a cache hit which must be
|
||||
// validated. The validation request returns a 304, which yields the original
|
||||
// cached response.
|
||||
$b = $client->get('http://en.wikipedia.org/wiki/Main_Page');
|
||||
|
||||
Running the above code sample should output verbose cURL information that looks
|
||||
something like this:
|
||||
|
||||
::
|
||||
|
||||
> GET /wiki/Main_Page HTTP/1.1
|
||||
Host: en.wikipedia.org
|
||||
User-Agent: Guzzle/4.2.1 curl/7.37.0 PHP/5.5.13
|
||||
Via: 1.1 GuzzleCache/4.2.1
|
||||
|
||||
< HTTP/1.1 200 OK
|
||||
< Server: Apache
|
||||
< X-Content-Type-Options: nosniff
|
||||
< Content-language: en
|
||||
< X-UA-Compatible: IE=Edge
|
||||
< Vary: Accept-Encoding,Cookie
|
||||
< Last-Modified: Thu, 21 Aug 2014 01:51:49 GMT
|
||||
< Content-Type: text/html; charset=UTF-8
|
||||
< X-Varnish: 2345493325, 1998949714 1994269567
|
||||
< Via: 1.1 varnish, 1.1 varnish
|
||||
< Transfer-Encoding: chunked
|
||||
< Date: Thu, 21 Aug 2014 02:34:12 GMT
|
||||
< Age: 2541
|
||||
< Connection: keep-alive
|
||||
< X-Cache: cp1055 hit (1), cp1068 frontend hit (25353)
|
||||
< Cache-Control: private, s-maxage=0, max-age=0, must-revalidate
|
||||
< Set-Cookie: GeoIP=US:Seattle:47.6062:-122.3321:v4; Path=/; Domain=.wikipedia.org
|
||||
<
|
||||
* Connection #0 to host en.wikipedia.org left intact
|
||||
* Re-using existing connection! (#0) with host en.wikipedia.org
|
||||
> GET /wiki/Main_Page HTTP/1.1
|
||||
Host: en.wikipedia.org
|
||||
User-Agent: Guzzle/4.2.1 curl/7.37.0 PHP/5.5.13
|
||||
Via: 1.1 GuzzleCache/4.2.1, 1.1 GuzzleCache/4.2.1
|
||||
If-Modified-Since: Thu, 21 Aug 2014 01:51:49 GMT
|
||||
|
||||
< HTTP/1.1 304 Not Modified
|
||||
< Server: Apache
|
||||
< X-Content-Type-Options: nosniff
|
||||
< Content-language: en
|
||||
< X-UA-Compatible: IE=Edge
|
||||
< Vary: Accept-Encoding,Cookie
|
||||
< Last-Modified: Thu, 21 Aug 2014 01:51:49 GMT
|
||||
< Content-Type: text/html; charset=UTF-8
|
||||
< X-Varnish: 2345493325, 1998950450 1994269567
|
||||
< Via: 1.1 varnish, 1.1 varnish
|
||||
< Date: Thu, 21 Aug 2014 02:34:12 GMT
|
||||
< Age: 2541
|
||||
< Connection: keep-alive
|
||||
< X-Cache: cp1055 hit (1), cp1068 frontend hit (25360)
|
||||
< Cache-Control: private, s-maxage=0, max-age=0, must-revalidate
|
||||
< Set-Cookie: GeoIP=US:Seattle:47.6062:-122.3321:v4; Path=/; Domain=.wikipedia.org
|
||||
<
|
||||
* Connection #0 to host en.wikipedia.org left intact
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Add the following to your composer.json:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"require": {
|
||||
"guzzlehttp/cache-subscriber": "0.2.*@dev"
|
||||
}
|
||||
}
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ composer require guzzlehttp/cache-subscriber
|
||||
|
||||
Creating a CacheSubscriber
|
||||
--------------------------
|
||||
|
||||
The easiest way to create a CacheSubscriber is using the ``attach()`` helper
|
||||
method of ``GuzzleHttp\Subscriber\Cache\CacheSubscriber``. This method accepts
|
||||
a request or client object and attaches the necessary subscribers used to
|
||||
perform cache lookups, validation requests, and automatic purging of resources.
|
||||
|
||||
The ``attach()`` method accepts the following options:
|
||||
|
||||
storage
|
||||
A ``GuzzleHttp\Subscriber\Cache\CacheStorageInterface`` object used to
|
||||
store cached responses. If no value is not provided, an in-memory array
|
||||
cache will be used.
|
||||
validate
|
||||
A Boolean value that determines if cached response are ever validated
|
||||
against the origin server. This setting defaults to ``true`` but can be
|
||||
disabled by passing ``false``.
|
||||
purge
|
||||
A Boolean value that determines if cached responses are purged when
|
||||
non-idempotent requests are sent to their URI. This setting 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 ``GuzzleHttp\Message\RequestInterface`` and returns a
|
||||
Boolean value. If no value is provided, the default behavior is utilized.
|
||||
|
||||
.. warning::
|
||||
|
||||
This is a WIP update for Guzzle 5+. It hasn't been tested and is in
|
||||
active development. Expect bugs and breaks.
|
||||
30
modules/productcomments/vendor/guzzlehttp/cache-subscriber/composer.json
vendored
Normal file
30
modules/productcomments/vendor/guzzlehttp/cache-subscriber/composer.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "guzzlehttp/cache-subscriber",
|
||||
"description": "Guzzle HTTP cache subscriber",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": ["cache", "guzzle"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"guzzlehttp/guzzle": "~5.0",
|
||||
"doctrine/cache": "~1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "GuzzleHttp\\Subscriber\\Cache\\": "src" }
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.2-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
modules/productcomments/vendor/guzzlehttp/cache-subscriber/phpunit.xml.dist
vendored
Normal file
14
modules/productcomments/vendor/guzzlehttp/cache-subscriber/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="vendor/autoload.php"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite>
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
403
modules/productcomments/vendor/guzzlehttp/cache-subscriber/src/CacheStorage.php
vendored
Normal file
403
modules/productcomments/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/productcomments/vendor/guzzlehttp/cache-subscriber/src/CacheStorageInterface.php
vendored
Normal file
45
modules/productcomments/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/productcomments/vendor/guzzlehttp/cache-subscriber/src/CacheSubscriber.php
vendored
Normal file
275
modules/productcomments/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/productcomments/vendor/guzzlehttp/cache-subscriber/src/PurgeSubscriber.php
vendored
Normal file
51
modules/productcomments/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/productcomments/vendor/guzzlehttp/cache-subscriber/src/Utils.php
vendored
Normal file
202
modules/productcomments/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/productcomments/vendor/guzzlehttp/cache-subscriber/src/ValidationSubscriber.php
vendored
Normal file
207
modules/productcomments/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);
|
||||
}
|
||||
}
|
||||
}
|
||||
138
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/CacheStorageTest.php
vendored
Normal file
138
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/CacheStorageTest.php
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace GuzzleHttp\Tests\Subscriber\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use GuzzleHttp\Subscriber\Cache\CacheStorage;
|
||||
|
||||
/**
|
||||
* Test the CacheStorage class.
|
||||
*
|
||||
* @class CacheStorageTest
|
||||
*/
|
||||
class CacheStorageTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Test that a Response's max-age returns the correct TTL.
|
||||
*/
|
||||
public function testGetTtlMaxAge()
|
||||
{
|
||||
$response = new Response(200, [
|
||||
'Cache-control' => 'max-age=10',
|
||||
]);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache());
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
$this->assertEquals(10, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the default TTL for cachable responses with no max-age headers
|
||||
* is zero.
|
||||
*/
|
||||
public function testGetTtlDefault()
|
||||
{
|
||||
$response = new Response(200);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache());
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
|
||||
// assertSame() here to be specific about null / false returns.
|
||||
$this->assertSame(0, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting the default TTL.
|
||||
*/
|
||||
public function testSetTtlDefault()
|
||||
{
|
||||
$response = new Response(200);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache(), null, 10);
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
$this->assertEquals(10, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that stale-if-error is added to the max-age header.
|
||||
*/
|
||||
public function testGetTtlMaxAgeStaleIfError()
|
||||
{
|
||||
$response = new Response(200, [
|
||||
'Cache-control' => 'max-age=10, stale-if-error=10',
|
||||
]);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache());
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
$this->assertEquals(20, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that stale-if-error works without a max-age header.
|
||||
*/
|
||||
public function testGetTtlStaleIfErrorAlone()
|
||||
{
|
||||
$response = new Response(200, [
|
||||
'Cache-control' => 'stale-if-error=10',
|
||||
]);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache());
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
$this->assertEquals(10, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that expires is considered when cache-control is not available.
|
||||
*/
|
||||
public function testGetTtlExpires()
|
||||
{
|
||||
$expires = new \DateTime('+100 seconds');
|
||||
$response = new Response(200, [
|
||||
'Expires' => $expires->format(DATE_RFC1123),
|
||||
]);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache());
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
$this->assertEquals(100, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cache-control is considered before expires.
|
||||
*/
|
||||
public function testGetTtlCacheControlExpires()
|
||||
{
|
||||
$expires = new \DateTime('+100 seconds');
|
||||
$response = new Response(200, [
|
||||
'Expires' => $expires->format(DATE_RFC1123),
|
||||
'Cache-control' => 'max-age=10',
|
||||
]);
|
||||
|
||||
$getTtl = $this->getMethod('getTtl');
|
||||
$cache = new CacheStorage(new ArrayCache());
|
||||
$ttl = $getTtl->invokeArgs($cache, [$response]);
|
||||
$this->assertEquals(10, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a protected or private method.
|
||||
*
|
||||
* @param string $name The name of the method.
|
||||
*
|
||||
* @return \ReflectionMethod A method object.
|
||||
*/
|
||||
protected static function getMethod($name)
|
||||
{
|
||||
$class = new \ReflectionClass('GuzzleHttp\Subscriber\Cache\CacheStorage');
|
||||
$method = $class->getMethod($name);
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method;
|
||||
}
|
||||
}
|
||||
25
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/CacheSubscriberTest.php
vendored
Normal file
25
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/CacheSubscriberTest.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Tests\Subscriber\Cache;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
|
||||
|
||||
class CacheSubscriberTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testCreatesAndAttachedDefaultSubscriber()
|
||||
{
|
||||
$client = new Client();
|
||||
$cache = CacheSubscriber::attach($client);
|
||||
$this->assertArrayHasKey('subscriber', $cache);
|
||||
$this->assertArrayHasKey('storage', $cache);
|
||||
$this->assertInstanceOf(
|
||||
'GuzzleHttp\Subscriber\Cache\CacheStorage',
|
||||
$cache['storage']
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
'GuzzleHttp\Subscriber\Cache\CacheSubscriber',
|
||||
$cache['subscriber']
|
||||
);
|
||||
$this->assertTrue($client->getEmitter()->hasListeners('error'));
|
||||
}
|
||||
}
|
||||
768
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/IntegrationTest.php
vendored
Normal file
768
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/IntegrationTest.php
vendored
Normal file
@@ -0,0 +1,768 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Tests\Subscriber\Cache;
|
||||
|
||||
require_once __DIR__ . '/../vendor/guzzlehttp/ringphp/tests/Client/Server.php';
|
||||
require_once __DIR__ . '/../vendor/guzzlehttp/guzzle/tests/Server.php';
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
|
||||
use GuzzleHttp\Subscriber\History;
|
||||
use GuzzleHttp\Tests\Server;
|
||||
|
||||
class IntegrationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
Server::start();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
Server::stop();
|
||||
}
|
||||
|
||||
public function testCachesResponses()
|
||||
{
|
||||
Server::enqueue([
|
||||
new Response(200, [
|
||||
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
|
||||
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
'Age' => '1277'
|
||||
]),
|
||||
new Response(304, [
|
||||
'Content-Type' => 'text/html; charset=UTF-8',
|
||||
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
|
||||
'Date' => 'Wed, 29 Oct 2014 20:52:16 GMT',
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
'Age' => '1278'
|
||||
]),
|
||||
new Response(200, [
|
||||
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
|
||||
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
'Age' => '1277'
|
||||
]),
|
||||
new Response(200, [
|
||||
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
|
||||
'Date' => 'Wed, 29 Oct 2014 20:53:15 GMT',
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:53:00 GMT',
|
||||
'Age' => '1277'
|
||||
]),
|
||||
]);
|
||||
|
||||
$history = new History();
|
||||
$client = $this->setupClient($history);
|
||||
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response1->getStatusCode());
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response2->getStatusCode());
|
||||
$last = $history->getLastResponse();
|
||||
$this->assertEquals('HIT from GuzzleCache', $last->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('HIT from GuzzleCache', $last->getHeader('X-Cache'));
|
||||
|
||||
// Validate that expired requests without must-revalidate expire.
|
||||
$response3 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response3->getStatusCode());
|
||||
$response4 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response4->getStatusCode());
|
||||
$last = $history->getLastResponse();
|
||||
$this->assertEquals('MISS from GuzzleCache', $last->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('MISS from GuzzleCache', $last->getHeader('X-Cache'));
|
||||
|
||||
// Validate that all of our requests were received.
|
||||
$this->assertCount(4, Server::received());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that Warning headers aren't added to cache misses.
|
||||
*/
|
||||
public function testCacheMissNoWarning()
|
||||
{
|
||||
Server::enqueue([
|
||||
new Response(200, [
|
||||
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
|
||||
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
'Age' => '1277',
|
||||
]),
|
||||
]);
|
||||
|
||||
$client = $this->setupClient();
|
||||
$response = $client->get('/foo');
|
||||
$this->assertFalse($response->hasHeader('warning'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the Vary header creates unique cache entries.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testVaryUniqueResponses()
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
Server::enqueue(
|
||||
[
|
||||
new Response(
|
||||
200, [
|
||||
'Vary' => 'Accept',
|
||||
'Content-type' => 'text/html',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory('It works!')
|
||||
),
|
||||
new Response(
|
||||
200, [
|
||||
'Vary' => 'Accept',
|
||||
'Content-type' => 'application/json',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory(json_encode(['body' => 'It works!']))
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
$client = $this->setupClient();
|
||||
|
||||
$response1 = $client->get(
|
||||
'/foo',
|
||||
['headers' => ['Accept' => 'text/html']]
|
||||
);
|
||||
$this->assertEquals('It works!', $this->getResponseBody($response1));
|
||||
|
||||
$response2 = $client->get(
|
||||
'/foo',
|
||||
['headers' => ['Accept' => 'application/json']]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'MISS from GuzzleCache',
|
||||
$response2->getHeader('x-cache')
|
||||
);
|
||||
|
||||
$decoded = json_decode($this->getResponseBody($response2));
|
||||
|
||||
if (!isset($decoded) || !isset($decoded->body)) {
|
||||
$this->fail('JSON response could not be decoded.');
|
||||
} else {
|
||||
$this->assertEquals('It works!', $decoded->body);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCachesResponsesForWithoutVaryHeader()
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
Server::enqueue(
|
||||
[
|
||||
new Response(
|
||||
200, [
|
||||
'Content-type' => 'text/html',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000, must-revalidate',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory()
|
||||
),
|
||||
new Response(
|
||||
200, [
|
||||
'Content-type' => 'text/html',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000, must-revalidate',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory()
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
$client = $this->setupClient();
|
||||
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response1->getStatusCode());
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response2->getStatusCode());
|
||||
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that requests varying on both Accept and User-Agent properly split
|
||||
* different User-Agents into different cache items.
|
||||
*/
|
||||
public function testVaryUserAgent()
|
||||
{
|
||||
$this->setupMultipleVaryResponses();
|
||||
$client = $this->setupClient();
|
||||
|
||||
$response1 = $client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/1.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Test/1.0 request.',
|
||||
$this->getResponseBody($response1)
|
||||
);
|
||||
|
||||
$response2 = $client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'MISS from GuzzleCache',
|
||||
$response2->getHeader('x-cache')
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Test/2.0 request.',
|
||||
$this->getResponseBody($response2)
|
||||
);
|
||||
|
||||
// Test that we get cache hits where both Vary headers match.
|
||||
$response5 = $client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'HIT from GuzzleCache',
|
||||
$response5->getHeader('x-cache')
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Test/2.0 request.',
|
||||
$this->getResponseBody($response5)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that requests varying on Accept but not User-Agent return different responses.
|
||||
*/
|
||||
public function testVaryAccept()
|
||||
{
|
||||
$this->setupMultipleVaryResponses();
|
||||
$client = $this->setupClient();
|
||||
|
||||
// Prime the cache.
|
||||
$client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/1.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
$response1 = $client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'Testing/1.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'MISS from GuzzleCache',
|
||||
$response1->getHeader('x-cache')
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Test/1.0 request.',
|
||||
json_decode($this->getResponseBody($response1))->body
|
||||
);
|
||||
|
||||
$response2 = $client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'MISS from GuzzleCache',
|
||||
$response2->getHeader('x-cache')
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Test/2.0 request.',
|
||||
json_decode($this->getResponseBody($response2))->body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that we return cached responses when multiple Vary headers match.
|
||||
*/
|
||||
public function testMultipleVaryMatch()
|
||||
{
|
||||
$this->setupMultipleVaryResponses();
|
||||
$client = $this->setupClient();
|
||||
|
||||
// Prime the cache.
|
||||
$client->get('/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/1.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$client->get('/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'text/html',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$client->get('/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'Testing/1.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$client->get('/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
$response = $client->get(
|
||||
'/foo',
|
||||
[
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'Testing/2.0',
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'HIT from GuzzleCache',
|
||||
$response->getHeader('x-cache')
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Test/2.0 request.',
|
||||
json_decode($this->getResponseBody($response))->body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that stale responses are used on errors if allowed.
|
||||
*/
|
||||
public function testOnErrorStaleResponse()
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
Server::enqueue([
|
||||
new Response(200, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'private, max-age=0, must-revalidate, stale-if-error=666',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
], Stream::factory('It works!')),
|
||||
new Response(503, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
'Age' => '1277'
|
||||
]),
|
||||
]);
|
||||
|
||||
$client = $this->setupClient();
|
||||
|
||||
// Prime the cache.
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response1->getStatusCode());
|
||||
|
||||
// This should return the first request.
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response2->getStatusCode());
|
||||
$this->assertEquals('It works!', $this->getResponseBody($response2));
|
||||
$this->assertEquals('HIT_ERROR from GuzzleCache', $response2->getHeader('x-cache'));
|
||||
$this->assertCount(2, Server::received());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that expired stale responses aren't returned.
|
||||
*/
|
||||
public function testOnErrorStaleResponseExpired()
|
||||
{
|
||||
// These dates are in the past, so the responses will be expired.
|
||||
Server::enqueue([
|
||||
new Response(200, [
|
||||
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
|
||||
'Cache-Control' => 'private, max-age=0, must-revalidate, stale-if-error=10',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
]),
|
||||
new Response(503, [
|
||||
'Date' => 'Wed, 29 Oct 2014 20:55:15 GMT',
|
||||
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
|
||||
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
|
||||
]),
|
||||
]);
|
||||
|
||||
$client = $this->setupClient();
|
||||
|
||||
// Prime the cache.
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response1->getStatusCode());
|
||||
$this->assertEquals('Wed, 29 Oct 2014 20:52:15 GMT', $response1->getHeader('Date'));
|
||||
|
||||
try {
|
||||
$client->get('/foo');
|
||||
$this->fail('503 was not thrown with an expired cache entry.');
|
||||
} catch (ServerException $e) {
|
||||
$this->assertEquals(503, $e->getCode());
|
||||
$this->assertEquals('Wed, 29 Oct 2014 20:55:15 GMT', $e->getResponse()->getHeader('Date'));
|
||||
$this->assertCount(2, Server::received());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the can_cache option can modify cache behaviour.
|
||||
*/
|
||||
public function testCanCache()
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
// Return an uncacheable response, that is then cached by can_cache
|
||||
// returning TRUE.
|
||||
Server::enqueue(
|
||||
[
|
||||
new Response(
|
||||
200, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'private, max-age=0, no-cache',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory('It works!')),
|
||||
new Response(
|
||||
304, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'private, max-age=0, no-cache',
|
||||
'Last-Modified' => $now,
|
||||
'Age' => 0,
|
||||
]),
|
||||
]
|
||||
);
|
||||
|
||||
$client = new Client(['base_url' => Server::$url]);
|
||||
CacheSubscriber::attach(
|
||||
$client,
|
||||
[
|
||||
'can_cache' => function (RequestInterface $request) {
|
||||
return true;
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('It works!', $this->getResponseBody($response2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that PURGE can delete cached responses.
|
||||
*/
|
||||
public function testCanPurge()
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
// Return a cached response that is then purged, and requested again
|
||||
Server::enqueue(
|
||||
[
|
||||
new Response(
|
||||
200, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, max-age=60',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory('It is foo!')),
|
||||
new Response(
|
||||
200, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, max-age=60',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory('It is bar!')),
|
||||
]
|
||||
);
|
||||
|
||||
$client = $this->setupClient();
|
||||
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('It is foo!', $this->getResponseBody($response1));
|
||||
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('It is foo!', $this->getResponseBody($response2));
|
||||
|
||||
$response3 = $client->send($client->createRequest('PURGE', '/foo'));
|
||||
$this->assertEquals(204, $response3->getStatusCode());
|
||||
|
||||
$response4 = $client->get('/foo');
|
||||
$this->assertEquals('MISS from GuzzleCache', $response4->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('It is bar!', $this->getResponseBody($response4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cache entries are deleted when a response 404s.
|
||||
*/
|
||||
public function test404CacheDelete()
|
||||
{
|
||||
$this->fourXXCacheDelete(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cache entries are deleted when a response 410s.
|
||||
*/
|
||||
public function test410CacheDelete()
|
||||
{
|
||||
$this->fourXXCacheDelete(410);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the resident_time calculation (RFC7234 4.2.3)
|
||||
*/
|
||||
public function testAgeIsIncremented()
|
||||
{
|
||||
Server::enqueue([
|
||||
new Response(200, [
|
||||
'Date' => $this->date(),
|
||||
'Cache-Control' => 'public, max-age=60',
|
||||
'Age' => '59'
|
||||
], Stream::factory('Age is 59!')),
|
||||
new Response(200, [
|
||||
'Date' => $this->date(),
|
||||
'Cache-Control' => 'public, max-age=60',
|
||||
'Age' => '0'
|
||||
], Stream::factory('It works!')),
|
||||
]);
|
||||
|
||||
$client = $this->setupClient();
|
||||
|
||||
// First request : the response is cached
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response1->getStatusCode());
|
||||
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('Age is 59!', $this->getResponseBody($response1));
|
||||
|
||||
// Second request : cache hit, age is now 60
|
||||
sleep(1);
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response1->getStatusCode());
|
||||
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
|
||||
|
||||
// This request should not be valid anymore : age is 59 + 2 = 61 which is strictly greater than 60
|
||||
sleep(1);
|
||||
$response3 = $client->get('/foo');
|
||||
$this->assertEquals(200, $response3->getStatusCode());
|
||||
$this->assertEquals('MISS from GuzzleCache', $response3->getHeader('X-Cache-Lookup'));
|
||||
$this->assertEquals('It works!', $this->getResponseBody($response3));
|
||||
|
||||
$this->assertCount(2, Server::received());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a response body from TestServer.
|
||||
*
|
||||
* TestServer encodes all responses with base64, so we need to decode them
|
||||
* before we can do any assert's on them.
|
||||
*
|
||||
* @param Response $response The response with a body to decode.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getResponseBody($response)
|
||||
{
|
||||
return base64_decode($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up responses used by our Vary tests.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function setupMultipleVaryResponses()
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
Server::enqueue(
|
||||
[
|
||||
new Response(
|
||||
200, [
|
||||
'Vary' => 'Accept, User-Agent',
|
||||
'Content-type' => 'text/html',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory('Test/1.0 request.')
|
||||
),
|
||||
new Response(
|
||||
200,
|
||||
[
|
||||
'Vary' => 'Accept, User-Agent',
|
||||
'Content-type' => 'text/html',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
|
||||
'Last-Modified' => $now,
|
||||
],
|
||||
Stream::factory('Test/2.0 request.')
|
||||
),
|
||||
new Response(
|
||||
200, [
|
||||
'Vary' => 'Accept, User-Agent',
|
||||
'Content-type' => 'application/json',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory(json_encode(['body' => 'Test/1.0 request.']))
|
||||
),
|
||||
new Response(
|
||||
200, [
|
||||
'Vary' => 'Accept, User-Agent',
|
||||
'Content-type' => 'application/json',
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
|
||||
'Last-Modified' => $now,
|
||||
], Stream::factory(json_encode(['body' => 'Test/2.0 request.']))
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a Guzzle client for testing.
|
||||
*
|
||||
* @param History $history (optional) parameter of a History to track
|
||||
* requests in.
|
||||
*
|
||||
* @return Client A client ready to run test requests against.
|
||||
*/
|
||||
private function setupClient(History $history = null)
|
||||
{
|
||||
$client = new Client(['base_url' => Server::$url]);
|
||||
CacheSubscriber::attach($client);
|
||||
if ($history) {
|
||||
$client->getEmitter()->attach($history);
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date string suitable for using in an HTTP header.
|
||||
*
|
||||
* @param int $timestamp (optional) A Unix timestamp to generate the date.
|
||||
*
|
||||
* @return string The generated date string.
|
||||
*/
|
||||
private function date($timestamp = null)
|
||||
{
|
||||
if (!$timestamp) {
|
||||
$timestamp = time();
|
||||
}
|
||||
|
||||
return gmdate("D, d M Y H:i:s", $timestamp) . ' GMT';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to test that a 400 response deletes cache entries.
|
||||
*
|
||||
* @param int $errorCode The error code to test, such as 404 or 410.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function fourXXCacheDelete($errorCode)
|
||||
{
|
||||
$now = $this->date();
|
||||
|
||||
Server::enqueue(
|
||||
[
|
||||
new Response(
|
||||
200, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, max-age=1000, must-revalidate',
|
||||
'Last-Modified' => $now,
|
||||
]
|
||||
),
|
||||
new Response(
|
||||
304, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, max-age=1000, must-revalidate',
|
||||
'Last-Modified' => $now,
|
||||
'Age' => 0,
|
||||
]
|
||||
),
|
||||
new Response(
|
||||
$errorCode, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, max-age=1000, must-revalidate',
|
||||
'Last-Modified' => $now,
|
||||
]
|
||||
),
|
||||
new Response(
|
||||
200, [
|
||||
'Date' => $now,
|
||||
'Cache-Control' => 'public, max-age=1000, must-revalidate',
|
||||
'Last-Modified' => $now,
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
$client = $this->setupClient();
|
||||
$response1 = $client->get('/foo');
|
||||
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
|
||||
$response2 = $client->get('/foo');
|
||||
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
|
||||
|
||||
try {
|
||||
$client->get('/foo');
|
||||
$this->fail($errorCode . ' was not thrown.');
|
||||
} catch (RequestException $e) {
|
||||
$response3 = $e->getResponse();
|
||||
$this->assertEquals($errorCode, $response3->getStatusCode());
|
||||
$this->assertEquals('MISS from GuzzleCache', $response3->getHeader('X-Cache-Lookup'));
|
||||
}
|
||||
|
||||
$response4 = $client->get('/foo');
|
||||
$this->assertEquals('MISS from GuzzleCache', $response4->getHeader('X-Cache-Lookup'));
|
||||
}
|
||||
}
|
||||
65
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/UtilsTest.php
vendored
Normal file
65
modules/productcomments/vendor/guzzlehttp/cache-subscriber/tests/UtilsTest.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace GuzzleHttp\Subscriber\Cache;
|
||||
|
||||
use GuzzleHttp\Message\MessageFactory;
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
/**
|
||||
* Test the Utils class.
|
||||
*
|
||||
* @class UtilsTest
|
||||
*/
|
||||
class UtilsTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Test that a max-age of zero isn't returned as null.
|
||||
*/
|
||||
public function testGetMaxAgeZero()
|
||||
{
|
||||
$messageFactory = new MessageFactory();
|
||||
$response = $messageFactory->createResponse(200, ['Cache-Control' => 's-maxage=0']);
|
||||
$this->assertSame(0, Utils::getMaxAge($response));
|
||||
|
||||
$response = $messageFactory->createResponse(200, ['Cache-Control' => 'max-age=0']);
|
||||
$this->assertSame(0, Utils::getMaxAge($response));
|
||||
|
||||
$response = $messageFactory->createResponse(200, ['Expires' => gmdate('D, d M Y H:i:s') . ' GMT']);
|
||||
$this->assertLessThanOrEqual(0, Utils::getMaxAge($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a response with no max-age information returns null.
|
||||
*/
|
||||
public function testGetMaxAgeNull()
|
||||
{
|
||||
$messageFactory = new MessageFactory();
|
||||
$response = $messageFactory->createResponse(200);
|
||||
$this->assertSame(null, Utils::getMaxAge($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a response that is zero fresh returns zero and not null.
|
||||
*/
|
||||
public function testGetFreshnessZero()
|
||||
{
|
||||
$messageFactory = new MessageFactory();
|
||||
$response = $messageFactory->createResponse(200,
|
||||
[
|
||||
'Cache-Control' => 'max-age=0',
|
||||
'Age' => 0,
|
||||
]);
|
||||
|
||||
$this->assertSame(0, Utils::getFreshness($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that responses that can't have freshness determined return null.
|
||||
*/
|
||||
public function testGetFreshnessNull()
|
||||
{
|
||||
$messageFactory = new MessageFactory();
|
||||
$response = $messageFactory->createResponse(200);
|
||||
$this->assertSame(null, Utils::getFreshness($response));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user