You are here

varnish.module in Varnish 5

Same filename and directory in other branches
  1. 8 varnish.module
  2. 6 varnish.module
  3. 7 varnish.module

varnish.module Provide drupal hooks for integration with the Varnish control layer.

File

varnish.module
View source
<?php

// $Id:
define(VARNISH_NO_CLEAR, 0);
define(VARNISH_DEFAULT_CLEAR, 1);
define(VARNISH_SELECTIVE_CLEAR, 2);

/**
 * @file varnish.module
 * Provide drupal hooks for integration with the Varnish control layer.
 */

/**
 * Implementation of hook_menu()
 *
 * Set up admin settings callbacks, etc.
 */
function varnish_menu() {
  $items = array();
  $items[] = array(
    'path' => 'admin/settings/varnish',
    'title' => 'Varnish settings',
    'description' => 'Configure your varnish integration.',
    'callback' => 'drupal_get_form',
    'callback arguments' => array(
      'varnish_admin_settings_form',
    ),
    'access' => user_access('administer varnish'),
  );
  $items[] = array(
    'path' => 'admin/reports/varnish',
    'title' => 'Varnish status',
    'description' => 'Configure your varnish integration.',
    'callback' => 'varnish_admin_reports_page',
    'access' => user_access('administer varnish'),
  );
  $items[] = array(
    'path' => 'admin/settings/varnish/cache-clear',
    'title' => 'Clear Varnish cache',
    'description' => 'Clear your varnish integration.',
    'callback' => 'varnish_flush_cache_manually',
    'access' => user_access('administer varnish'),
  );
  return $items;
}

/** 
 * Implemetation of hook_perm()
 *
 * Allows admins to control access to varnish settings.
 */
function varnish_perm() {
  return array(
    'administer varnish',
  );
}

/**
 * Implementation of hook_requirements()
 *
 * Insure that varnish's connection is good.
 */
function varnish_requirements($phase) {
  if ($phase == 'runtime') {
    $requirements = array(
      'varnish',
    );
    $requirements['varnish']['title'] = t('Varnish status');

    // try a varnish admin connect, report results
    $status = _varnish_terminal_run('status', 300);
    if (strpos($status, 'Child in state running') === FALSE) {
      $requirements['varnish']['value'] = t('Varnish connection broken');
      $requirements['varnish']['severity'] = REQUIREMENT_ERROR;
      $requirements['varnish']['description'] = t('The Varnish control terminal is not responding at %server on port %port.', array(
        '%server' => variable_get('varnish_control_terminal_server', '127.0.0.1'),
        '%port' => variable_get('varnish_control_terminal_port', '6082'),
      ));
    }
    else {
      $requirements['varnish']['value'] = t('Varnish running. Observe more detailed statistics !link.', array(
        '!link' => l(t('here'), 'admin/reports/varnish'),
      ));
    }
    return $requirements;
  }
}

/**
 * Implementation of hook_nodeapi()
 *
 * Used to pick up cache_clearing events
 */
function varnish_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  if ($op == 'insert' || $op == 'update') {

    // We've probably just run through node_save, and normally this is where
    // Drupal calls a cache_clear_all().
    switch (variable_get('varnish_cache_clear', VARNISH_DEFAULT_CLEAR)) {
      case VARNISH_DEFAULT_CLEAR:
        varnish_purge_all_pages();
        break;
    }
  }
}

/**
 * Implementation of hook_comment()
 *
 * Used to pick up cache_clearing events
 */
function varnish_comment($comment, $op) {
  switch ($op) {
    case 'insert':
    case 'update':
    case 'publish':
    case 'unpublish':
    case 'delete':
      if (variable_get('varnish_cache_clear', VARNISH_DEFAULT_CLEAR) == VARNISH_DEFAULT_CLEAR) {
        varnish_purge_all_pages();
      }
      break;
  }
}

/**
 * Implementation of hook_form_alter()
 *
 * Add our submit callback to the "clear caches" button.
 */
function varnish_form_alter($form_id, &$form) {
  if ($form_id == 'system_performance_settings') {
    $form['#submit']['varnish_purge_all_pages'] = array();
  }
}

/**
 * Implementation of hook_flush_caches()
 *
 * Flush caches on events like cron.
 *
 * This borrows logic from cache_clear_all() to respect cache_lifetime.
 */
function varnish_flush_cache_manually() {
  $destination = referer_uri();
  varnish_purge_all_pages();
  drupal_set_message('Varnish cache cleared.');
  drupal_goto($destination);
}

/**
 * Implementation of hook_cron
 */
function varnish_cron() {
  if (variable_get('varnish_flush_cron', 0)) {
    if (variable_get('cache_lifetime', 0)) {
      $cache_flush = variable_get('cache_flush_varnish', 0);
      if ($cache_flush == 0) {

        // This is the first request to clear the cache, start a timer.
        variable_set('cache_flush_varnish', time());
      }
      else {
        if (time() > $cache_flush + variable_get('cache_lifetime', 0)) {
          variable_set('cache_flush_varnish', 0);
          varnish_purge_all_pages();
        }
      }
    }
    else {
      varnish_purge_all_pages();
    }
  }
}

/**
 * Helper function to quickly flush all caches for the current site.
 */
function varnish_purge_all_pages() {
  $path = base_path();
  $host = _varnish_get_host();
  _varnish_terminal_run("purge req.http.host ~ {$host} && req.url ~ ^{$path}");
}

/**
 * Help[er function to parse the host from the global $base_url
 */
function _varnish_get_host() {
  global $base_url;
  $parts = parse_url($base_url);
  return $parts['host'];
}

/**
 * Extensible logic function to get other urls to clear.
 *
 * TODO: Merge this with boost logic.
 */
function varnish_get_active_urls($node) {
  $urls = array();
  $urls[] = 'node/' . $node->nid;
  if ($node->type == 'blog') {
    $urls[] = 'blog';
    $urls[] = 'blog/' . $node->uid;
  }

  // lots more here...
  foreach ($urls as $url) {
    $alias = drupal_get_path_alias($url);

    // TODO: languages?
    if ($alias != $url) {
      $urls[] = $alias;
    }
  }
  return $urls;
}

/**
 * Helper function that sends commands to Varnish
 *
 * Utilizes sockets to talk to varnish terminal.
 */
function _varnish_terminal_run($command, $returnlength = 1000) {
  if (!extension_loaded('sockets')) {
    drupal_set_message(t('Sockets extension not enabled. Varnish terminal communication aborted.'), 'error');
    return FALSE;
  }
  $server = variable_get('varnish_control_terminal_server', '127.0.0.1');
  $port = variable_get('varnish_control_terminal_port', '6082');
  $client = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
  if (!socket_connect($client, $server, $port)) {
    watchdog('varnish', t('Unable to connect to server socket !server:!port', array(
      '!server' => $server,
      '!port' => $port,
    )));
    return FALSE;
  }
  socket_write($client, "{$command}\n");
  $code = socket_read($client, 3, PHP_BINARY_READ);
  if ($code != 200) {
    $error = socket_read($client, 3000, PHP_BINARY_READ);
    watchdog('varnish', t('Recieved status code !code running !command. Full response text: !error', array(
      '!code' => $code,
      '!command' => $command,
      '!error' => $error,
    )));
    $ret = FALSE;
  }
  else {

    // successful connection
    $ret = socket_read($client, $returnlength, PHP_BINARY_READ);
  }
  socket_close($client);
  return $ret;
}

