You are here

cdn.module in CDN 6.2

Same filename and directory in other branches
  1. 8.3 cdn.module
  2. 5 cdn.module
  3. 6 cdn.module
  4. 7.2 cdn.module

File

cdn.module
View source
<?php

/**
 * @file
 * Implementation of the core hooks, public and private functions.
 */
require_once 'cdn.constants.inc';

//----------------------------------------------------------------------------

// Drupal core.

/**
 * Implementation of hook_file_url_alter().
 */
function cdn_file_url_alter(&$path) {
  $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
  $farfuture = variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT);
  $stats = variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS);
  $https_support = variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE);
  $maintenance_mode = variable_get('maintenance_mode', FALSE);
  $is_https_page = cdn_request_is_https();

  // Don't alter file URLs when running update.php.
  if (defined('MAINTENANCE_MODE')) {
    return;
  }
  if (cdn_status_is_enabled()) {

    // If the current path is an absolute or protocol-relative URL, return
    // immediately.
    $fragments = parse_url($path);
    if (isset($fragments['host']) || drupal_substr($path, 0, 2) == '//') {
      return;
    }
    if (!cdn_check_protocol()) {
      return;
    }
    if (!cdn_check_drupal_path($_GET['q'])) {
      return;
    }
    if (!cdn_check_file($path)) {
      return;
    }
    if ($stats) {
      cdn_load_include('stats');
      $start = microtime();
    }

    // Alter the file path when using Origin Pull mode and using that mode's
    // Far Future setting.
    if ($mode == CDN_MODE_BASIC && $farfuture && !$maintenance_mode) {
      cdn_load_include('basic.farfuture');

      // We need the unescaped version of the path to perform file operations.
      $path = urldecode($path);

      // If the file does not yet exist, perform a normal HTTP request to this
      // file, to generate it. (E.g. when ImageCache is used, this will
      // generate the derivative file.) When that fails, don't serve it from
      // the CDN.
      if (!file_exists($path) && !_cdn_basic_farfuture_generate_file($path)) {
        $path = drupal_urlencode($path);
        return;
      }

      // Generate a unique file identifier (UFI).
      $ufi = cdn_basic_farfuture_get_identifier($path);

      // Now that file operations have been performed, re-encode the path.
      $path = drupal_urlencode($path);

      // Generate the new path.
      $path_before_farfuture = $path;

      // Generate a unique token to verify that the request was generated by
      // CDN. We cannot use drupal_get_token() since it depends on the user
      // session.
      $path_info = pathinfo(urldecode($path));
      $token = _cdn_hmac_base64($ufi . $path_info['filename'], drupal_get_private_key() . _cdn_get_hash_salt());
      $path = "cdn/farfuture/{$token}/{$ufi}/{$path}";
    }

    // Load the include file that contains the logic for the mode that's
    // currently enabled.
    cdn_load_include($mode == CDN_MODE_BASIC ? 'basic' : 'advanced');

    // Depending on the mode, use a different function to get the servers on
    // which the file is available.
    $servers = $mode == CDN_MODE_BASIC ? cdn_basic_get_servers($path) : cdn_advanced_get_servers($path);

    // The file is not available on any server.
    if (count($servers) == 0) {
      $cdn_url = FALSE;
      $server = FALSE;
    }
    elseif (count($servers) > 1 && function_exists('cdn_pick_server')) {
      $picked_server = cdn_pick_server($servers);
      $cdn_url = $picked_server['url'];
      $server = $picked_server['server'];
    }
    elseif (count($servers) > 1 && variable_get(CDN_PICK_SERVER_PHP_CODE_VARIABLE, '') !== '') {
      $php_code = variable_get(CDN_PICK_SERVER_PHP_CODE_VARIABLE, '');
      $picked_server = _cdn_eval_pick_server($php_code, $servers);
      $cdn_url = $picked_server['url'];
      $server = $picked_server['server'];
    }
    elseif (count($servers > 1)) {
      $filename = basename($servers[0]['url']);
      $unique_file_id = hexdec(substr(md5($filename), 0, 5));
      $choice = $unique_file_id % count($servers);
      $cdn_url = $servers[$choice]['url'];
      $server = $servers[$choice]['server'];
    }
    else {
      $cdn_url = $servers[0]['url'];
      $server = $servers[0]['server'];
    }

    // If the current page is being served via HTTPS, and the CDN supports
    // HTTPS, then use the HTTPS file URL.
    if ($is_https_page && $https_support && !empty($cdn_url)) {
      $cdn_url = preg_replace('/^http:/', 'https:', $cdn_url);
    }

    // If the user can access it, add this to the per-page statistics.
    if ($stats) {
      $end = microtime();
      $source_path = $mode == CDN_MODE_BASIC && $farfuture && !$maintenance_mode ? $path_before_farfuture : $path;
      _cdn_devel_page_stats($source_path, $cdn_url, $server, $end - $start);
    }

    // Override the path with the corresponding CDN URL, *if* the file is
    // available on the CDN (it may only be not available in advanced mode).
    if ($cdn_url !== FALSE) {
      $path = $cdn_url;
    }
    else {

      // If the file is not available on the CDN and Origin Pull mode's Far
      // Future setting is enabled, then don't serve it using CDN module's
      // non-scalable Far Future functionality, but using Drupal's default.
      if ($mode == CDN_MODE_BASIC && $farfuture) {
        $path = $path_before_farfuture;
      }
    }
  }
}

