Hey Steve, as you know, Authcache uses a fork of the Cache Router module. It would be better to make the Authcache module independent of the Cache Router codebase for easier maintenance on my side and to avoid confusion among users.

Only a few modifications should be necessary. Test modules are here.

Changes to cacherouter.inc - page_cache_fastpath()
This code serves cached pages for both anonymous and authenticated users (for logged-in users an "authcache" hash key from a cookie is used). The function was also updated to use "304 Not Modified" headers to prevent HTML from being sent again, use the db engine, send cached headers (e.g., for RSS XML feeds), and to handle drupal_set_message()/drupal_goto() redirects (you may want to update cacherouter.module to support this ... see authcache_exit() hook. For example, if a new user creates an account, the confirmation status message won't be dispalyed since Drupal redirects to the front page, which is mostly likely served from the cache).

function page_cache_fastpath() {
  global $base_root, $cache, $conf;

  // User is logged in but their role should not receive any cached pages
  // (i.e., cached anonymous pages, since they have no authcache key)
  if(isset($_COOKIE['drupal_user']) && !isset($_COOKIE['authcache'])) {
    return FALSE;
  }

  if (empty($_POST) && !isset($_COOKIE['nocache'])) {

    // Connect to database if 'db' engine selected
    if($conf['cacherouter']['default']['engine'] == 'db') {
      require_once './includes/database.inc';
      db_set_active();
    }

    if (!isset($cache)) {
      $cache = new CacheRouter();
    }
    if ($cache->page_fast_cache('cache_page')) {
      $key = (isset($_COOKIE['authcache']) && $_COOKIE['authcache']) ? $_COOKIE['authcache'] : '';
      $page = $cache->get($key . $base_root . request_uri(), 'cache_page');
      if (!empty($page)) {

        // Set HTTP headers in preparation for a cached page response.

        // The following headers force validation of cache:
        header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
        header("Cache-Control: must-revalidate");

        // Visitors can keep a local cache of the page, but must revalidate
        // it on every request.  Then, they are given a '304 Not Modified'
        // response with no body as long as they say page has not been modified.
        $last_modified = gmdate('D, d M Y H:i:s', $page->created) .' GMT';

        // See if the client has provided the required HTTP caching header
        // (No need for ETag; resolution of HTTP date value is sufficient)
        $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;

        if ($if_modified_since == $last_modified) {

          // Save cache benchmark
          if(isset($_COOKIE['authcache_debug'])) {
            setcookie('cache_render',timer_read('page'));
          }

          header('HTTP/1.1 304 Not Modified');
          return TRUE;
        }

        // Time from when page was saved to cache
        header("Last-Modified: $last_modified");

        // Send the original request's headers.
        $headers = explode("\n", $page->headers);
        foreach ($headers as $header) {
          header($header);
        }

        // Checking if first chars are compressed (always the same pattern for every header)
        if (substr($page->data, 0,3) == "\x1f\x8b\x08") {

          // Determine if the browser accepts gzipped data.
          if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE && function_exists('gzencode')) {
            // Strip the gzip header and run uncompress.
            $page->data = gzinflate(substr(substr($page->data, 10), 0, -8));
          }
          elseif (function_exists('gzencode')) {
            // Send gzip header to the browser
            header('Content-Encoding: gzip');
          }
        }

        // Save cache benchmark
        if(isset($_COOKIE['authcache_debug'])) {
          setcookie('cache_render',timer_read('page'));
        }

        print $page->data;
        return TRUE;
      }
    }
  }

  // Cache was temporarily disabled (most likely from drupal_set_message()/drupal_goto() redirect)
  if(isset($_COOKIE['nocache_temp'])) {
    setcookie('nocache', '', time() - 36000, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
    setcookie('nocache_temp', '', time() - 36000, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
  }

  return FALSE;
}

Changes to db.php - page_fast_cache()
While the database is the slowest type caching, some people may still want to use it. I added a db_set_active() call (above) that connects to the Drupal database. (Drupal caches the connection in a static variable, so there isn't additional overhead if the other Drupal bootstrap steps are initiated.) The db.php file needs to be changed to allow fast cache.

  function page_fast_cache() {
    return $this->fast_cache;
  }

Let me know what you think. Also, if you need any help maintaining Cache Router, I'd be happy to help out.

Thanks!

CommentFileSizeAuthor
authcache-cacherouter.zip54.1 KBJonah Ellison
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

EvanDonovan’s picture

Any progress on this? It would be great if these modules were more "modular" :)

Jonah Ellison’s picture

Here is a better solution!

I just integrated the Memcache module with Authcache without any forking. This did however change the page_cache_fastpath() code I posted above, so please disregard.

Since this function is starting to contain more Authcache-specific code, a more modular solution for integrating with Cache Router would be to wrap the page_cache_fastpath() function with:

if (!function_exists('page_cache_fastpath')) {
  function page_cache_fastpath() {
    // ...
  }
}

This way Authcache can just include cacherouter.inc like it does with Memcache's memcache.inc.

slantview’s picture

This is now fixed I believe. I added in the if (function_exists('page_cache_fastpath')) and added lazy instantiation to the db class so it will automatically connect to the db if we are trying to do fastpath full page caching. This should allow you to integrate Cache Router without using a fork. Let me know if there is anything else I can do to get this working together and be more "modular" :)

slantview’s picture

Status: Active » Fixed
EvanDonovan’s picture

So for people who potentially want to use Cache Router + Authcache, what should we do now? Is it in the production release yet?

Jonah Ellison’s picture

My goal is to remove the Cache Router fork code completely from the Authcache module, once a new beta or dev version of Cache Router is released. Then people will need to download the new Cache Router and Authcache modules separately.

EvanDonovan’s picture

OK. I just was confused by the fact that this was marked "fixed" if it's not able to be used yet.

slantview’s picture

Well, it is fixed from my perspective. I added the features needed for Jonah to remove the fork from his code.

EvanDonovan’s picture

I understand. I didn't mean to sound like I was complaining; I was just uncertain as to what the status was for users.

I was also confused, I think, about which queue this issue was in. Perhaps I should open a separate issue in the Authcache queue to track the progress of removing the Cache Router code from that module?

slantview’s picture

That sounds like a great idea. Getting all this stuff to work together will be a great win. I will talk to csevb10 (Bill) about getting ajaxify_regions module to work with cache router as well.

Status: Fixed » Closed (fixed)
Issue tags: -authcache

Automatically closed -- issue fixed for 2 weeks with no activity.