summaryrefslogtreecommitdiffstats
path: root/core/lib/Drupal/Core/PageCache
diff options
context:
space:
mode:
authorAlex Pott2014-09-20 10:14:29 (GMT)
committerAlex Pott2014-09-20 10:14:29 (GMT)
commitfb6c562c9eef10adf986959dbeca1e7bc6d490a9 (patch)
tree71eb2d6e0c1264ba8eef6dd2f6a2e5fa1af56bd7 /core/lib/Drupal/Core/PageCache
parent1476c56c62e4d84ef3e9a57029a92b1f0773cbc8 (diff)
Issue #2263981 by znerol, beejeebus: Introduce a robust and extensible page cache-policy framework.
Diffstat (limited to 'core/lib/Drupal/Core/PageCache')
-rw-r--r--core/lib/Drupal/Core/PageCache/ChainRequestPolicy.php65
-rw-r--r--core/lib/Drupal/Core/PageCache/ChainRequestPolicyInterface.php25
-rw-r--r--core/lib/Drupal/Core/PageCache/ChainResponsePolicy.php56
-rw-r--r--core/lib/Drupal/Core/PageCache/ChainResponsePolicyInterface.php24
-rw-r--r--core/lib/Drupal/Core/PageCache/DefaultRequestPolicy.php27
-rw-r--r--core/lib/Drupal/Core/PageCache/RequestPolicy/CommandLineOrUnsafeMethod.php38
-rw-r--r--core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php49
-rw-r--r--core/lib/Drupal/Core/PageCache/RequestPolicyInterface.php54
-rw-r--r--core/lib/Drupal/Core/PageCache/ResponsePolicy/KillSwitch.php42
-rw-r--r--core/lib/Drupal/Core/PageCache/ResponsePolicyInterface.php42
10 files changed, 422 insertions, 0 deletions
diff --git a/core/lib/Drupal/Core/PageCache/ChainRequestPolicy.php b/core/lib/Drupal/Core/PageCache/ChainRequestPolicy.php
new file mode 100644
index 0000000..23dc1d6
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/ChainRequestPolicy.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\ChainRequestPolicy.
+ */
+
+namespace Drupal\Core\PageCache;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Implements a compound request policy.
+ *
+ * When evaluating the compound policy, all of the contained rules are applied
+ * to the request. The overall result is computed according to the following
+ * rules:
+ *
+ * <ol>
+ * <li>Returns static::DENY if any of the rules evaluated to static::DENY</li>
+ * <li>Returns static::ALLOW if at least one of the rules evaluated to
+ * static::ALLOW and none to static::DENY</li>
+ * <li>Otherwise returns NULL</li>
+ * </ol>
+ */
+class ChainRequestPolicy implements ChainRequestPolicyInterface {
+
+ /**
+ * A list of policy rules to apply when this policy is evaluated.
+ *
+ * @var \Drupal\Core\PageCache\RequestPolicyInterface[]
+ */
+ protected $rules = [];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check(Request $request) {
+ $final_result = NULL;
+
+ foreach ($this->rules as $rule) {
+ $result = $rule->check($request);
+ if ($result === static::DENY) {
+ return $result;
+ }
+ elseif ($result === static::ALLOW) {
+ $final_result = $result;
+ }
+ elseif (isset($result)) {
+ throw new \UnexpectedValueException('Return value of RequestPolicyInterface::check() must be one of RequestPolicyInterface::ALLOW, RequestPolicyInterface::DENY or NULL');
+ }
+ }
+
+ return $final_result;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPolicy(RequestPolicyInterface $policy) {
+ $this->rules[] = $policy;
+ return $this;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/ChainRequestPolicyInterface.php b/core/lib/Drupal/Core/PageCache/ChainRequestPolicyInterface.php
new file mode 100644
index 0000000..e8548df
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/ChainRequestPolicyInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\ChainRequestPolicyInterface.
+ */
+
+namespace Drupal\Core\PageCache;
+
+/**
+ * Defines the interface for compound request policies.
+ */
+interface ChainRequestPolicyInterface extends RequestPolicyInterface {
+
+ /**
+ * Add a policy to the list of policy rules.
+ *
+ * @param \Drupal\Core\PageCache\RequestPolicyInterface $policy
+ * The request policy rule to add.
+ *
+ * @return $this
+ */
+ public function addPolicy(RequestPolicyInterface $policy);
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/ChainResponsePolicy.php b/core/lib/Drupal/Core/PageCache/ChainResponsePolicy.php
new file mode 100644
index 0000000..20d5e39
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/ChainResponsePolicy.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\ChainResponsePolicy.
+ */
+
+namespace Drupal\Core\PageCache;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Implements a compound response policy.
+ *
+ * When evaluating the compound policy, all of the contained rules are applied
+ * to the response. The overall result is computed according to the following
+ * rules:
+ *
+ * <ol>
+ * <li>Returns static::DENY if any of the rules evaluated to static::DENY</li>
+ * <li>Otherwise returns NULL</li>
+ * </ol>
+ */
+class ChainResponsePolicy implements ChainResponsePolicyInterface {
+
+ /**
+ * A list of policy rules to apply when this policy is checked.
+ *
+ * @var \Drupal\Core\PageCache\ResponsePolicyInterface[]
+ */
+ protected $rules = [];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check(Response $response, Request $request) {
+ foreach ($this->rules as $rule) {
+ $result = $rule->check($response, $request);
+ if ($result === static::DENY) {
+ return $result;
+ }
+ elseif (isset($result)) {
+ throw new \UnexpectedValueException('Return value of ResponsePolicyInterface::check() must be one of ResponsePolicyInterface::DENY or NULL');
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPolicy(ResponsePolicyInterface $policy) {
+ $this->rules[] = $policy;
+ return $this;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/ChainResponsePolicyInterface.php b/core/lib/Drupal/Core/PageCache/ChainResponsePolicyInterface.php
new file mode 100644
index 0000000..5018f19
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/ChainResponsePolicyInterface.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\ChainResponsePolicyInterface.
+ */
+
+namespace Drupal\Core\PageCache;
+
+/**
+ * Defines the interface for compound request policies.
+ */
+interface ChainResponsePolicyInterface extends ResponsePolicyInterface {
+
+ /**
+ * Add a policy to the list of policy rules.
+ *
+ * @param \Drupal\Core\PageCache\ResponsePolicyInterface $policy
+ * The request policy rule to add.
+ *
+ * @return $this
+ */
+ public function addPolicy(ResponsePolicyInterface $policy);
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/DefaultRequestPolicy.php b/core/lib/Drupal/Core/PageCache/DefaultRequestPolicy.php
new file mode 100644
index 0000000..b327753
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/DefaultRequestPolicy.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\DefaultRequestPolicy.
+ */
+
+namespace Drupal\Core\PageCache;
+
+/**
+ * The default page cache request policy.
+ *
+ * Delivery of cached pages is denied if either the application is running from
+ * the command line or the request was not initiated with a safe method (GET or
+ * HEAD). Also caching is only allowed for requests without a session cookie.
+ */
+class DefaultRequestPolicy extends ChainRequestPolicy {
+
+ /**
+ * Constructs the default page cache request policy.
+ */
+ public function __construct() {
+ $this->addPolicy(new RequestPolicy\CommandLineOrUnsafeMethod());
+ $this->addPolicy(new RequestPolicy\NoSessionOpen());
+ }
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/RequestPolicy/CommandLineOrUnsafeMethod.php b/core/lib/Drupal/Core/PageCache/RequestPolicy/CommandLineOrUnsafeMethod.php
new file mode 100644
index 0000000..66a3bf6
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/RequestPolicy/CommandLineOrUnsafeMethod.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\RequestPolicy\CommandLineOrUnsafeMethod.
+ */
+
+namespace Drupal\Core\PageCache\RequestPolicy;
+
+use Drupal\Core\PageCache\RequestPolicyInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Reject when running from the command line or when HTTP method is not safe.
+ *
+ * The policy denies caching if the request was initiated from the command line
+ * interface (drush) or the request method is neither GET nor HEAD (see RFC
+ * 2616, section 9.1.1 - Safe Methods).
+ */
+class CommandLineOrUnsafeMethod implements RequestPolicyInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check(Request $request) {
+ if ($this->isCli() || !$request->isMethodSafe()) {
+ return static::DENY;
+ }
+ }
+
+ /**
+ * Indicates whether this is a CLI request.
+ */
+ protected function isCli() {
+ return PHP_SAPI === 'cli';
+ }
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php b/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php
new file mode 100644
index 0000000..c18cf56
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\RequestPolicy\NoSessionOpen.
+ */
+
+namespace Drupal\Core\PageCache\RequestPolicy;
+
+use Drupal\Core\PageCache\RequestPolicyInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * A policy allowing delivery of cached pages when there is no session open.
+ *
+ * Do not serve cached pages to authenticated users, or to anonymous users when
+ * $_SESSION is non-empty. $_SESSION may contain status messages from a form
+ * submission, the contents of a shopping cart, or other userspecific content
+ * that should not be cached and displayed to other users.
+ */
+class NoSessionOpen implements RequestPolicyInterface {
+
+ /**
+ * The name of the session cookie.
+ *
+ * @var string
+ */
+ protected $sessionCookieName;
+
+ /**
+ * Constructs a new page cache session policy.
+ *
+ * @param string $session_cookie_name
+ * (optional) The name of the session cookie. Defaults to session_name().
+ */
+ public function __construct($session_cookie_name = NULL) {
+ $this->sessionCookieName = $session_cookie_name ?: session_name();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check(Request $request) {
+ if (!$request->cookies->has($this->sessionCookieName)) {
+ return static::ALLOW;
+ }
+ }
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/RequestPolicyInterface.php b/core/lib/Drupal/Core/PageCache/RequestPolicyInterface.php
new file mode 100644
index 0000000..3a4ef21
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/RequestPolicyInterface.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\RequestPolicyInterface.
+ */
+
+namespace Drupal\Core\PageCache;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines the interface for request policy implementations.
+ *
+ * The request policy is evaluated in order to determine whether delivery of a
+ * cached page should be attempted. The caller should do so if static::ALLOW is
+ * returned from the check() method.
+ */
+interface RequestPolicyInterface {
+
+ /**
+ * Allow delivery of cached pages.
+ */
+ const ALLOW = 'allow';
+
+ /**
+ * Deny delivery of cached pages.
+ */
+ const DENY = 'deny';
+
+ /**
+ * Determines whether delivery of a cached page should be attempted.
+ *
+ * Note that the request-policy check runs very early. In particular it is
+ * not possible to determine the logged in user. Also the current route match
+ * is not yet present when the check runs. Therefore, request-policy checks
+ * need to be designed in a way such that they do not depend on any other
+ * service and only take in account the information present on the incoming
+ * request.
+ *
+ * When matching against the request path, special attention is needed to
+ * support path prefixes which are often used on multilingual sites.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The incoming request object.
+ *
+ * @return string|NULL
+ * One of static::ALLOW, static::DENY or NULL. Calling code may attempt to
+ * deliver a cached page if static::ALLOW is returned. Returns NULL if the
+ * policy is not specified for the given request.
+ */
+ public function check(Request $request);
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/ResponsePolicy/KillSwitch.php b/core/lib/Drupal/Core/PageCache/ResponsePolicy/KillSwitch.php
new file mode 100644
index 0000000..62ecc45
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/ResponsePolicy/KillSwitch.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\ResponsePolicy\KillSwitch.
+ */
+
+namespace Drupal\Core\PageCache\ResponsePolicy;
+
+use Drupal\Core\PageCache\ResponsePolicyInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * A policy evaluating to static::DENY when the kill switch was triggered.
+ */
+class KillSwitch implements ResponsePolicyInterface {
+
+ /**
+ * A flag indicating whether the kill switch was triggered.
+ *
+ * @var bool
+ */
+ protected $kill = FALSE;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check(Response $response, Request $request) {
+ if ($this->kill) {
+ return static::DENY;
+ }
+ }
+
+ /**
+ * Deny any page caching on the current request.
+ */
+ public function trigger() {
+ $this->kill = TRUE;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/PageCache/ResponsePolicyInterface.php b/core/lib/Drupal/Core/PageCache/ResponsePolicyInterface.php
new file mode 100644
index 0000000..596f43f
--- /dev/null
+++ b/core/lib/Drupal/Core/PageCache/ResponsePolicyInterface.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PageCache\ResponsePolicyInterface.
+ */
+
+namespace Drupal\Core\PageCache;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Defines the interface for response policy implementations.
+ *
+ * The response policy is evaluated in order to determine whether a page should
+ * be stored a in the cache. Calling code should do so unless static::DENY is
+ * returned from the check() method.
+ */
+interface ResponsePolicyInterface {
+
+ /**
+ * Deny storage of a page in the cache.
+ */
+ const DENY = 'deny';
+
+ /**
+ * Determines whether it is save to store a page in the cache.
+ *
+ * @param \Symfony\Component\HttpFoundation\Response $response
+ * The response which is about to be sent to the client.
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object.
+ *
+ * @return string|NULL
+ * Either static::DENY or NULL. Calling code may attempt to store a page in
+ * the cache unless static::DENY is returned. Returns NULL if the policy
+ * policy is not specified for the given response.
+ */
+ public function check(Response $response, Request $request);
+
+}