/**
 * Implementation of hook_cdn_unique_file_identifier_info().
 */
function cdn_cdn_unique_file_identifier_info() {

  // Keys are machine names.
  return array(
    'md5_hash' => array(
      'label' => t('MD5 hash'),
      'prefix' => 'md5',
      'description' => t('MD5 hash of the file.'),
      'filesystem' => TRUE,
      'callback' => 'md5_file',
    ),
    'mtime' => array(
      'label' => t('Last modification time'),
      'prefix' => 'mtime',
      'description' => t('Last modification time of the file.'),
      'filesystem' => TRUE,
      'callback' => 'filemtime',
    ),
    'perpetual' => array(
      'label' => t('Perpetual'),
      'prefix' => 'perpetual',
      'description' => t('Perpetual files never change (or are never cached
                          by the browser, e.g. video files).'),
      'filesystem' => FALSE,
      'value' => 'forever',
    ),
    'drupal_version' => array(
      'label' => t('Drupal version'),
      'prefix' => 'drupal',
      'description' => t('Drupal core version — this should only be applied
                          to files that ship with Drupal core.'),
      'filesystem' => FALSE,
      'value' => VERSION,
    ),
    'drupal_cache' => array(
      'label' => t('Drupal cache'),
      'prefix' => 'drupal-cache',
      'description' => t('Uses the current Drupal cache ID
                          (<code>css_js_query_string</code>). This ID is
                          updated automatically whenever the Drupal cache is
                          flushed (e.g. when you submit the modules form). Be
                          aware that this can change relatively often, forcing
                          redownloads by your visitors.'),
      'filesystem' => FALSE,
      'value' => variable_get('css_js_query_string', 0),
    ),
    'deployment_id' => array(
      'label' => t('Deployment ID'),
      'prefix' => 'deployment',
      'description' => t('A developer-defined deployment ID. Can be an
                          arbitrary string or number, as long as it uniquely
                          identifies deployments and therefore the affected
                          files.<br />
                          Define this deployment ID in any enabled module or
                          in <code>settings.php</code> as the
                          <code>CDN_DEPLOYMENT_ID</code>
                          constant, and it will be picked up instantaneously.'),
      'filesystem' => FALSE,
      'callback' => '_cdn_ufi_deployment_id',
    ),
  );
}

/**
 * Implementation of hook_theme_registry_alter().
 *
 * Make CDN's page preprocess function run *after* everything else's, so that
 * a theme can't call drupal_get_js() and mess everything up.
 *
 * @param &$theme_registry
 *   The entire cache of theme registry information, post-processing.
 */
function cdn_theme_registry_alter(&$theme_registry) {
  if (!cdn_status_is_enabled()) {
    return;
  }

  // We only need to override CSS aggregation if the advagg module is not in
  // use; otherwise that module handles that perfectly fine already!
  $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
  if ($mode == CDN_MODE_BASIC && !module_exists('advagg')) {
    $theme_registry['page']['preprocess functions'][] = '_cdn_css_aggregate';
  }
  if (variable_get(CDN_THEME_LAYER_FALLBACK_VARIABLE, FALSE)) {
    cdn_load_include('fallback');
    _cdn_fallback_theme_registry_alter($theme_registry);
  }
}

/**
 * Implementation of hook_menu().
 */
function cdn_menu() {

  // Admin UI.
  $items['admin/settings/cdn'] = array(
    'title' => 'CDN',
    'description' => 'Configure CDN integration.',
    'access arguments' => array(
      CDN_PERM_ADMIN,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_general_settings_form',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/general'] = array(
    'title' => 'General',
    'description' => 'General settings.',
    'access arguments' => array(
      CDN_PERM_ADMIN,
    ),
    'weight' => -10,
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/details'] = array(
    'title' => 'Details',
    'access arguments' => array(
      CDN_PERM_ADMIN,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_details_form',
    ),
    'weight' => -8,
    'type' => MENU_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/other'] = array(
    'title' => 'Other',
    'description' => 'Other settings.',
    'access arguments' => array(
      CDN_PERM_ADMIN,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_other_settings_form',
    ),
    'weight' => -4,
    'type' => MENU_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );

  // Statistics: file touch support.
  $items['admin/cdn/touch/%'] = array(
    'title' => 'Touch file',
    'description' => 'Touch a file to force a resync with File Conveyor.',
    'access arguments' => array(
      CDN_PERM_TOUCH,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_touch_file_form',
      3,
    ),
    'type' => MENU_CALLBACK,
    'file' => 'cdn.stats.inc',
  );

  // Origin Pull mode's Far Future expiration support.
  $items['cdn/farfuture/%/%/%cdn_basic_farfuture_tail'] = array(
    'title' => 'Download a far futured file',
    'access callback' => TRUE,
    'page callback' => 'cdn_basic_farfuture_download',
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'type' => MENU_CALLBACK,
    'load arguments' => array(
      '%map',
      '%index',
    ),
    'file' => 'cdn.basic.farfuture.inc',
  );
  $items['cdn/farfuture/reverse-proxy-test/%'] = array(
    'title' => 'Far Future reverse proxy test',
    'access callback' => TRUE,
    'page callback' => 'cdn_basic_farfuture_reverseproxy_test',
    'page arguments' => array(
      3,
    ),
    'type' => MENU_CALLBACK,
    'file' => 'cdn.basic.farfuture.inc',
  );
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function cdn_perm() {
  return array(
    CDN_PERM_ADMIN,
    CDN_PERM_ACCESS_STATS,
    CDN_PERM_ACCESS_TESTING,
    CDN_PERM_TOUCH,
  );
}

/**
 * Implementation of hook_form_alter().
 */
function cdn_form_system_performance_settings_alter(&$form, &$form_state) {

  // Add an id to the "Clear cached data" fieldset in the
  // "system_performance_settings" form, so we can link to it directly.
  $form['bandwidth_optimizations']['#attributes']['id'] = 'bw-optimizations';
  $form['clear_cache']['#attributes']['id'] = 'clear-cache';
}

/**
 * Implementation of hook_nodeapi().
 */
function cdn_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {

  // We implement hook_nodeapi() for $op == 'alter', to alter the final HTML,
  // to ensure that all file URLs (notably for images) in nodes also point to
  // the CDN.
  if ($op == 'alter') {
    cdn_load_include('fallback');
    if (isset($node->body)) {
      cdn_html_alter_image_urls($node->body);
    }
    elseif (isset($node->teaser)) {
      cdn_html_alter_image_urls($node->teaser);
    }
    if (isset($node->content) && is_array($node->content['body']) && isset($node->content['body']['#value'])) {
      cdn_html_alter_image_urls($node->content['body']['#value']);
    }
  }
}

/**
 * Implementation of hook_theme().
 */
function cdn_theme() {
  return array(
    'cdn_page_stats' => array(
      'file' => 'theme.inc',
      'arguments' => array(
        'file_count' => NULL,
        'cdn_file_count' => NULL,
        'synced_files_per_server_count' => NULL,
        'total_time' => NULL,
        'synced_files' => NULL,
        'unsynced_files' => NULL,
      ),
    ),
    'cdn_page_stats_file_link' => array(
      'file' => 'theme.inc',
      'arguments' => array(
        'file' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_init().
 */
function cdn_init() {
  if (!cdn_status_is_enabled()) {
    return;
  }
  _cdn_prefetch_dns();

  // We only need to override CSS aggregation if the advagg module is not in
  // use; otherwise that module handles that perfectly fine already!
  $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
  if ($mode == CDN_MODE_BASIC && !module_exists('advagg')) {
    cdn_load_include('basic.css');
  }
  if (variable_get(CDN_THEME_LAYER_FALLBACK_VARIABLE, FALSE)) {
    cdn_load_include('fallback');
  }

  // When per-page statistics are enabled, add the CSS that will be used to
  // make these statistics more usable.
  if (variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS)) {
    drupal_add_css(drupal_get_path('module', 'cdn') . '/cdn.css');
  }
}

/**
 * Implementation of hook_exit().
 */
function cdn_exit($destination = NULL) {

  // When the _cdn_devel_page_stats() function does not exist, there are no
  // stats to show, hence we can return immediately.
  // This can happen when the stats are disabled (for the current user or
  // entirely), or when a cached page is being served.
  if (!function_exists('_cdn_devel_page_stats')) {
    return;
  }

  // Try not to break non-HTML pages.
  if (function_exists('drupal_get_headers') && !strstr(drupal_get_headers(), 'html')) {
    return;
  }
  if (!$destination && variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED) != CDN_DISABLED && variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS)) {
    list($file_count, $cdn_file_count, $synced_files_per_server_count, $total_time, $synced_files, $unsynced_files, ) = _cdn_devel_page_stats();
    print theme('cdn_page_stats', $file_count, $cdn_file_count, $synced_files_per_server_count, $total_time, $synced_files, $unsynced_files);
  }
}

/**
 * Implementation of hook_flush_caches().
 */
function cdn_flush_caches() {

  // Force refresh of the blacklist.
  cdn_get_blacklist(TRUE);
}

/**
 * Implementation of hook_cdn_blacklist().
 */
function cdn_cdn_blacklist() {
  $blacklist = array();

  // Blacklist wysiwyg library files.
  if (module_exists('wysiwyg')) {
    foreach (wysiwyg_get_all_editors() as $editor) {
      if (!$editor['installed']) {
        continue;
      }
      $blacklist[] = $editor['library path'] . '/*';
    }
  }

  // Blacklist Image CAPTCHA' dynamically generated CAPTCHA images.
  $blacklist[] = 'image_captcha*';
  return $blacklist;
}

//----------------------------------------------------------------------------

// Menu system callbacks.

/**
 * Implements %wildcard_to_arg().
 *
 * Identical to menu_tail_to_arg() in Drupal 6 and 7.
 */
function cdn_basic_farfuture_tail_to_arg($arg, $map, $index) {
  return implode('/', array_slice($map, $index));
}

/**
 * Implements %wildcard_load().
 *
 * Backport of menu_tail_load() of Drupal 7; does not exist in Drupal 6.
 */
function cdn_basic_farfuture_tail_load($arg, $map, $index) {
  $arg = implode('/', array_slice($map, $index));
  $map = array_slice($map, 0, $index);
  return $arg;
}

//----------------------------------------------------------------------------

// Drush.

/**
 * Implements hook_cacheaudit().
 */
function cdn_cacheaudit() {
  $results = array(
    array(
      'Settings',
      'Value',
    ),
  );
  $status = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
  if ($status == CDN_ENABLED) {
    $status_value = 'enabled';
  }
  elseif ($status == CDN_TESTING) {
    $status_value = 'testing';
  }
  else {
    $status_value = 'disabled';
  }
  $results[] = array(
    'Status',
    $status_value,
  );
  $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
  $results[] = array(
    'Mode',
    $mode ? 'Origin Pull' : 'File Conveyor',
  );
  if ($mode == CDN_MODE_BASIC) {
    cdn_load_include('basic');
    $results[] = array(
      '    mappings',
      count(_cdn_basic_parse_raw_mapping(cdn_basic_get_mapping())),
    );
    $results[] = array(
      '    Far Future expiration',
      variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT),
    );
  }
  $results[] = array(
    'Domains',
    count(cdn_get_domains()),
  );
  return array(
    'cdn' => $results,
  );
}

//----------------------------------------------------------------------------

// Public functions.

/**
 * Get all domains from which files might be served. This information is
 * necessary for some modules, e.g. Boost.
 *
 * @return
 *   An array of domain names.
 */
function cdn_get_domains() {
  $domains = array();

  // Origin Pull mode domains.
  if (variable_get(CDN_MODE_VARIABLE, FALSE) == CDN_MODE_BASIC) {
    cdn_load_include('basic');
    $mapping = cdn_basic_get_mapping();
    $lines = preg_split("/[\n\r]+/", $mapping, -1, PREG_SPLIT_NO_EMPTY);
    foreach ($lines as $line) {

      // Ignore empty lines.
      $line = trim($line);
      if (empty($line)) {
        continue;
      }

      // Parse this line. It may or may not limit the CDN URL to a list of
      // file extensions.
      $parts = explode('|', $line);

      // It may also list more than one domain.
      foreach (explode(' ', $parts[0]) as $part) {

        // Remove white space.
        $part = trim($part);

        // Allow for protocol-relative domains. We prepend "http:", otherwise
        // parse_url() won't return anything.
        if (strpos($part, '//') === 0) {
          $part = 'http:' . $part;
        }
        $domains[] = parse_url($part, PHP_URL_HOST);
      }
    }
  }
  elseif (variable_get(CDN_MODE_VARIABLE, FALSE) == CDN_MODE_ADVANCED) {
    cdn_load_include('advanced');
    $db = _cdn_advanced_get_db_connection();

    // In case no connection to the database could be made, pretend no
    // domains are being used.
    if (!$db) {
      return array();
    }

    // Retrieve all unique domains (by retrieving one URL per server) and then
    // parsing the domain names in those URLs.
    $sql = "SELECT url\n            FROM synced_files\n            GROUP BY server";
    $stmt = $db
      ->prepare($sql);
    $stmt
      ->execute();
    $rows = $stmt
      ->fetchAll(PDO::FETCH_ASSOC);
    foreach ($rows as $row) {
      $domains[] = parse_url($row['url'], PHP_URL_HOST);
    }
  }
  return array_unique($domains);
}

/**
 * See if any installed modules need to exclude certain files from being
 * accessed from the CDN. List gets updated on cron runs.
 *
 * Typically, files that are loaded by JS through AJAX violate the same origin
 * policy browsers have to comply with. Hence the browser refuses to load the
 * file, causing a broken website. This blacklist allows module developers to
 * specify which files should not be loaded from the CDN.
 *
 * @param $reset
 *   Whether to force the stored blacklist to be regenerated.
 * @return string
 *   Every line is a path pattern (@see drupal_match_path()).
 */
function cdn_get_blacklist($reset = FALSE) {
  static $blacklist = NULL;
  if (is_null($blacklist) || $reset) {
    $cache = cache_get('cdn_blacklist');
    if (!isset($cache->data) || $reset) {

      // Query modules for a list of files to be included into the blacklist.
      $blacklist = module_invoke_all('cdn_blacklist');

      // Invoke hook_cdn_blacklist_alter.
      drupal_alter('cdn_blacklist', $blacklist);

      // Remove duplicates.
      $blacklist = array_unique($blacklist);

      // Convert array to string.
      $blacklist = implode("\n", $blacklist);

      // Save to the cache.
      cache_set('cdn_blacklist', $blacklist, 'cache', CACHE_TEMPORARY);
    }
    else {
      $blacklist = $cache->data;
    }
  }
  return $blacklist;
}

/**
 * Check if CDN module should serve files from a HTTPS location.
 */
function cdn_serve_from_https() {
  return variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE) && cdn_request_is_https();
}

/**
 * Check if the current request is over HTTPS.
 */
function cdn_request_is_https() {
  return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) && $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] == 'https';
}

/**
 * Check if the CDN module is enabled.
 */
function cdn_status_is_enabled() {
  $status = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
  return $status == CDN_ENABLED || $status == CDN_TESTING && user_access(CDN_PERM_ACCESS_TESTING);
}

/**
 * Check if the current protocol is supported by the CDN.
 *
 * Note: currently only checks HTTPS, in the future possibly also SPDY.
 */
function cdn_check_protocol() {
  $https_support = variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE);

  // If the current page is being served via HTTPS, and the CDN does not
  // support HTTPS, then don't rewrite the file URL, because it would make the
  // visit insecure.
  if (cdn_request_is_https() && !$https_support) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Check if a Drupal path should serve files from the CDN (i.e.: is the Drupal
 * path blacklisted?).
 *
 * @param $path
 *   A Drupal path.
 */
function cdn_check_drupal_path($path) {
  global $user;
  $blacklist = variable_get(CDN_EXCEPTION_DRUPAL_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_DRUPAL_PATH_BLACKLIST_DEFAULT);
  $auth_blacklist = variable_get(CDN_EXCEPTION_AUTH_USERS_BLACKLIST_VARIABLE, CDN_EXCEPTION_AUTH_USERS_BLACKLIST_DEFAULT);

  // Check if the Drupal path matches one of the blacklisted Drupal paths.
  if (drupal_match_path($path, $blacklist)) {
    return FALSE;
  }

  // If logged in user, apply a secondary blacklist.
  if ($user->uid > 0 && drupal_match_path($path, $auth_blacklist)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Check if a file should be served from the CDN.
 *
 * @param $path
 *   Path to a file; relative to the Drupal root directory.
 */
function cdn_check_file($path) {
  $file_path_blacklist = variable_get(CDN_EXCEPTION_FILE_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_BLACKLIST_DEFAULT);
  $file_path_whitelist = variable_get(CDN_EXCEPTION_FILE_PATH_WHITELIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_WHITELIST_DEFAULT);
  $module_blacklist = cdn_get_blacklist();

  // A file should not be served from a CDN when it matches one of the
  // blacklists, except when it matches the whitelist.
  if ((drupal_match_path($path, $file_path_blacklist) || drupal_match_path($path, $module_blacklist)) && !drupal_match_path($path, $file_path_whitelist)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Helper function to efficiently load include files for this module.
 */
function cdn_load_include($basename) {
  module_load_include('inc', 'cdn', "cdn.{$basename}");
}

//----------------------------------------------------------------------------

// Private functions.

/**
 * Callback for generating a unique file identifier.
 *
 * @param $path
 *   The file path to the file for which to generate a  unique identifier.
 */
function _cdn_ufi_deployment_id($path) {
  return CDN_DEPLOYMENT_ID;
}

/**
 * Helper function to evaluate CDN_PICK_SERVER_PHP_CODE_VARIABLE, when that is
 * being used instead of an actual cdn_pick_server() function.
 *
 * @param $php_code
 *   The PHP code to be evaluated.
 * @param $servers_for_file
 *   The $servers_for_file parameter, as it will be available to the eval()'d
 *   $php_code. This variable is not passed by reference, hence it will not be
 *   changed outside the scope of this function.
 */
function _cdn_eval_pick_server($php_code, $servers_for_file) {
  ob_start();
  $result = @eval($php_code);
  ob_end_clean();
  return $result;
}

/**
 * Helper function to add DNS prefetching to the page.
 *
 * @see http://drupal.org/node/1247078.
 */
function _cdn_prefetch_dns() {
  $domains = cdn_get_domains();
  if (count($domains)) {

    // Enable prefetching.
    drupal_set_html_head('<meta http-equiv="x-dns-prefetch-control" content="on" />');

    // The domain names to prefetch. Use protocol-relative URLs.
    foreach ($domains as $domain) {
      drupal_set_html_head('<link rel="dns-prefetch" href="//' . $domain . '" />');
    }
  }
}

Functions

Namesort descending Description
cdn_basic_farfuture_tail_load Implements %wildcard_load().
cdn_basic_farfuture_tail_to_arg Implements %wildcard_to_arg().
cdn_cacheaudit Implements hook_cacheaudit().
cdn_cdn_blacklist Implementation of hook_cdn_blacklist().
cdn_cdn_unique_file_identifier_info Implementation of hook_cdn_unique_file_identifier_info().
cdn_check_drupal_path Check if a Drupal path should serve files from the CDN (i.e.: is the Drupal path blacklisted?).
cdn_check_file Check if a file should be served from the CDN.
cdn_check_protocol Check if the current protocol is supported by the CDN.
cdn_exit Implementation of hook_exit().
cdn_file_url_alter Implementation of hook_file_url_alter().
cdn_flush_caches Implementation of hook_flush_caches().
cdn_form_system_performance_settings_alter Implementation of hook_form_alter().
cdn_get_blacklist See if any installed modules need to exclude certain files from being accessed from the CDN. List gets updated on cron runs.
cdn_get_domains Get all domains from which files might be served. This information is necessary for some modules, e.g. Boost.
cdn_init Implementation of hook_init().
cdn_load_include Helper function to efficiently load include files for this module.
cdn_menu Implementation of hook_menu().
cdn_nodeapi Implementation of hook_nodeapi().
cdn_perm Implementation of hook_perm().
cdn_request_is_https Check if the current request is over HTTPS.
cdn_serve_from_https Check if CDN module should serve files from a HTTPS location.
cdn_status_is_enabled Check if the CDN module is enabled.
cdn_theme Implementation of hook_theme().
cdn_theme_registry_alter Implementation of hook_theme_registry_alter().
_cdn_eval_pick_server Helper function to evaluate CDN_PICK_SERVER_PHP_CODE_VARIABLE, when that is being used instead of an actual cdn_pick_server() function.
_cdn_prefetch_dns Helper function to add DNS prefetching to the page.
_cdn_ufi_deployment_id Callback for generating a unique file identifier.