diff --git a/CHANGELOG.md b/CHANGELOG.md index cc646b25f..f58411bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -440,4 +440,11 @@ Magento CE 1.8+ or EE 1.13+, see [these instructions](https://github.com/nexcess * [#1155] Update TECHNICAL_NOTES.md. (@GLips) ### RELEASE-0.6.9 + * [#1162] Support PHP7. (@allardhoeve) + * [#1173] Fix load balancing for Varnish 4. (@kleinmann) + * [#1182] Update version-3.vcl normalisation. (@gewaechshaus) + +### RELEASE-0.7.0 + + diff --git a/app/code/community/Nexcessnet/Turpentine/Block/Poll/ActivePoll.php b/app/code/community/Nexcessnet/Turpentine/Block/Poll/ActivePoll.php index 8322aa069..56343127f 100644 --- a/app/code/community/Nexcessnet/Turpentine/Block/Poll/ActivePoll.php +++ b/app/code/community/Nexcessnet/Turpentine/Block/Poll/ActivePoll.php @@ -21,11 +21,19 @@ class Nexcessnet_Turpentine_Block_Poll_ActivePoll extends Mage_Poll_Block_ActivePoll { - public function setTemplate($template) - { - $this->_template = $template; - $this->setPollTemplate('turpentine/ajax.phtml', 'poll'); - $this->setPollTemplate('turpentine/ajax.phtml', 'results'); - return $this; - } + public function setTemplate($template) + { + if ((Mage::getConfig()->getModuleConfig('Mage_Poll')->is('active', 'true')) && + (!Mage::getStoreConfig('advanced/modules_disable_output/Mage_Poll'))) + { + $this->_template = $template; + $this->setPollTemplate('turpentine/ajax.phtml', 'poll'); + $this->setPollTemplate('turpentine/ajax.phtml', 'results'); + } + else + { + // Mage_Poll is disabled, so do nothing + } + return $this; + } } diff --git a/app/code/community/Nexcessnet/Turpentine/Model/Config/Select/NormalizeHost.php b/app/code/community/Nexcessnet/Turpentine/Model/Config/Select/NormalizeHost.php new file mode 100644 index 000000000..010c47d15 --- /dev/null +++ b/app/code/community/Nexcessnet/Turpentine/Model/Config/Select/NormalizeHost.php @@ -0,0 +1,34 @@ +'yes', 'label'=>Mage::helper('adminhtml')->__('Enable')), + array('value'=>'yes_forwarded_host', 'label'=>$helper->__('Yes, use HTTP X-Forwarded-Host Header')), + array('value'=>'no', 'label'=>Mage::helper('adminhtml')->__('Disable')), + ); + } +} diff --git a/app/code/community/Nexcessnet/Turpentine/Model/Observer/Esi.php b/app/code/community/Nexcessnet/Turpentine/Model/Observer/Esi.php index 0ae107d8c..86d598fc3 100644 --- a/app/code/community/Nexcessnet/Turpentine/Model/Observer/Esi.php +++ b/app/code/community/Nexcessnet/Turpentine/Model/Observer/Esi.php @@ -226,12 +226,19 @@ public function injectEsi($eventObject) { $debugHelper->logInfo( 'Checking ESI block candidate: %s', $blockObject->getNameInLayout() ? $blockObject->getNameInLayout() : $blockObject->getModuleName() ); - } + + $debugHelper->logInfo( "-- block testing: shouldResponseUseEsi = " . $esiHelper->shouldResponseUseEsi()); + $debugHelper->logInfo( "-- block testing: instanceof Mage_Core_Block_Template = " . $blockObject instanceof Mage_Core_Block_Template ); + $debugHelper->logInfo( "-- block testing: Esi Options = " . print_r($blockObject->getEsiOptions(), true) ); + } if ($esiHelper->shouldResponseUseEsi() && $blockObject instanceof Mage_Core_Block_Template && $esiOptions = $blockObject->getEsiOptions()) { if ((isset($esiOptions['disableEsiInjection'])) && ($esiOptions['disableEsiInjection'] == 1)) { + if ($esiHelper->getEsiBlockLogEnabled()) { + $debugHelper->logInfo("-- ESI Injection disabled"); + } return; } diff --git a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Admin/Socket.php b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Admin/Socket.php index 4c6e14b48..3df98e393 100644 --- a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Admin/Socket.php +++ b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Admin/Socket.php @@ -499,6 +499,9 @@ protected function _command($verb, $okCode = 200) { "Got unexpected response code from Varnish: %d\n%s", $response['code'], $response['text'] )); } else { + if (Mage::getStoreConfig('turpentine_varnish/general/varnish_log_commands')) { + Mage::helper('turpentine/debug')->logDebug('VARNISH command sent: ' . $data); + } return $response; } } diff --git a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Abstract.php b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Abstract.php index 86805aea8..88fb04b75 100644 --- a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Abstract.php +++ b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Abstract.php @@ -110,8 +110,8 @@ public function save($generatedConfig) { * @return string */ protected function _getVclTemplateFilename($baseFilename) { - $extensionDir = Mage::getModuleDir('', 'Nexcessnet_Turpentine'); - return sprintf('%s/misc/%s', $extensionDir, $baseFilename); + $extensionDir = Mage::getModuleDir('', 'Nexcessnet_Turpentine'); + return sprintf('%s/misc/%s', $extensionDir, $baseFilename); } /** @@ -136,6 +136,23 @@ protected function _getCustomIncludeFilename() { array('root_dir' => Mage::getBaseDir()) ); } + + /** + * Get the custom VCL template, if it exists + * Returns 'null' if the file doesn't exist + * + * @return string + */ + protected function _getCustomTemplateFilename() { + $filePath = $this->_formatTemplate( + Mage::getStoreConfig('turpentine_varnish/servers/custom_vcl_template'), + array('root_dir' => Mage::getBaseDir()) + ); + if (is_file($filePath)) { return $filePath; } + else { return null; } + } + + /** * Format a template string, replacing {{keys}} with the appropriate values * and remove unspecified keys @@ -173,7 +190,11 @@ protected function _vcl_call($subroutine) { */ protected function _getAdminFrontname() { if (Mage::getStoreConfig('admin/url/use_custom_path')) { - return Mage::getStoreConfig('admin/url/custom_path'); + if(Mage::getStoreConfig('web/url/use_store')) { + return Mage::getModel('core/store')->load(0)->getCode() . "/" . Mage::getStoreConfig('admin/url/custom_path'); + } else { + return Mage::getStoreConfig('admin/url/custom_path'); + } } else { return (string) Mage::getConfig()->getNode( 'admin/routers/adminhtml/args/frontName' ); @@ -769,16 +790,6 @@ protected function _vcl_sub_normalize_user_agent() { $tpl = << $this->_getNormalizeHostTarget() )); } + /** + * Get the Host normalization sub routine + * + * @return string + */ + protected function _vcl_sub_normalize_host_forwarded() { + $tpl = <<_formatTemplate($tpl, array()); + } + /** * Get the hostname for cookie normalization * @@ -990,8 +1016,13 @@ protected function _getTemplateVars() { if (Mage::getStoreConfig('turpentine_vcl/normalization/user_agent')) { $vars['normalize_user_agent'] = $this->_vcl_sub_normalize_user_agent(); } - if (Mage::getStoreConfig('turpentine_vcl/normalization/host')) { - $vars['normalize_host'] = $this->_vcl_sub_normalize_host(); + Mage::log(Mage::getStoreConfig('turpentine_vcl/normalization/host')); + if (Mage::getStoreConfig('turpentine_vcl/normalization/host') && Mage::getStoreConfig('turpentine_vcl/normalization/host') != 'no') { + if (Mage::getStoreConfig('turpentine_vcl/normalization/host') == "yes_forwarded_host") { + $vars['normalize_host'] = $this->_vcl_sub_normalize_host_forwarded(); + } else { + $vars['normalize_host'] = $this->_vcl_sub_normalize_host(); + } } if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_regex')) { $vars['normalize_cookie_regex'] = $this->_getNormalizeCookieRegex(); diff --git a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version3.php b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version3.php index 745ec6076..2247404b0 100644 --- a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version3.php +++ b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version3.php @@ -31,7 +31,14 @@ class Nexcessnet_Turpentine_Model_Varnish_Configurator_Version3 * @return string */ public function generate($doClean = true) { - $tplFile = $this->_getVclTemplateFilename(self::VCL_TEMPLATE_FILE); + // first, check if a custom template is set + $customTemplate = $this->_getCustomTemplateFilename(); + if ($customTemplate) { + $tplFile = $customTemplate; + } + else { + $tplFile = $this->_getVclTemplateFilename(self::VCL_TEMPLATE_FILE); + } $vcl = $this->_formatTemplate(file_get_contents($tplFile), $this->_getTemplateVars()); return $doClean ? $this->_cleanVcl($vcl) : $vcl; diff --git a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version4.php b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version4.php index 273c09f1a..72512cbcf 100644 --- a/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version4.php +++ b/app/code/community/Nexcessnet/Turpentine/Model/Varnish/Configurator/Version4.php @@ -31,7 +31,14 @@ class Nexcessnet_Turpentine_Model_Varnish_Configurator_Version4 * @return string */ public function generate($doClean = true) { - $tplFile = $this->_getVclTemplateFilename(self::VCL_TEMPLATE_FILE); + // first, check if a custom template is set + $customTemplate = $this->_getCustomTemplateFilename(); + if ($customTemplate) { + $tplFile = $customTemplate; + } + else { + $tplFile = $this->_getVclTemplateFilename(self::VCL_TEMPLATE_FILE); + } $vcl = $this->_formatTemplate(file_get_contents($tplFile), $this->_getTemplateVars()); return $doClean ? $this->_cleanVcl($vcl) : $vcl; @@ -55,6 +62,134 @@ protected function _getTemplateVars() { $vars = parent::_getTemplateVars(); $vars['advanced_session_validation'] = $this->_getAdvancedSessionValidation(); + + if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') { + $vars['directors'] = $this->_vcl_directors(); + $vars['admin_backend_hint'] = 'vdir_admin.backend()'; + $vars['set_backend_hint'] = 'set req.backend_hint = vdir.backend();'; + } else { + $vars['directors'] = ''; + $vars['admin_backend_hint'] = 'admin'; + $vars['set_backend_hint'] = ''; + } + return $vars; } + + protected function _vcl_directors() + { + $tpl = <<cleanExplode(PHP_EOL, + Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes_admin')); + } else { + $adminBackendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL, + Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes')); + } + + $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL, + Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes')); + + for($i = 0, $iMax = count($backendNodes); $i < $iMax; $i++) { + $tpl .= <<_formatTemplate($tpl, $vars); + } + + /** + * Format a VCL director declaration, for load balancing + * + * @param string $name name of the director, also used to select config settings + * @param array $backendOptions options for each backend + * @return string + */ + protected function _vcl_director($name, $backendOptions) { + $tpl = <<cleanExplode(PHP_EOL, + Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes_admin')); + $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url_admin'); + $prefix = 'admin'; + } else { + $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL, + Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes')); + $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url'); + + if('admin' == $name) { + $prefix = 'admin'; + } else { + $prefix = ''; + } + } + + $backends = ''; + $number = 0; + foreach ($backendNodes as $backendNode) { + $parts = explode(':', $backendNode, 2); + $host = (empty($parts[0])) ? '127.0.0.1' : $parts[0]; + $port = (empty($parts[1])) ? '80' : $parts[1]; + $backends .= $this->_vcl_director_backend($host, $port, $prefix . $number, $probeUrl, $backendOptions); + + $number++; + } + $vars = array( + 'name' => $name, + 'backends' => $backends + ); + return $this->_formatTemplate($tpl, $vars); + } + + /** + * Format a VCL backend declaration to put inside director + * + * @param string $host backend host + * @param string $port backend port + * @param string $descriptor backend descriptor + * @param string $probeUrl URL to check if backend is up + * @param array $options extra options for backend + * @return string + */ + protected function _vcl_director_backend($host, $port, $descriptor, $probeUrl = '', $options = array()) { + $tpl = << $host, + 'port' => $port, + 'probe' => '' + ); + if ( ! empty($probeUrl)) { + $vars['probe'] = $this->_vcl_get_probe($probeUrl); + } + $str = $this->_formatTemplate($tpl, $vars); + foreach ($options as $key => $value) { + $str .= sprintf(' .%s = %s;', $key, $value).PHP_EOL; + } + $str .= <<generateBlocks($node); } } + if ($roots = $layout->getNode()->xpath('//block[@name=\'root\']')) { + foreach (array('formkey') as $globalBlock) { + if ($blocks = $layout->getNode()->xpath(sprintf('//block[@name=\'%s\']', $globalBlock))) { + $dummy = $roots[0]->addChild('reference'); + $dummy->appendChild($blocks[0]); + $layout->generateBlocks($dummy); + } + } + } $block = $layout->getBlock($esiData->getNameInLayout()); if ( ! $this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) { diff --git a/app/code/community/Nexcessnet/Turpentine/etc/config.xml b/app/code/community/Nexcessnet/Turpentine/etc/config.xml index 404ca74b5..6bc619ebd 100644 --- a/app/code/community/Nexcessnet/Turpentine/etc/config.xml +++ b/app/code/community/Nexcessnet/Turpentine/etc/config.xml @@ -47,6 +47,7 @@ 127.0.0.1:6082 {{root_dir}}/var/default.vcl {{root_dir}}/app/code/community/Nexcessnet/Turpentine/misc/custom_include.vcl + diff --git a/app/code/community/Nexcessnet/Turpentine/etc/system.xml b/app/code/community/Nexcessnet/Turpentine/etc/system.xml index a5960bbd0..29776d6ad 100644 --- a/app/code/community/Nexcessnet/Turpentine/etc/system.xml +++ b/app/code/community/Nexcessnet/Turpentine/etc/system.xml @@ -82,6 +82,16 @@ 0 0 + + + Log all commands sent to Varnish by Turpentine in the log specified (custom if enabled). Caution - can cause logs to grow quickly! + select + turpentine/config_select_toggle + 45 + 1 + 0 + 0 + Log block names for adding ESI, only enable when needed to avoid performance hit @@ -92,6 +102,8 @@ 0 0 + + Enable fixing the messages block to load via AJAX, disable if you already have an extension that does this @@ -242,6 +254,15 @@ 0 0 + + + text + If defined and present, this template will be used instead of the default VCL template appropriate for the version of Varnish. + 50 + 1 + 0 + 0 + @@ -413,7 +434,7 @@ Force requests to be for a specific domain name, will probably break most multi-store setups select - turpentine/config_select_toggle + turpentine/config_select_normalizeHost 30 1 0 @@ -424,7 +445,7 @@ Domain to force requests to, defaults to the domain in the base URL text - 1 + yes 40 1 diff --git a/app/code/community/Nexcessnet/Turpentine/misc/version-3.vcl b/app/code/community/Nexcessnet/Turpentine/misc/version-3.vcl index 05ac503d2..b29e47e51 100644 --- a/app/code/community/Nexcessnet/Turpentine/misc/version-3.vcl +++ b/app/code/community/Nexcessnet/Turpentine/misc/version-3.vcl @@ -389,7 +389,7 @@ sub vcl_deliver { "; domain=" + regsub(req.http.Host, ":\d+$", ""); } else { # it's a real user, allow sharing of cookies between stores - if(req.http.Host ~ "{{normalize_cookie_regex}}") { + if (req.http.Host ~ "{{normalize_cookie_regex}}" && "{{normalize_cookie_regex}}" ~ "..") { set resp.http.Set-Cookie = resp.http.Set-Cookie + "; domain={{normalize_cookie_target}}"; } else { diff --git a/app/code/community/Nexcessnet/Turpentine/misc/version-4.vcl b/app/code/community/Nexcessnet/Turpentine/misc/version-4.vcl index 757601a6f..329dabb6f 100644 --- a/app/code/community/Nexcessnet/Turpentine/misc/version-4.vcl +++ b/app/code/community/Nexcessnet/Turpentine/misc/version-4.vcl @@ -28,6 +28,7 @@ C{ ## Imports import std; +import directors; ## Backends @@ -97,6 +98,10 @@ sub generate_session_expires { {{generate_session_end}} ## Varnish Subroutines +sub vcl_init { + {{directors}} +} + sub vcl_recv { {{maintenance_allowed_ips}} @@ -110,6 +115,8 @@ sub vcl_recv { } } + {{normalize_host}} + # We only deal with GET and HEAD by default # we test this here instead of inside the url base regex section # so we can disable caching for the entire site if needed @@ -129,7 +136,6 @@ sub vcl_recv { {{normalize_encoding}} {{normalize_user_agent}} - {{normalize_host}} # check if the request is for part of magento if (req.url ~ "{{url_base_regex}}") { @@ -137,8 +143,10 @@ sub vcl_recv { set req.http.X-Turpentine-Secret-Handshake = "{{secret_handshake}}"; # use the special admin backend and pipe if it's for the admin section if (req.url ~ "{{url_base_regex}}{{admin_frontname}}") { - set req.backend_hint = admin; + set req.backend_hint = {{admin_backend_hint}}; return (pipe); + } else { + {{set_backend_hint}} } if (req.http.Cookie ~ "\bcurrency=") { set req.http.X-Varnish-Currency = regsub(