nexcess / magento-turpentine

A Varnish extension for Magento.
GNU General Public License v2.0
520 stars 253 forks source link

ESI Issue with ESI in Json Response #1447

Open josh-palan opened 7 years ago

josh-palan commented 7 years ago

I am using Amasty's infinite scroll plugin on my product listing pages. Amasty injects JS into the listing page that, on scroll, makes an AJAX request to a Magento endpoint to get the next product_list block. That endpoint returns a JSON object:

{"page":"<html from block>"}

The HTML from that block contains ESI requests to the product information. Varnish is parsing that ESI and correctly inserting the HTML from that block, but the returned HTML from that ESI contains unescaped quotes (") which are breaking the returned JSON object.

I'm lost as to where to go from here. I've seen others with similar issues, but it seemed the JSON object wasn't being parsed for the ESI. My JSON is being parsed, but the returned HTML is unescaped.

miguelbalparda commented 7 years ago

Probably the easiest approach is to model the response first in a variable and then pass it to the function, all this in the frontend using JS. The other approach might be changing some internals of this module, but that might take you some more time.

josh-palan commented 7 years ago

Well the issue is I'm unable to edit the response in the frontend using JS because the response is coming back to the JS malformed. A little more background, The JS is making the call to the server like so:

var request = new Ajax.Request(url, {
            method : 'get',
            parameters : params,
            onSuccess: function(response){
                <logic to display page>
            }

The function that is called looks like so:

public function handleLayoutRender()
    {
        if (!Mage::app()->getRequest()->getParam('is_scroll')) {
            return;
        }
        $layout = Mage::getSingleton('core/layout');
        if (!$layout) {
            return;
        }

        $isAJAX = Mage::app()->getRequest()->getParam('is_ajax', false);
        $isAJAX = $isAJAX && Mage::app()->getRequest()->isXmlHttpRequest();
        if (!$isAJAX) {
            return;
        }

        $layout->removeOutputBlock('root');    
        Mage::app()->getFrontController()->getResponse()->setHeader('content-type', 'application/json');

        $html = "";
        /*compatibility with Amasty Finder*/
        $finder = $layout->getBlock('amfinder89');
        if ($finder) {
            $html = $finder->toHtml();
        }

        $page = Mage::helper('amscroll')->findProductList($layout);
        if (!$page) {
            return;
        }

        $html .= $page->toHtml();

        $response =  array(
            'page' => $this->_removeAjaxParam($html)
        );
        /* compatibility with amasty labels */
        $label = $layout->getBlock('amlabel_script');
        if ($label) {
            $response['amlabel_script'] = $label->toHtml();
        }

        $response = Mage::helper('core')->jsonEncode($response);

        Mage::app()->getResponse()->setBody($response)->sendResponse();
        if (class_exists('Amasty_Base_Helper_Utils')) {
            Mage::helper('ambase/utils')->_exit();
        }
    }

The issue is that when Mage::app()->getResponse()->setBody($response)->sendResponse(); is called, the $response JS object contains ESIs. Those are parsed correctly, but again the HTML that comes from Varnish isn't escaped, so a single " destroys the JSON object.

I actually created a local extension to override various files from this extension, so doing so isn't an issue, I just don't quite know where to start. I've been looking into doing some sort of logic to escape the html within the VCL, but I'm having trouble gaining access to and editing a response's body object...