Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- define('NZPOST_CACHE_PER_SESSION', 'X-Varnish-Client-SID');
- define('NZPOST_CACHE_PER_ROLES', 'X-Varnish-Client-Roles');
- define('NZPOST_CACHE_GLOBAL', 'global');
- define('NZPOST_CACHE_AUTHONLY', 'authonly');
- define('NZPOST_CACHE_ANONONLY', 'anononly');
- define('NZPOST_CACHE_PER_DEFAULTS', 'X-Varnish-Client-ToolPrefs');
- define('NZPOST_CACHE_NOCACHE', 'nocache');
- define('NZPOST_CACHE_PER_JS', 'X-Varnish-Client-HasJS');
- define('NZPOST_CACHE_PER_UA', 'User-Agent');
- define('NZPOST_CACHE_DEFAULT_PATHS_NOCACHE', "user\r\nuser/*\r\n\r\ncart\r\ncart/*\r\nadmin/*\r\nlogout");
- define('NZPOST_CACHE_DEFAULT_PATHS_PERSESSION', '');
- define('NZPOST_CACHE_DEFAULT_PATHS_PERROLES', '');
- define('NZPOST_CACHE_DEFAULT_PATHS_GLOBAL', '');
- define('NZPOST_CACHE_DEFAULT_PATHS_AUTHONLY', '');
- define('NZPOST_CACHE_DEFAULT_PATHS_ANONONLY', '');
- /**
- * Implementation of hook_init()
- */
- function nzpost_cache_init() {
- global $user;
- $GLOBALS['nzpost_cache_ttl'] = variable_get('nzpost_cache_default_vanish_ttl', 86400);
- // If we're on an ESI callback, translate the esi module's cache settings into Vary headers for Varnish
- if (strpos(request_uri(), '/esi') === 0) { // The ESI module overwrites $_GET['q']
- require_once dirname(__FILE__).'/includes/esi.inc';
- _nzpost_cache_esi_setheaders();
- }
- else {
- // For anything other than ESI, send a fixed Varnish Vary header to vary on whether or not the user is an admin
- drupal_set_header('Vary', 'X-Varnish-Client-IsAdmin', true);
- }
- // If the user is an admin, force TTL to 0
- if (_nzpost_cache_is_admin(array_keys($user->roles))) {
- $GLOBALS['nzpost_cache_ttl'] = 0;
- }
- // Add into Drupal.settings some per-page settings that may be useful to Javascript
- $page_data = array(
- 'nzp_page' => array(
- 'path' => str_replace('destination=', '', drupal_get_destination())
- )
- );
- drupal_add_js($page_data, 'setting');
- _nzpost_cache_check_varnish();
- _nzpost_cache_check_cookies();
- }
- /**
- * Implementation of hook_exit()
- */
- function nzpost_cache_exit() {
- global $user;
- if (function_exists('drupal_get_headers')) {
- $existing_headers = drupal_get_headers();
- if (strpos($existing_headers, 'content-type: text/html') !== false) {
- // Most of the time we want to allow back button behaviour to work, except when we don't
- // We accomplish this by using no-store when back should be broken, and omitting it when it shouldn't be.
- $block_paths = variable_get('nzpost_cache_force_back_refresh', array(
- 'cart',
- 'cart/checkout',
- 'cart/checkout/review'
- ));
- if (!in_array($_GET['q'], $block_paths)) {
- // Don't allow caching, but don't go as far as no-cache, which IE will take too literally for back button support to work.
- drupal_set_header('Cache-Control', 'private,max-age=0');
- // max-age=0 still allows the browser to serve it from the cache within the session (in IE). We set
- // an additional Expires -1 header to bypass this behaviour and always revalidate except on back button.
- drupal_set_header('Expires', '-1');
- }
- else {
- drupal_set_header('Cache-Control', 'no-cache, no-store, pre-check=0, post-check=0, must-revalidate');
- }
- }
- // Set up Varnish headers (note Vary headers have probably already been set)
- if (!isset($GLOBALS['nzpost_cache_mode_set'])) {
- // No module has chosen a particular cache mode for this page.
- // First we check through the path patterns in the variables for a match, but if that fails,
- // we fall back to "anononly" caching, similar to what Boost gave us before.
- if (isset($_GET['q'])) {
- $path = drupal_get_path_alias($_GET['q']);
- foreach (array(
- NZPOST_CACHE_NOCACHE => variable_get('nzpost_cache_varnish_paths_nocache', NZPOST_CACHE_DEFAULT_PATHS_NOCACHE),
- NZPOST_CACHE_PER_SESSION => variable_get('nzpost_cache_varnish_paths_persession', NZPOST_CACHE_DEFAULT_PATHS_PERSESSION),
- NZPOST_CACHE_PER_ROLES => variable_get('nzpost_cache_varnish_paths_perroles', NZPOST_CACHE_DEFAULT_PATHS_PERROLES),
- NZPOST_CACHE_GLOBAL => variable_get('nzpost_cache_varnish_paths_global', NZPOST_CACHE_DEFAULT_PATHS_GLOBAL),
- NZPOST_CACHE_AUTHONLY => variable_get('nzpost_cache_varnish_paths_nocache', NZPOST_CACHE_DEFAULT_PATHS_AUTHONLY),
- NZPOST_CACHE_ANONONLY => variable_get('nzpost_cache_varnish_paths_anononly', NZPOST_CACHE_DEFAULT_PATHS_ANONONLY)
- ) as $set_mode => $variable_val) {
- // Compile to a regex and match the current path
- if (drupal_match_path($_GET['q'], $variable_val) || ($path && drupal_match_path($path, $variable_val))) {
- // Force this mode
- $intended_mode = $set_mode;
- drupal_alter('nzpost_cache_defaultmode', $set_mode, $_GET['q'], $path);
- if ($intended_mode == $set_mode) {
- //d('[nzpost_cache] Matched page path pattern, setting mode to '.$set_mode.' for '.$_GET['q'].($path ? ' ('.$path.')' : ''));
- }
- else {
- //d('[nzpost_cache] Matched page path pattern but overridden by alter hook, setting mode to '.$set_mode.' for '.$_GET['q'].($path ? ' ('.$path.')' : ''));
- }
- nzpost_cache_setmode($set_mode);
- break;
- }
- }
- }
- if (!isset($GLOBALS['nzpost_cache_mode_set'])) {
- // Apply the anononly default if the content type is text/html, otherwise nocache
- if (strpos($existing_headers, 'content-type: text/html') !== false) {
- //d('Fallback to ANONONLY');
- nzpost_cache_setmode(NZPOST_CACHE_ANONONLY);
- }
- else {
- //d('Fallback to NOCACHE');
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE);
- }
- }
- }
- if (!empty($GLOBALS['nzpost_cache_esi_enabled'])) {
- drupal_set_header('X-Varnish-ESI', 'on');
- }
- if (isset($GLOBALS['nzpost_cache_ttl'])) {
- _nzpost_cache_set_ttl_header($GLOBALS['nzpost_cache_ttl']);
- }
- else {
- _nzpost_cache_set_ttl_header(0);
- }
- }
- if (isset($_COOKIE['NO_CACHE']) && empty($_SESSION['messages']) && isset($_GET['q']) && strpos($_GET['q'], 'esi/') !== 0) {
- // This page view is obviously not cached and the user used cookie_cache_bypass_adv's cookie to bust the cache
- // for this load. Now that we've flushed out any messages they may want to see, we can actually remove the cookie
- // and send them on their merry way back to cacheland.
- global $cookie_domain;
- setcookie('NO_CACHE', '', 1, '/', $cookie_domain);
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE); // not really required, but for neatness.
- }
- }
- /**
- * Courtesy function for setting the X-VARNISH-TTL header.
- */
- function _nzpost_cache_set_ttl_header($ttl) {
- if ($ttl > 0) {
- drupal_set_header('X-VARNISH-TTL', $ttl, false);
- }
- else {
- // Pressflow refuses to send a header set to 0, so we set it to -1 instead
- drupal_set_header('X-VARNISH-TTL', -1, false);
- }
- }
- /**
- * Implementation of hook_esi_roles_hash_alter()
- */
- function nzpost_cache_esi_roles_hash_alter(&$hash, $included_rids, $rids, $seed) {
- // For identification of admin users by Varnish, we append a special "admin bit" on the end of the roles hash
- // that our Varnish config knows to look for (and translates into X-Varnish-Client-IsAdmin)
- if (_nzpost_cache_is_admin($rids)) {
- //d('Detected admin role. Setting admin bit in ESI roles hash');
- $hash .= 'ADMIN';
- }
- }
- /**
- * Implementation of hook_messages_alter() (custom NZ Post hook in template.php)
- */
- function nzpost_cache_messages_alter(&$messages, $display) {
- if (!empty($messages)) {
- // Ban caching on this page
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE, 0);
- }
- }
- /**
- * Returns true if any of the passed role IDs match one of our "admin" roles that may never be cached in Varnish
- *
- * @param array $roles an array of role IDs
- * @return bool true if the user has an admin role
- */
- function _nzpost_cache_is_admin($roles) {
- // Certain permissions make them automatically an admin
- if (user_access('access administration menu')) {
- return true;
- }
- return (bool)array_intersect(array_filter(variable_get('nzpost_cache_varnish_nocache_roles', array())), $roles);
- }
- /**
- * Implementation of hook_theme_registry_alter()
- */
- function nzpost_cache_theme_registry_alter(&$theme_registry) {
- if (isset($theme_registry['esi_tag'])) {
- $theme_registry['esi_tag']['function'] = 'nzpost_cache_esi_tag_proxy';
- }
- }
- /**
- * Proxy theme function for theme_esi_tag() that sets the ESI flag for Varnish on the way through
- */
- function nzpost_cache_esi_tag_proxy($block, $data) {
- $GLOBALS['nzpost_cache_esi_enabled'] = true;
- $content = '<!-- esi -->'.theme_esi_tag($block, $data).'<!-- /esi -->'; // esi.theme.inc is still loaded by the theme system automatically
- $base_url = $base_url_backup;
- return $content;
- }
- /**
- * Called by other modules, sets the Varnish cache mode for the current page.
- * Optionally sets the TTL at the same time if you want to override the default.
- *
- * You *can* call this multiple times to add two different cache modes at once, but be sure
- * that you know what you're doing. NZPOST_CACHE_GLOBAL + NZPOST_CACHE_PER_DEFAULTS makes sense, for example.
- *
- * Options are:
- * NZPOST_CACHE_PER_SESSION (cache the current path only for the current user/session, or anonymous)
- * NZPOST_CACHE_PER_ROLES (cache the current path only for people with the same roles)
- * NZPOST_CACHE_GLOBAL (cache the current path the same for everyone, logged in or out)
- * NZPOST_CACHE_AUTHONLY (helper: cache the current path only for authenticated users, not for anonymous)
- * NZPOST_CACHE_ANONONLY (helper: cache the current path only for anonymous users, not for authenticated)
- * NZPOST_CACHE_PER_DEFAULTS (additional: make the current path depenedent on A/B tool preference cookies as well as the above)
- * NZPOST_CACHE_NOCACHE (forces $ttl 0, no Vary)
- * NZPOST_CACHE_PER_JS (additional: make the current path dependent on the has_js cookie as well as the above)
- * NZPOST_CACHE_PER_UA (additional: make the current path dependent on the user-agent header as well as the above)
- *
- * @param string $mode one of the above mode constants
- * @param int $ttl (optional) how long to cache the current page for (note this is context-sensitive, so if NZPOST_CACHE_PER_ROLEHASH is the $mode, then this is the TTL for the current user's role set only. This can be handy!
- */
- function nzpost_cache_setmode($mode, $ttl = null) {
- // mark that something has picked an explicit mode
- $GLOBALS['nzpost_cache_mode_set'] = true;
- if ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') {
- // We don't want to set headers on POST requests etc
- $GLOBALS['nzpost_cache_ttl'] = 0;
- return;
- }
- if ($mode == NZPOST_CACHE_GLOBAL) {
- // Global cache, do nothing.
- }
- else if (strpos($mode, 'X-Varnish-Client') === 0) {
- // This is a Vary header itself
- $GLOBALS['nzpost_cache_varys'][] = $mode;
- }
- else if ($mode == 'authonly') {
- // This is an alias for PER_ROLEHASH with TTL set 0 for anonymous users
- if (user_is_anonymous()) {
- return nzpost_cache_setmode(NZPOST_CACHE_PER_ROLES, 0);
- }
- else {
- if (!$ttl) {
- $ttl = variable_get('nzpost_cache_default_vanish_ttl', 86400);
- }
- return nzpost_cache_setmode(NZPOST_CACHE_PER_ROLES, $ttl);
- }
- }
- else if ($mode == 'anononly') {
- // This is an alias for PER_ROLEHASH with TTL set 0 for authenticated users
- if (user_is_anonymous()) {
- if (!$ttl) {
- $ttl = variable_get('nzpost_cache_default_vanish_ttl', 86400);
- }
- return nzpost_cache_setmode(NZPOST_CACHE_PER_ROLES, $ttl);
- }
- else {
- return nzpost_cache_setmode(NZPOST_CACHE_PER_ROLES, 0);
- }
- }
- else if ($mode == 'nocache') {
- $ttl = 0;
- }
- if ($ttl !== null) {
- $GLOBALS['nzpost_cache_ttl'] = $ttl;
- _nzpost_cache_set_ttl_header($ttl);
- }
- if ($mode != NZPOST_CACHE_GLOBAL && $mode != NZPOST_CACHE_NOCACHE) {
- drupal_set_header('Vary', $mode, true); // in append mode
- }
- $path = isset($_GET['q']) ? $_GET['q'] : '';
- //d('[nzpost_cache] Varnish set to mode '.$mode.', TTL is now '.$GLOBALS['nzpost_cache_ttl'].' ('.$path.')');
- }
- /**
- * Checks incoming variables from Varnish to make sure things are set up OK.
- */
- function _nzpost_cache_check_varnish() {
- global $conf, $user;
- // Don't interfere with Drush etc! This causes problems when theme cache is cleared in CLI.
- if (PHP_SAPI == 'cli') return;
- if (!empty($_SERVER['HTTP_X_VARNISH']) && empty($_SERVER['HTTP_X_MATCHED_SITE'])) {
- // If X-Matched-Site isn't set, Varnish will be in pipe mode and won't parse out ESIs. Warn.
- watchdog('nzpost_cache', 'Varnish VCL is not configured to handle this domain (@domain), check nzpost_local.vcl', array('@domain' => $_SERVER['HTTP_HOST']), WATCHDOG_CRITICAL);
- if (user_access('administer site configuration')) {
- drupal_set_message(t('Varnish VCL is not configured to handle this domain. Expect strangeness.'), 'error', false);
- }
- }
- else if ($user->uid && ((!isset($_SERVER['HTTP_X_SESSION_NAME']) || $_SERVER['HTTP_X_SESSION_NAME'] != session_name()) || (!isset($_SERVER['HTTP_X_VARNISH_CLIENT_SID']) || $_SERVER['HTTP_X_VARNISH_CLIENT_SID'] != session_id()))) {
- // If X-Matched-Site isn't set, Varnish will be in pipe mode and won't parse out ESIs. Warn.
- watchdog('nzpost_cache', 'Varnish VCL is not extracting correct session information. Check session cookie name matches nzpost_local.vcl. SN: @sn, VSN: @vsn, SID: @sid, VSID: @vsid', array('@sn' => session_name(), '@vsn' => $_SERVER['HTTP_X_SESSION_NAME'], '@sid' => session_id(), '@vsid' => $_SERVER['HTTP_X_VARNISH_CLIENT_SID']), WATCHDOG_CRITICAL);
- if (user_access('administer site configuration')) {
- drupal_set_message(t('Varnish VCL is not extracting your correct session information (name,ID) from requests. Expect strangeness.'), 'error', false);
- }
- }
- }
- /**
- * Implementation of hook_form_alter()
- */
- function nzpost_cache_form_alter(&$form, &$form_state, $form_id) {
- // Attach an #after_build to every form so we can detect whether form tokens are in use
- if (!isset($form['#after_build'])) {
- $form['#after_build'] = array();
- }
- $form['#after_build'][] = '_nzpost_cache_form_afterbuild';
- }
- /**
- * An #after_build attached to every form that checks if form tokens are in use. If so,
- * the page the form is on is not cacheable to prevent validation errors.
- * See Fog#3963 for background.
- */
- function _nzpost_cache_form_afterbuild($form) {
- if (!empty($form['form_token'])) {
- //d('[nzpost_cache] Disabling caching for the current page because a form on it ('.$form['form_id']['#value'].') has uncacheable form tokens (form_token).');
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE);
- }
- return $form;
- }
- /**
- * Does some work that the ESI module doesn't, and ensures users have the appropriate roles cookie.
- * This is to prevent ugly situations where a user has the wrong roles cookie, and caches a per-role ESI
- * or page with content for the wrong roles, which then gets shown for other users who actually have the roles.
- * A common example is the top-right login links, where the "authenticated user" version of the links are cached
- * by someone who is actually logged out, but still somehow has the authenticated user role hash. Other authenticated
- * users will then see the "Login" and "Register" links, even though they're logged in.
- */
- function _nzpost_cache_check_cookies() {
- global $user;
- $roles_cookie_name = 'R'.session_name();
- if ($user->uid == 0 && isset($_COOKIE[$roles_cookie_name])) {
- // ESI module doesn't catch this case - eg. session cookie deleted manually etc
- // Kill the roles cookie.
- if (function_exists('drupal_alter')) {
- $edit = array();
- esi_user('logout', $edit, $user);
- }
- d('Removed wrong roles cookie');
- watchdog('nzpost_cache', "Killed roles cookie for user who shouldn't have one (is anon)", array(), WATCHDOG_ERROR);
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE);
- }
- else if (isset($_COOKIE[$roles_cookie_name])) {
- // Perform a quick check to see if the user has the correct roles hash. Maybe we can remove this if we find out why it happens!
- if (function_exists('drupal_alter')) {
- module_load_include('inc', 'esi');
- $correct_hash = _esi__get_roles_hash();
- if ($_COOKIE[$roles_cookie_name] != $correct_hash) {
- $edit = array();
- esi_user('login', $edit, $user);
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE);
- d('Rebuilt roles cookie: '.$_COOKIE[$roles_cookie_name].' != '.$correct_hash);
- watchdog('nzpost_cache', 'Rebuilt roles cookie for user who had the wrong roles hash cookie', array(), WATCHDOG_ERROR);
- }
- }
- }
- else if ($user->uid > 0 && !isset($_COOKIE[$roles_cookie_name])) {
- // esi_init() will actually set them the cookie, but we need to make sure that the page isn't cached too
- nzpost_cache_setmode(NZPOST_CACHE_NOCACHE);
- }
- }
- /**
- * Implementation of hook_file_download()
- */
- function nzpost_error_file_download($filepath) {
- // Use this hook to add a no-cache header to the download (see nginx_accel_redirect module)
- return array(
- 'X-VARNISH-TTL' => 0
- );
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement