summaryrefslogtreecommitdiff
path: root/vendor/symfony/http-kernel
diff options
context:
space:
mode:
authorGreg Roach <fisharebest@webtrees.net>2019-03-06 10:34:32 +0000
committerGreg Roach <fisharebest@webtrees.net>2019-03-06 10:34:32 +0000
commitd250673fd02596cce82a1fbc789be57c0f4bf3fd (patch)
tree20f32eb94c00fd78052da031b27fcc516c26f85a /vendor/symfony/http-kernel
parent9c811d4799f06f28d9bfb1399c2ffa6bab93c682 (diff)
downloadwebtrees-d250673fd02596cce82a1fbc789be57c0f4bf3fd.tar.gz
webtrees-d250673fd02596cce82a1fbc789be57c0f4bf3fd.tar.bz2
webtrees-d250673fd02596cce82a1fbc789be57c0f4bf3fd.zip
Update vendor dependencies
Diffstat (limited to 'vendor/symfony/http-kernel')
-rw-r--r--vendor/symfony/http-kernel/DataCollector/DataCollector.php6
-rw-r--r--vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php6
-rw-r--r--vendor/symfony/http-kernel/EventListener/ExceptionListener.php7
-rw-r--r--vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php2
-rw-r--r--vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php196
-rw-r--r--vendor/symfony/http-kernel/Kernel.php6
-rw-r--r--vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php229
7 files changed, 397 insertions, 55 deletions
diff --git a/vendor/symfony/http-kernel/DataCollector/DataCollector.php b/vendor/symfony/http-kernel/DataCollector/DataCollector.php
index 14ea230f57..efe1705179 100644
--- a/vendor/symfony/http-kernel/DataCollector/DataCollector.php
+++ b/vendor/symfony/http-kernel/DataCollector/DataCollector.php
@@ -34,9 +34,6 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
*/
private $cloner;
- /**
- * @internal
- */
public function serialize()
{
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
@@ -45,9 +42,6 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
return $isCalledFromOverridingMethod ? $this->data : serialize($this->data);
}
- /**
- * @internal
- */
public function unserialize($data)
{
$this->data = \is_array($data) ? $data : unserialize($data);
diff --git a/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php b/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php
index 04ea44a073..323ebf2054 100644
--- a/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php
+++ b/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php
@@ -133,9 +133,6 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->clonesIndex = 0;
}
- /**
- * @internal
- */
public function serialize()
{
if ($this->clonesCount !== $this->clonesIndex) {
@@ -152,9 +149,6 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
return $ser;
}
- /**
- * @internal
- */
public function unserialize($data)
{
$this->data = unserialize($data);
diff --git a/vendor/symfony/http-kernel/EventListener/ExceptionListener.php b/vendor/symfony/http-kernel/EventListener/ExceptionListener.php
index dd00797f5f..811f99bd9d 100644
--- a/vendor/symfony/http-kernel/EventListener/ExceptionListener.php
+++ b/vendor/symfony/http-kernel/EventListener/ExceptionListener.php
@@ -79,13 +79,12 @@ class ExceptionListener implements EventSubscriberInterface
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', $f->getClass(), $f->getMessage(), $e->getFile(), $e->getLine()));
- $wrapper = $e;
-
- while ($prev = $wrapper->getPrevious()) {
+ $prev = $e;
+ do {
if ($exception === $wrapper = $prev) {
throw $e;
}
- }
+ } while ($prev = $wrapper->getPrevious());
$prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
$prev->setAccessible(true);
diff --git a/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php
index 5ee09fb1a7..c01b09559a 100644
--- a/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php
+++ b/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php
@@ -80,7 +80,7 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
return SubRequestHandler::handle($this->kernel, $subRequest, HttpKernelInterface::SUB_REQUEST, false);
} catch (\Exception $e) {
// we dispatch the exception event to trigger the logging
- // the response that comes back is simply ignored
+ // the response that comes back is ignored
if (isset($options['ignore_errors']) && $options['ignore_errors'] && $this->dispatcher) {
$event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e);
diff --git a/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php b/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php
index 168e9564b0..862bac557d 100644
--- a/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php
+++ b/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php
@@ -5,10 +5,6 @@
*
* (c) Fabien Potencier <fabien@symfony.com>
*
- * This code is partially based on the Rack-Cache library by Ryan Tomayko,
- * which is released under the MIT license.
- * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801)
- *
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
@@ -28,30 +24,69 @@ use Symfony\Component\HttpFoundation\Response;
*/
class ResponseCacheStrategy implements ResponseCacheStrategyInterface
{
- private $cacheable = true;
+ /**
+ * Cache-Control headers that are sent to the final response if they appear in ANY of the responses.
+ */
+ private static $overrideDirectives = ['private', 'no-cache', 'no-store', 'no-transform', 'must-revalidate', 'proxy-revalidate'];
+
+ /**
+ * Cache-Control headers that are sent to the final response if they appear in ALL of the responses.
+ */
+ private static $inheritDirectives = ['public', 'immutable'];
+
private $embeddedResponses = 0;
- private $ttls = [];
- private $maxAges = [];
private $isNotCacheableResponseEmbedded = false;
+ private $age = 0;
+ private $flagDirectives = [
+ 'no-cache' => null,
+ 'no-store' => null,
+ 'no-transform' => null,
+ 'must-revalidate' => null,
+ 'proxy-revalidate' => null,
+ 'public' => null,
+ 'private' => null,
+ 'immutable' => null,
+ ];
+ private $ageDirectives = [
+ 'max-age' => null,
+ 's-maxage' => null,
+ 'expires' => null,
+ ];
/**
* {@inheritdoc}
*/
public function add(Response $response)
{
- if (!$response->isFresh() || !$response->isCacheable()) {
- $this->cacheable = false;
- } else {
- $maxAge = $response->getMaxAge();
- $this->ttls[] = $response->getTtl();
- $this->maxAges[] = $maxAge;
+ ++$this->embeddedResponses;
- if (null === $maxAge) {
- $this->isNotCacheableResponseEmbedded = true;
+ foreach (self::$overrideDirectives as $directive) {
+ if ($response->headers->hasCacheControlDirective($directive)) {
+ $this->flagDirectives[$directive] = true;
}
}
- ++$this->embeddedResponses;
+ foreach (self::$inheritDirectives as $directive) {
+ if (false !== $this->flagDirectives[$directive]) {
+ $this->flagDirectives[$directive] = $response->headers->hasCacheControlDirective($directive);
+ }
+ }
+
+ $age = $response->getAge();
+ $this->age = max($this->age, $age);
+
+ if ($this->willMakeFinalResponseUncacheable($response)) {
+ $this->isNotCacheableResponseEmbedded = true;
+
+ return;
+ }
+
+ $this->storeRelativeAgeDirective('max-age', $response->headers->getCacheControlDirective('max-age'), $age);
+ $this->storeRelativeAgeDirective('s-maxage', $response->headers->getCacheControlDirective('s-maxage') ?: $response->headers->getCacheControlDirective('max-age'), $age);
+
+ $expires = $response->getExpires();
+ $expires = null !== $expires ? $expires->format('U') - $response->getDate()->format('U') : null;
+ $this->storeRelativeAgeDirective('expires', $expires >= 0 ? $expires : null, 0);
}
/**
@@ -64,33 +99,124 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
return;
}
- // Remove validation related headers in order to avoid browsers using
- // their own cache, because some of the response content comes from
- // at least one embedded response (which likely has a different caching strategy).
- if ($response->isValidateable()) {
- $response->setEtag(null);
- $response->setLastModified(null);
+ // Remove validation related headers of the master response,
+ // because some of the response content comes from at least
+ // one embedded response (which likely has a different caching strategy).
+ $response->setEtag(null);
+ $response->setLastModified(null);
+
+ $this->add($response);
+
+ $response->headers->set('Age', $this->age);
+
+ if ($this->isNotCacheableResponseEmbedded) {
+ $response->setExpires($response->getDate());
+
+ if ($this->flagDirectives['no-store']) {
+ $response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
+ } else {
+ $response->headers->set('Cache-Control', 'no-cache, must-revalidate');
+ }
+
+ return;
}
- if (!$response->isFresh() || !$response->isCacheable()) {
- $this->cacheable = false;
+ $flags = array_filter($this->flagDirectives);
+
+ if (isset($flags['must-revalidate'])) {
+ $flags['no-cache'] = true;
}
- if (!$this->cacheable) {
- $response->headers->set('Cache-Control', 'no-cache, must-revalidate');
+ $response->headers->set('Cache-Control', implode(', ', array_keys($flags)));
- return;
+ $maxAge = null;
+ $sMaxage = null;
+
+ if (\is_numeric($this->ageDirectives['max-age'])) {
+ $maxAge = $this->ageDirectives['max-age'] + $this->age;
+ $response->headers->addCacheControlDirective('max-age', $maxAge);
}
- $this->ttls[] = $response->getTtl();
- $this->maxAges[] = $response->getMaxAge();
+ if (\is_numeric($this->ageDirectives['s-maxage'])) {
+ $sMaxage = $this->ageDirectives['s-maxage'] + $this->age;
- if ($this->isNotCacheableResponseEmbedded) {
- $response->headers->removeCacheControlDirective('s-maxage');
- } elseif (null !== $maxAge = min($this->maxAges)) {
- $response->setSharedMaxAge($maxAge);
- $response->headers->set('Age', $maxAge - min($this->ttls));
+ if ($maxAge !== $sMaxage) {
+ $response->headers->addCacheControlDirective('s-maxage', $sMaxage);
+ }
+ }
+
+ if (\is_numeric($this->ageDirectives['expires'])) {
+ $date = clone $response->getDate();
+ $date->modify('+'.($this->ageDirectives['expires'] + $this->age).' seconds');
+ $response->setExpires($date);
+ }
+ }
+
+ /**
+ * RFC2616, Section 13.4.
+ *
+ * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
+ *
+ * @return bool
+ */
+ private function willMakeFinalResponseUncacheable(Response $response)
+ {
+ // RFC2616: A response received with a status code of 200, 203, 300, 301 or 410
+ // MAY be stored by a cache […] unless a cache-control directive prohibits caching.
+ if ($response->headers->hasCacheControlDirective('no-cache')
+ || $response->headers->getCacheControlDirective('no-store')
+ ) {
+ return true;
+ }
+
+ // Last-Modified and Etag headers cannot be merged, they render the response uncacheable
+ // by default (except if the response also has max-age etc.).
+ if (\in_array($response->getStatusCode(), [200, 203, 300, 301, 410])
+ && null === $response->getLastModified()
+ && null === $response->getEtag()
+ ) {
+ return false;
+ }
+
+ // RFC2616: A response received with any other status code (e.g. status codes 302 and 307)
+ // MUST NOT be returned in a reply to a subsequent request unless there are
+ // cache-control directives or another header(s) that explicitly allow it.
+ $cacheControl = ['max-age', 's-maxage', 'must-revalidate', 'proxy-revalidate', 'public', 'private'];
+ foreach ($cacheControl as $key) {
+ if ($response->headers->hasCacheControlDirective($key)) {
+ return false;
+ }
+ }
+
+ if ($response->headers->has('Expires')) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Store lowest max-age/s-maxage/expires for the final response.
+ *
+ * The response might have been stored in cache a while ago. To keep things comparable,
+ * we have to subtract the age so that the value is normalized for an age of 0.
+ *
+ * If the value is lower than the currently stored value, we update the value, to keep a rolling
+ * minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
+ *
+ * @param string $directive
+ * @param int|null $value
+ * @param int $age
+ */
+ private function storeRelativeAgeDirective($directive, $value, $age)
+ {
+ if (null === $value) {
+ $this->ageDirectives[$directive] = false;
+ }
+
+ if (false !== $this->ageDirectives[$directive]) {
+ $value = $value - $age;
+ $this->ageDirectives[$directive] = null !== $this->ageDirectives[$directive] ? min($this->ageDirectives[$directive], $value) : $value;
}
- $response->setMaxAge(0);
}
}
diff --git a/vendor/symfony/http-kernel/Kernel.php b/vendor/symfony/http-kernel/Kernel.php
index c5e351ca3a..6737caee9e 100644
--- a/vendor/symfony/http-kernel/Kernel.php
+++ b/vendor/symfony/http-kernel/Kernel.php
@@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $requestStackSize = 0;
private $resetServices = false;
- const VERSION = '4.2.3';
- const VERSION_ID = 40203;
+ const VERSION = '4.2.4';
+ const VERSION_ID = 40204;
const MAJOR_VERSION = 4;
const MINOR_VERSION = 2;
- const RELEASE_VERSION = 3;
+ const RELEASE_VERSION = 4;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '07/2019';
diff --git a/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php
index 6d67a17739..22cadf7129 100644
--- a/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php
+++ b/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php
@@ -237,4 +237,233 @@ class ResponseCacheStrategyTest extends TestCase
$this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage'));
$this->assertFalse($masterResponse->isValidateable());
}
+
+ /**
+ * @dataProvider cacheControlMergingProvider
+ */
+ public function testCacheControlMerging(array $expects, array $master, array $surrogates)
+ {
+ $cacheStrategy = new ResponseCacheStrategy();
+ $buildResponse = function ($config) {
+ $response = new Response();
+
+ foreach ($config as $key => $value) {
+ switch ($key) {
+ case 'age':
+ $response->headers->set('Age', $value);
+ break;
+
+ case 'expires':
+ $expires = clone $response->getDate();
+ $expires->modify('+'.$value.' seconds');
+ $response->setExpires($expires);
+ break;
+
+ case 'max-age':
+ $response->setMaxAge($value);
+ break;
+
+ case 's-maxage':
+ $response->setSharedMaxAge($value);
+ break;
+
+ case 'private':
+ $response->setPrivate();
+ break;
+
+ case 'public':
+ $response->setPublic();
+ break;
+
+ default:
+ $response->headers->addCacheControlDirective($key, $value);
+ }
+ }
+
+ return $response;
+ };
+
+ foreach ($surrogates as $config) {
+ $cacheStrategy->add($buildResponse($config));
+ }
+
+ $response = $buildResponse($master);
+ $cacheStrategy->update($response);
+
+ foreach ($expects as $key => $value) {
+ if ('expires' === $key) {
+ $this->assertSame($value, $response->getExpires()->format('U') - $response->getDate()->format('U'));
+ } elseif ('age' === $key) {
+ $this->assertSame($value, $response->getAge());
+ } elseif (true === $value) {
+ $this->assertTrue($response->headers->hasCacheControlDirective($key), sprintf('Cache-Control header must have "%s" flag', $key));
+ } elseif (false === $value) {
+ $this->assertFalse(
+ $response->headers->hasCacheControlDirective($key),
+ sprintf('Cache-Control header must NOT have "%s" flag', $key)
+ );
+ } else {
+ $this->assertSame($value, $response->headers->getCacheControlDirective($key), sprintf('Cache-Control flag "%s" should be "%s"', $key, $value));
+ }
+ }
+ }
+
+ public function cacheControlMergingProvider()
+ {
+ yield 'result is public if all responses are public' => [
+ ['private' => false, 'public' => true],
+ ['public' => true],
+ [
+ ['public' => true],
+ ],
+ ];
+
+ yield 'result is private by default' => [
+ ['private' => true, 'public' => false],
+ ['public' => true],
+ [
+ [],
+ ],
+ ];
+
+ yield 'combines public and private responses' => [
+ ['must-revalidate' => false, 'private' => true, 'public' => false],
+ ['public' => true],
+ [
+ ['private' => true],
+ ],
+ ];
+
+ yield 'inherits no-cache from surrogates' => [
+ ['no-cache' => true, 'public' => false],
+ ['public' => true],
+ [
+ ['no-cache' => true],
+ ],
+ ];
+
+ yield 'inherits no-store from surrogate' => [
+ ['no-store' => true, 'public' => false],
+ ['public' => true],
+ [
+ ['no-store' => true],
+ ],
+ ];
+
+ yield 'resolve to lowest possible max-age' => [
+ ['public' => false, 'private' => true, 's-maxage' => false, 'max-age' => '60'],
+ ['public' => true, 'max-age' => 3600],
+ [
+ ['private' => true, 'max-age' => 60],
+ ],
+ ];
+
+ yield 'resolves multiple max-age' => [
+ ['public' => false, 'private' => true, 's-maxage' => false, 'max-age' => '60'],
+ ['private' => true, 'max-age' => 100],
+ [
+ ['private' => true, 'max-age' => 3600],
+ ['public' => true, 'max-age' => 60, 's-maxage' => 60],
+ ['private' => true, 'max-age' => 60],
+ ],
+ ];
+
+ yield 'merge max-age and s-maxage' => [
+ ['public' => true, 's-maxage' => '60', 'max-age' => null],
+ ['public' => true, 's-maxage' => 3600],
+ [
+ ['public' => true, 'max-age' => 60],
+ ],
+ ];
+
+ yield 'result is private when combining private responses' => [
+ ['no-cache' => false, 'must-revalidate' => false, 'private' => true],
+ ['s-maxage' => 60, 'private' => true],
+ [
+ ['s-maxage' => 60, 'private' => true],
+ ],
+ ];
+
+ yield 'result can have s-maxage and max-age' => [
+ ['public' => true, 'private' => false, 's-maxage' => '60', 'max-age' => '30'],
+ ['s-maxage' => 100, 'max-age' => 2000],
+ [
+ ['s-maxage' => 1000, 'max-age' => 30],
+ ['s-maxage' => 500, 'max-age' => 500],
+ ['s-maxage' => 60, 'max-age' => 1000],
+ ],
+ ];
+
+ yield 'does not set headers without value' => [
+ ['max-age' => null, 's-maxage' => null, 'public' => null],
+ ['private' => true],
+ [
+ ['private' => true],
+ ],
+ ];
+
+ yield 'max-age 0 is sent to the client' => [
+ ['private' => true, 'max-age' => '0'],
+ ['max-age' => 0, 'private' => true],
+ [
+ ['max-age' => 60, 'private' => true],
+ ],
+ ];
+
+ yield 'max-age is relative to age' => [
+ ['max-age' => '240', 'age' => 60],
+ ['max-age' => 180],
+ [
+ ['max-age' => 600, 'age' => 60],
+ ],
+ ];
+
+ yield 'retains lowest age of all responses' => [
+ ['max-age' => '160', 'age' => 60],
+ ['max-age' => 600, 'age' => 60],
+ [
+ ['max-age' => 120, 'age' => 20],
+ ],
+ ];
+
+ yield 'max-age can be less than age, essentially expiring the response' => [
+ ['age' => 120, 'max-age' => '90'],
+ ['max-age' => 90, 'age' => 120],
+ [
+ ['max-age' => 120, 'age' => 60],
+ ],
+ ];
+
+ yield 'max-age is 0 regardless of age' => [
+ ['max-age' => '0'],
+ ['max-age' => 60],
+ [
+ ['max-age' => 0, 'age' => 60],
+ ],
+ ];
+
+ yield 'max-age is not negative' => [
+ ['max-age' => '0'],
+ ['max-age' => 0],
+ [
+ ['max-age' => 0, 'age' => 60],
+ ],
+ ];
+
+ yield 'calculates lowest Expires header' => [
+ ['expires' => 60],
+ ['expires' => 60],
+ [
+ ['expires' => 120],
+ ],
+ ];
+
+ yield 'calculates Expires header relative to age' => [
+ ['expires' => 210, 'age' => 120],
+ ['expires' => 90],
+ [
+ ['expires' => 600, 'age' => '120'],
+ ],
+ ];
+ }
}