diff --git a/Helper/VclGenerator.php b/Helper/VclGenerator.php index e4bff4a..a2c5288 100644 --- a/Helper/VclGenerator.php +++ b/Helper/VclGenerator.php @@ -25,11 +25,13 @@ class VclGenerator extends AbstractHelper { private $magentoEdition; private $magentoVersion; private $moduleVersion; + protected $scopeConfig; function __construct ( Data $data, LazyVclParser $parser, - ProductMetadataInterface $metadata + ProductMetadataInterface $metadata, + ScopeConfigInterface $scopeConfig ) { $this->data = $data; $this->parser = $parser; @@ -37,6 +39,7 @@ function __construct ( $this->magentoVersion = $this->metadata->getVersion (); $this->magentoEdition = $this->metadata->getEdition (); $this->moduleVersion = $this->data->getModuleVersion (); + $this->scopeConfig = $scopeConfig; } public function insertSubHooksInclude ( $root ) { @@ -216,6 +219,67 @@ public function versionEndpoint ( $node ) { return $node; } +public function insertXkey($node) { + $isXkeyEnabled = $this->scopeConfig->getValue( + 'system/full_page_cache/varnish/enable_xkey', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $isSoftPurgingUsed = $this->scopeConfig->getValue( + 'system/full_page_cache/varnish/use_soft_purging', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if (!$isXkeyEnabled || !$isSoftPurgingUsed) { + return $node; + } + + if ($node["type"] == "import" && strpos($node["raw"], "import std;") !== false) { + $importXkey = "import xkey;\n"; + if (strpos($node["value"], $importXkey) === false) { + $node["value"] .= $importXkey; + $node["raw"] .= $importXkey; + } + return $node; + } + + if ($node["type"] == "sub" && $node["identifier"] == "vcl_recv") { + $value = implode("\n", [ + " # Full Page Cache flush", + " if (req.http.X-Magento-Tags-Pattern == \".*\") {", + " ban(\"obj.http.X-Magento-Tags ~ \" + req.http.X-Magento-Tags-Pattern);", + " } elseif (req.http.X-Magento-Tags-Pattern) {", + " set req.http.X-Magento-Tags-Pattern = regsuball(req.http.X-Magento-Tags-Pattern, \"[^a-zA-Z0-9_-]+\", \" \");", + " set req.http.X-Magento-Tags-Pattern = regsuball(req.http.X-Magento-Tags-Pattern, \"(^\\s*)|(\\s*$)\", \"\");", + " set req.http.n-gone = xkey.softpurge(req.http.X-Magento-Tags-Pattern);", + " return (synth(200, \"Invalidated \" + req.http.n-gone + \" objects\"));", + " }", + ]); + + $value = "\n$value\n" . $node["value"]; + $raw = "sub " . $node["identifier"] . " {\n" . $value . "\n}"; + $node["value"] = $value; + $node["raw"] = $raw; + } + + if ($node["type"] == "sub" && $node["identifier"] == "vcl_backend_response") { + $value = implode("\n", [ + " set beresp.grace = 3h;", + " if (beresp.http.X-Magento-Tags) {", + " set beresp.http.Grace = beresp.grace;", + " set beresp.http.xkey = regsuball(beresp.http.X-Magento-Tags, \",\", \" \");", + " set beresp.http.X-Magento-Tags = \"fpc\";", + " }", + ]); + + $value = "\n$value\n" . $node["value"]; + $raw = "sub " . $node["identifier"] . " {\n" . $value . "\n}"; + $node["value"] = $value; + $node["raw"] = $raw; + } + + return $node; +} + function generateDefault ( $vcl ) { $this->parser->setData ( $vcl ); $root = $this->parser->getAST (); @@ -230,6 +294,7 @@ function generateDefault ( $vcl ) { $root = $this->parser->visit ( $root, [ $this, "insertDeliver" ] ); $root = $this->parser->visit ( $root, [ $this, "insertSubHooks" ] ); $root = $this->insertSubHooksInclude ( $root ); + $root = $this->parser->visit($root, [$this, "insertXkey"]); return $this->parser->getString ( $root ); } diff --git a/conf/varnish/default.vcl b/conf/varnish/default.vcl index 586b97a..c8d5cb9 100644 --- a/conf/varnish/default.vcl +++ b/conf/varnish/default.vcl @@ -1,6 +1,42 @@ vcl 4.1; +{{if enable_xkey}} +import xkey; +{{/if}} + backend default { .host = "magento"; .port = "8080"; } + +sub vcl_recv { + {{if enable_xkey}} + # Full Page Cache flush + if (req.http.X-Magento-Tags-Pattern == ".*") { + ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); + } elseif (req.http.X-Magento-Tags-Pattern) { + # replace "((^|,)cat_c(,|$))|((^|,)cat_p(,|$))" to be "cat_c cat_p" + set req.http.X-Magento-Tags-Pattern = regsuball(req.http.X-Magento-Tags-Pattern, "[^a-zA-Z0-9_-]+" ," "); + # trim spaces + set req.http.X-Magento-Tags-Pattern = regsuball(req.http.X-Magento-Tags-Pattern, "(^\s*)|(\s*$)" ,""); + set req.http.n-gone = xkey.softpurge(req.http.X-Magento-Tags-Pattern); + return (synth(200, "Invalidated " + req.http.n-gone + " objects")); + } + {{/if}} +} + +sub vcl_backend_response { + # Period to allow stale cache to be served + set beresp.grace = 3h; + + {{if enable_xkey}} + # using xkey + if (beresp.http.X-Magento-Tags) { + set beresp.http.Grace = beresp.grace; + # set space separated xkey + set beresp.http.xkey = regsuball(beresp.http.X-Magento-Tags, ",", " "); + # reset beresp.http.X-Magento-Tags with some common general value + set beresp.http.X-Magento-Tags = "fpc"; + } + {{/if}} +} diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index fdcf27d..5405fe9 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -26,6 +26,22 @@ 1 + + + Varnish VMOD xkey.]]> + Magento\Config\Model\Config\Source\Yesno + + 2 + + + + + Use soft purging instead of hard purging. This requires Xkey vmod to be installed + Magento\Config\Model\Config\Source\Yesno + + 1 + + 2 diff --git a/etc/config.xml b/etc/config.xml index f0b4c86..7cc4c24 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -13,6 +13,7 @@ + 0