/**
* Menu callback for varnish admin settings.
*/
function varnish_admin_settings_form() {
  $form = array();

  // Decide whether or not to flush caches on cron runs.
  $form['varnish_flush_cron'] = array(
    '#type' => 'radios',
    '#title' => t('Flush page cache on cron?'),
    '#options' => array(
      0 => t('Disabled'),
      1 => t('Enabled (with respect for cache_lifetime)'),
    ),
    '#default_value' => variable_get('varnish_flush_cron', 0),
    '#description' => t('Internally Drupal will attempt to flush its page cache every time cron.php runs. This can mean too-frequent cache flushes if you have cron running frequently. NOTE: this cache flush is global!'),
  );
  if (!extension_loaded('sockets')) {
    drupal_set_message(t('Sockets extension not enabled. Varnish terminal communication configuration skipped.'), 'error');
    return system_settings_form($form);
  }

  // Begin socket-dependent configuration.
  $form['varnish_control_terminal_server'] = array(
    '#type' => 'textfield',
    '#title' => t('Varnish Control Terminal Server'),
    '#default_value' => variable_get('varnish_control_terminal_server', '127.0.0.1'),
    '#required' => TRUE,
    '#description' => t('Set this to the server IP or hostname that varnish runs on. This must be configured for Drupal to talk to Varnish.'),
  );
  $form['varnish_control_terminal_port'] = array(
    '#type' => 'textfield',
    '#title' => t('Varnish Control Terminal Server'),
    '#default_value' => variable_get('varnish_control_terminal_port', '6082'),
    '#required' => TRUE,
    '#description' => t('Set this to the port on which your varnish control terminal runs. This must be configured for Drupal to talk to Varnish.'),
  );
  $form['varnish_control_key'] = array(
    '#type' => 'textfield',
    '#title' => t('Varnish Control Key'),
    '#default_value' => variable_get('varnish_control_key', ''),
    '#description' => t('Optional: if you have established a secret key for control terminal access, please put it here.'),
  );
  $form['varnish_cache_clear'] = array(
    '#type' => 'radios',
    '#title' => t('Varnish Cache Clearing'),
    '#options' => array(
      VARNISH_DEFAULT_CLEAR => t('Drupal Default'),
      VARNISH_NO_CLEAR => t('None'),
    ),
    '#default_value' => variable_get('varnish_cache_clear', VARNISH_DEFAULT_CLEAR),
    '#description' => t('What kind of cache clearing Varnish should utilize. "Drupal Default" will clear the entire Varnish page cache on node updates, comment updates/additions, and/or other cache flush events. "None" will allow stale pages to persist when nodes and comments are added, and all other Drupal-based cache clearing events (except for cron run varnish cache clearing if you have that enabled).'),
  );
  $form['varnish_stats'] = array(
    '#type' => 'fieldset',
    '#collapsible' => FALSE,
    '#title' => t('Stats'),
  );
  if ($_GET['stats'] == 1) {
    $status = _varnish_terminal_run('stats', 50000);
    $form['varnish_stats']['#collapsed'] = FALSE;
  }
  else {
    $status = l(t('Fetch stats'), 'admin/settings/varnish', array(), 'stats=1');
    $form['varnish_stats']['#collapsed'] = TRUE;
  }
  $form['varnish_stats']['data'] = array(
    '#type' => 'markup',
    '#prefix' => '<pre>',
    '#suffic' => '</pre>',
    '#value' => $status,
  );
  return system_settings_form($form);
}

/**
* Menu callback for varnish admin settings.
*/
function varnish_admin_reports_page() {

  // connect to varnish and do a full status report
  $status = _varnish_terminal_run('stats', 50000);
  return "<pre>{$status}</pre>";
}

Functions

Namesort descending Description
varnish_admin_reports_page Menu callback for varnish admin settings.
varnish_admin_settings_form Menu callback for varnish admin settings.
varnish_comment Implementation of hook_comment()
varnish_cron Implementation of hook_cron
varnish_flush_cache_manually Implementation of hook_flush_caches()
varnish_form_alter Implementation of hook_form_alter()
varnish_get_active_urls Extensible logic function to get other urls to clear.
varnish_menu Implementation of hook_menu()
varnish_nodeapi Implementation of hook_nodeapi()
varnish_perm Implemetation of hook_perm()
varnish_purge_all_pages Helper function to quickly flush all caches for the current site.
varnish_requirements Implementation of hook_requirements()
_varnish_get_host Help[er function to parse the host from the global $base_url
_varnish_terminal_run Helper function that sends commands to Varnish