Skip to content
Commits on Source (38)
// $Id$
REQUIREMENTS
------------
......@@ -9,7 +8,7 @@ The `path' and `pathauto' modules are recommended.
In order for the static files to be correctly expired, the Drupal cron job
must be correctly setup to execute more often than, or as often as, the
cache lifetime interval.
cache lifetime interval you specify.
Since the static page caching is implemented with mod_rewrite directives,
Apache version 1.3 or 2.x with mod_rewrite enabled is required (if Drupal's
......@@ -20,25 +19,27 @@ The `drush' module is required for (optional) command line usage.
INSTALLATION
------------
1. Go to administer >> settings and ensure that Drupal's clean URLs are
enabled and working properly on your site.
1. Go to Administer >> Site configuration >> Clean URLs and ensure that
Drupal's clean URLs are enabled and working properly on your site.
2. Copy all the module files into a subdirectory called modules/boost/
under your Drupal installation directory.
3. Go to administer >> modules and enable the Boost module.
3. Go to Administer >> Site building >> Modules and enable the Boost module.
4. Go to administer >> settings >> boost to review and change the
configuration options to your liking.
4. Go to Administer >> Site configuration >> Performance >> Boost to review
and change the module's configuration options to your liking.
5. Go to administer >> settings and enable static caching.
5. Go to Administer >> Site configuration >> Performance, specify the cache
directory (must be writable by the web server) and enable static caching.
6. Log out from Drupal (or use another browser) and browse around your site
as the anonymous user. Ensure that static files are indeed being
generated into the Boost cache directory.
generated into the Boost cache directory you specified above.
7. IMPORTANT: replace your .htaccess file in the Drupal installation
directory with the file from modules/boost/htaccess/boosted.txt.
directory with the file from modules/boost/htaccess/boosted.txt,
customizing the mod_rewrite rules to your particular setup if needed.
(If you fail to do this, static page caching will NOT work!)
8. (See README.txt for information on submitting bug reports.)
// $Id$
NOTE: this module is currently in an alpha state. Come back in a bit unless
you're an experienced user and don't mind figuring things out on your own.
DESCRIPTION
-----------
This module provides static page caching for Drupal 4.7, enabling a
This module provides static page caching for Drupal 5.x, enabling a
potentially very significant performance and scalability boost for
heavily-trafficked Drupal sites.
......@@ -18,7 +14,6 @@ FEATURES
site, reducing web server load and boosting your site's scalability.
* On-demand page caching (static file created after first page request).
* Full support for multi-site Drupal installations.
* Command line administration support (requires the drush module).
INSTALLATION
------------
......@@ -98,6 +93,16 @@ served as per the following simple rules:
IMPORTANT NOTES
---------------
* Drupal URL aliases get written out to disk as relative symbolic links
pointing to the file representing the internal Drupal URL path. For this
to work correctly with Apache, ensure your .htaccess file contains the
following line (as it will by default if you've installed the file shipped
with Boost):
Options +FollowSymLinks
* To check whether you got a static or dynamic version of a page, look at
the very end of the page's HTML source. You have the static version if the
last line looks like this:
<!-- Page cached by Boost at 2006-11-24 15:06:31 -->
* If your Drupal URL paths contain non-ASCII characters, you may have to
tweak your locate settings on the server in order to ensure the URL paths
get correctly translated into directory paths on the file system.
......@@ -125,7 +130,7 @@ LIMITATIONS
* At the moment, Windows users are S.O.L. due to the use of symlinks and
Unix-specific shell commands. The author has no personal interest in
supporting Windows but will accept well-documented, non-detrimental
patches to that effect.
patches to that effect (see http://drupal.org/node/174380).
BUG REPORTS
-----------
......@@ -135,3 +140,6 @@ Post feature requests and bug reports to the issue tracking system at:
CREDITS
-------
Developed and maintained by Arto Bendiken <http://bendiken.net/>
Ported to Drupal 5.x by Alexander I. Grafov <http://drupal.ru/>
Miscellaneous contributions by: Jacob Peddicord, Justin Miller, Barry
Jaspan.
// $Id$
This is a listing of known bugs, features that mostly work but are still
somewhat in progress, features that are being considered or planned for
......
This lists some of the users of Boost, describing the setup of the website
in question as well as providing rationale on how Boost benefits the site.
It is hoped that the cases described here may serve as useful guides for new
Boost users evaluating how to best implement static caching on their site.
Stand Against Poverty <http://www.standagainstpoverty.org>
United Nations campaign website used to organize events and report event
attendance for events at which nearly 117 million people worldwide
participated. The events seek to raise awareness about poverty and
highlight effort around the United Nations Millennium Development Goals
that seek toward reducing global poverty. The website runs on a cluster
of 4 load-balanced Apache web servers and a single database server.
Boost is used to reduce the overall resource usage consumed by anonymous
visitors on the site in order to devote more infrastructure resources
toward event organizers who sign in as authenticated users to create
events and report event attendance tallies. The Stand Against Poverty
site is is 4 languages, and uses the i18n module. Boost was used with
this patch http://drupal.org/node/174380#comment-663794 to allow for the
use of Boost on i18n sites. While there is still a substantial amount of
traffic on the website during the 3 day campaign, the impact of
anonymous traffic (which includes all traffic, until users sign in) is
greatly reduced.
Hosting infrastructure for StandAgainstPoverty.org provided by the good
folks at http://www.advomatic.com
Development Seed writes more about the campaign on their blog at:
http://www.developmentseed.org/blog/2008/oct/22/united-nations-uses-drupal-huge-anti-poverty-event
http://www.developmentseed.org/blog/2008/oct/23/improving-drupals-performance-boost-module-uns-millennium-campaign
Environmental Working Group <http://www.ewg.org/>
The Environmental Working Group (EWG) uses the power of public
information to protect public health and the environment. Boost is used
to cache all public-facing pages on the site (13,000+ and counting) and
has been critical in sustaining EWG's large amounts of traffic since the
site relaunched using Drupal in early 2007. EWG frequently receives
traffic from multiple press outlets on a given day and Boost allows EWG
to manage its infrastructure in-house and at a fraction of the price
that would otherwise be required.
Arto Bendiken <http://bendiken.net/>
Personal website of the author. Boost is used to cache virtually every
page on the site, quite significantly improving response times despite
the sometimes sluggish shared hosting the site runs on. An additional
benefit provided by Boost is that when the backend MySQL database server
goes down, as happens now and then, the site still keeps on trucking
instead of just showing the Drupal database error page (dynamic features
such as posting comments obviously don't work until MySQL access is
restored, however).
If you would like to add your website to this list, please contact the
author at http://drupal.org/user/26089/contact, describing your site and
setup. Try to keep the description to a paragraph or two, and don't forget
to include your name and the URL to your website. Note that additions to
this list are posted at the author's sole discretion, and submissions may be
abridged or edited for grammar.
<?php
// $Id$
/**
* @file
......@@ -52,10 +51,10 @@ function boost_settings_form($form = array()) {
//_boost_check_htaccess(); // TODO
$options = array(t('Cache every page except the listed pages.'), t('Cache only the listed pages.'));
$description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '%blog' for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => theme('placeholder', 'blog'), '%blog-wildcard' => theme('placeholder', 'blog/*'), '%front' => theme('placeholder', '<front>')));
$description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
if (user_access('use PHP for block visibility')) {
$options[] = t('Cache pages for which the following PHP code returns <code>TRUE</code> (PHP-mode, experts only).');
$description .= t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can severely break your Drupal site.', array('%php' => theme('placeholder', '<?php ?>')));
$description .= t(' If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can severely break your Drupal site.', array('%php' => '<?php ?>'));
}
$form['cacheability'] = array(
'#type' => 'fieldset',
......
<?php
// $Id$
/**
* @file
......@@ -43,12 +42,26 @@ function boost_is_cacheable($path) {
return !(BOOST_CACHEABILITY_OPTION xor preg_match($regexp, $alias));
}
/**
* Determines whether a given Drupal page is currently cached or not.
*/
function boost_is_cached($path) {
$path = (empty($path) ? BOOST_FRONTPAGE : $path);
$alias = drupal_get_path_alias($path);
$path = drupal_get_normal_path($path); // normalize path
// TODO: also determine if alias/symlink exists?
return file_exists(boost_file_path($path));
}
/**
* Deletes all static files currently in the cache.
*/
function boost_cache_clear_all() {
clearstatcache();
return _boost_rmdir_rf(boost_cache_directory());
if (($cache_dir = boost_cache_directory()) && file_exists($cache_dir)) {
return _boost_rmdir_rf($cache_dir);
}
}
/**
......@@ -56,7 +69,9 @@ function boost_cache_clear_all() {
*/
function boost_cache_expire_all() {
clearstatcache();
_boost_rmdir_rf(boost_cache_directory(), 'boost_file_is_expired');
if (($cache_dir = boost_cache_directory()) && file_exists($cache_dir)) {
_boost_rmdir_rf($cache_dir, 'boost_file_is_expired');
}
return TRUE;
}
......@@ -70,14 +85,12 @@ function boost_cache_expire($path, $wildcard = FALSE) {
$alias = drupal_get_path_alias($path);
$path = drupal_get_normal_path($path); // normalize path
$filename = boost_file_path($path);
if (file_exists($filename))
if (($filename = boost_file_path($path)) && file_exists($filename)) {
@unlink($filename);
}
if ($alias != $path) {
$symlink = boost_file_path($alias);
if (is_link($symlink))
@unlink($symlink);
if ($alias != $path && ($symlink = boost_file_path($alias)) && is_link($symlink)) {
@unlink($symlink);
}
return TRUE;
......@@ -89,9 +102,11 @@ function boost_cache_expire($path, $wildcard = FALSE) {
function boost_cache_get($path) {
$path = drupal_get_normal_path($path); // normalize path
$filename = boost_file_path($path);
if (file_exists($filename) && is_readable($filename))
return file_get_contents($filename);
if (($filename = boost_file_path($path))) {
if (file_exists($filename) && is_readable($filename)) {
return file_get_contents($filename);
}
}
return NULL;
}
......@@ -101,8 +116,11 @@ function boost_cache_get($path) {
* Replaces the cached contents of the specified page, if stale.
*/
function boost_cache_set($path, $data = '') {
// Append the Boost footer with the current timestamp
$data = rtrim($data) . "\n" . str_replace('%date', date('Y-m-d H:i:s'), BOOST_BANNER);
// Append the Boost footer with the relevant timestamps
$time = time();
$cached_at = date('Y-m-d H:i:s', $time);
$expires_at = date('Y-m-d H:i:s', $time + variable_get('cache_lifetime', 600));
$data = rtrim($data) . "\n" . str_replace(array('%cached_at', '%expires_at'), array($cached_at, $expires_at), BOOST_BANNER);
// Execute the pre-process function if one has been defined
if (function_exists(BOOST_PRE_PROCESS_FUNCTION))
......@@ -112,23 +130,24 @@ function boost_cache_set($path, $data = '') {
$path = drupal_get_normal_path($path); // normalize path
// Create or update the static file as needed
$filename = boost_file_path($path);
_boost_mkdir_p(dirname($filename));
if (!file_exists($filename) || boost_file_is_expired($filename)) {
if (file_put_contents($filename, $data) === FALSE) {
watchdog('boost', t('Unable to write file: %file', array('%file' => $filename)), WATCHDOG_WARNING);
if (($filename = boost_file_path($path))) {
_boost_mkdir_p(dirname($filename));
if (!file_exists($filename) || boost_file_is_expired($filename)) {
if (file_put_contents($filename, $data) === FALSE) {
watchdog('boost', t('Unable to write file: %file', array('%file' => $filename)), WATCHDOG_WARNING);
}
}
}
// If a URL alias is defined, create that as a symlink to the actual file
if ($alias != $path) {
$symlink = boost_file_path($alias);
_boost_mkdir_p(dirname($symlink));
if (!is_link($symlink) || realpath(readlink($symlink)) != realpath($filename)) {
if (file_exists($symlink))
@unlink($symlink);
if (!_boost_symlink($filename, $symlink)) {
watchdog('boost', t('Unable to create symlink: %link to %target', array('%link' => $symlink, '%target' => $filename)), WATCHDOG_WARNING);
// If a URL alias is defined, create that as a symlink to the actual file
if ($alias != $path && ($symlink = boost_file_path($alias))) {
_boost_mkdir_p(dirname($symlink));
if (!is_link($symlink) || realpath(readlink($symlink)) != realpath($filename)) {
if (file_exists($symlink)) {
@unlink($symlink);
}
if (!_boost_symlink($filename, $symlink)) {
watchdog('boost', t('Unable to create symlink: %link to %target', array('%link' => $symlink, '%target' => $filename)), WATCHDOG_WARNING);
}
}
}
}
......@@ -153,9 +172,21 @@ function boost_cache_directory($user_id = 0, $host = NULL) {
* Returns the static file path for a Drupal page.
*/
function boost_file_path($path) {
if ($path == BOOST_FRONTPAGE)
$path = 'index'; // special handling for Drupal front page
return implode('/', array(boost_cache_directory(), $path)) . BOOST_FILE_EXTENSION;
if (empty($path) || $path == BOOST_FRONTPAGE) {
$path = 'index'; // special handling for Drupal's front page
}
// Under no circumstances should the incoming path contain '..' or null
// bytes; we also limit the maximum directory nesting depth of the path
if (strpos($path, '..') !== FALSE || strpos($path, "\0") !== FALSE ||
count(explode('/', $path)) > BOOST_MAX_PATH_DEPTH) {
return FALSE;
}
// Convert any other undesirable characters in the path to underscores
$path = preg_replace('@[^/a-z0-9_-]@i', '_', $path);
return boost_cache_directory() . '/' . $path . BOOST_FILE_EXTENSION;
}
/**
......
<?php
// $Id$
/**
* @file
* Actions for managing the static page cache provided by the Boost module.
*/
//////////////////////////////////////////////////////////////////////////////
/**
* Lists all files currently in the Boost static file system cache.
*/
function drush_boost_list() {
// TODO: implementation.
}
/**
* Expires all files, or all files matching a given path, from the static page cache.
*/
function drush_boost_expire($path = NULL) {
drush_op('boost_cache_expire', $path, TRUE);
if (DRUSH_VERBOSE) {
drush_print(empty($key) ? t('Boost static page cache fully cleared.') :
t("Boost cached pages like `%path' expired.", array('%path' => $path)));
}
}
/**
* Enables the Boost static page cache.
*/
function drush_boost_enable() {
drush_op('variable_set', 'boost', CACHE_ENABLED);
if (DRUSH_VERBOSE) {
drush_print(t('Boost static page cache enabled.'));
}
}
/**
* Disables the Boost static page cache.
*/
function drush_boost_disable() {
drush_op('variable_set', 'boost', CACHE_DISABLED);
if (DRUSH_VERBOSE) {
drush_print(t('Boost static page cache disabled.'));
}
}
//////////////////////////////////////////////////////////////////////////////
<?php
// $Id$
/**
* @file
......@@ -21,18 +20,38 @@ function _boost_mkdir_p($pathname, $mode = 0775, $recursive = TRUE) {
/**
* Recursive version of rmdir(); use with extreme caution.
*
* @param $dirname
* the top-level directory that will be recursively removed
* @param $callback
* optional predicate function for determining if a file should be removed
*/
function _boost_rmdir_rf($dirname, $callback = NULL) {
$empty = TRUE; // Start with an optimistic mindset
foreach (glob($dirname . '/*', GLOB_NOSORT) as $file) {
if (is_dir($file)) {
_boost_rmdir_rf($file, $callback);
if (!_boost_rmdir_rf($file, $callback))
$empty = FALSE;
}
else if (is_file($file)) {
if (!$callback || (function_exists($callback) && $callback($file)))
@unlink($file);
if (function_exists($callback)) {
if (!$callback($file)) {
$empty = FALSE;
continue;
}
}
@unlink($file);
}
else {
$empty = FALSE; // it's probably a symbolic link
}
}
return @rmdir($dirname);
// The reason for this elaborate safeguard is that Drupal will log even
// warnings that should have been suppressed with the @ sign. Otherwise,
// we'd just rely on the FALSE return value from rmdir().
return ($empty && @rmdir($dirname));
}
/**
......@@ -42,8 +61,8 @@ function _boost_symlink($target, $link) {
if (!file_exists($target) || !file_exists(dirname($link)))
return FALSE;
$target = explode('/', realpath($target));
$link = explode('/', realpath($link));
$target = explode('/', $target);
$link = explode('/', $link);
// Only bother creating a relative link if the paths are in the same
// top-level directory; otherwise just symlink to the absolute path.
......@@ -64,3 +83,17 @@ function _boost_symlink($target, $link) {
}
//////////////////////////////////////////////////////////////////////////////
// PHP4 COMPATIBILITY
if (!function_exists('file_put_contents')) {
function file_put_contents($filename, $data) {
if ($fp = fopen($filename, 'wb')) {
fwrite($fp, $data);
fclose($fp);
return filesize($filename);
}
return FALSE;
}
}
//////////////////////////////////////////////////////////////////////////////
; $Id$
name = Boost
description = Provides a performance and scalability boost through caching Drupal pages as static HTML files.
package = Caching
<?php
// $Id$
/**
* @file
......@@ -12,6 +11,8 @@
* Implementation of hook_install(). Installs the current version of the database schema.
*/
function boost_install() {
// Ensure that the module is loaded early in the bootstrap:
db_query("UPDATE {system} SET weight = -90 WHERE name = 'boost'");
// Forcibly disable Drupal's built-in SQL caching to prevent any conflicts of interest:
if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
......
<?php
// $Id$
/**
* @file
......@@ -15,6 +14,7 @@ define('BOOST_FRONTPAGE', drupal_get_normal_path(variable_get('site_f
define('BOOST_ENABLED', variable_get('boost', CACHE_DISABLED));
define('BOOST_FILE_PATH', variable_get('boost_file_path', 'cache'));
define('BOOST_FILE_EXTENSION', variable_get('boost_file_extension', '.html'));
define('BOOST_MAX_PATH_DEPTH', 10);
define('BOOST_CACHEABILITY_OPTION', variable_get('boost_cacheability_option', 0));
define('BOOST_CACHEABILITY_PAGES', variable_get('boost_cacheability_pages', ''));
define('BOOST_FETCH_METHOD', variable_get('boost_fetch_method', 'php'));
......@@ -29,7 +29,7 @@ define('BOOST_COOKIE', variable_get('boost_cookie', 'DRUPAL_UID'))
// This line is appended to the generated static files; it is very useful
// for troubleshooting (e.g. determining whether one got the dynamic or
// static version):
define('BOOST_BANNER', variable_get('boost_banner', "<!-- Page cached by Boost at %date -->\n"));
define('BOOST_BANNER', variable_get('boost_banner', "<!-- Page cached by Boost at %cached_at, expires at %expires_at -->\n"));
// This is needed since the $user object is already destructed in _boost_ob_handler():
define('BOOST_USER_ID', $GLOBALS['user']->uid);
......@@ -76,6 +76,14 @@ function boost_menu($may_cache) {
$access = user_access('administer cache');
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/performance/boost',
'title' => t('Boost'),
'description' => t('Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.'),
'callback' => 'drupal_get_form',
'callback arguments' => array('boost_settings'),
'access' => user_access('administer site configuration'),
);
// TODO: define menu actions for cache administration.
}
return $items;
......@@ -85,13 +93,26 @@ function boost_menu($may_cache) {
* Implementation of hook_init(). Performs page setup tasks.
*/
function boost_init() {
// Stop right here unless we're being called for an ordinary page request
if (strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') === FALSE)
return;
// TODO: check interaction with other modules that use ob_start(); this
// may have to be moved to an earlier stage of the page request.
if (!variable_get('cache', CACHE_DISABLED) && BOOST_ENABLED) {
// We only support GET requests by anonymous visitors:
global $user;
if (empty($user->uid) && $_SERVER['REQUEST_METHOD'] == 'GET') {
if (boost_is_cacheable($_GET['q']))
// Make sure no query string (in addition to ?q=) was set, and that
// the page is cacheable according to our current configuration:
if (count($_GET) == 1 && boost_is_cacheable($_GET['q'])) {
// In the event of errors such as drupal_not_found(), GET['q'] is
// changed before _boost_ob_handler() is called. Apache is going to
// look in the cache for the original path, however, so we need to
// preserve it.
$GLOBALS['_boost_path'] = $_GET['q'];
ob_start('_boost_ob_handler');
}
}
}
......@@ -114,15 +135,56 @@ function boost_init() {
}
}
/**
* Implementation of hook_exit(). Performs cleanup tasks.
*
* For POST requests by anonymous visitors, this adds a dummy query string
* to any URL being redirected to using drupal_goto().
*
* This is pretty much a hack that assumes a bit too much familiarity with
* what happens under the hood of the Drupal core function drupal_goto().
*
* It's necessary, though, in order for any session messages set on form
* submission to actually show up on the next page if that page has been
* cached by Boost.
*/
function boost_exit($destination = NULL) {
// Check that hook_exit() was invoked by drupal_goto() for a POST request:
if (!empty($destination) && $_SERVER['REQUEST_METHOD'] == 'POST') {
// Check that we're dealing with an anonymous visitor. and that some
// session messages have actually been set during this page request:
global $user;
if (empty($user->uid) && ($messages = drupal_set_message())) {
// Check that the page we're redirecting to really necessitates
// special handling, i.e. it doesn't have a query string:
extract(parse_url($destination));
$path = ($path == base_path() ? '' : substr($path, strlen(base_path())));
if (empty($query)) {
// FIXME: call any remaining exit hooks since we're about to terminate?
// If no query string was previously set, add one just to ensure we
// don't serve a static copy of the page we're redirecting to, which
// would prevent the session messages from showing up:
$destination = url($path, 't=' . time(), $fragment, TRUE);
// Do what drupal_goto() would do if we were to return to it:
exit(header('Location: ' . $destination));
}
}
}
}
/**
* Implementation of hook_form_alter(). Performs alterations before a form
* is rendered.
*/
function boost_form_alter($form_id, &$form) {
// Alter Drupal's settings form by hiding the default cache enabled/disabled control (which will now always default to CACHE_DISABLED), and add our own control instead.
if ($form_id == 'system_settings_form') {
if ($form_id == 'system_performance_settings') {
require_once BOOST_PATH . '/boost.admin.inc';
$form['cache'] = boost_system_settings_form($form['cache']);
$form['page_cache'] = boost_system_settings_form($form['page_cache']);
}
}
......@@ -150,6 +212,12 @@ function boost_comment($comment, $op) {
if (!empty($comment['nid']))
boost_cache_expire('node/' . $comment['nid'], TRUE);
break;
case 'publish':
case 'unpublish':
case 'delete':
if (!empty($comment->nid))
boost_cache_expire('node/' . $comment->nid, TRUE);
break;
}
}
......@@ -221,14 +289,14 @@ function boost_user($op, &$edit, &$account, $category = NULL) {
*/
function boost_settings() {
require_once BOOST_PATH . '/boost.admin.inc';
return boost_settings_form();
return system_settings_form(boost_settings_form());
}
//////////////////////////////////////////////////////////////////////////////
// OUTPUT BUFFERING CALLBACK
/**
* PHP output buffering callback.
* PHP output buffering callback for static page caching.
*
* NOTE: objects have already been destructed so $user is not available.
*/
......@@ -237,8 +305,10 @@ function _boost_ob_handler($buffer) {
chdir(dirname($_SERVER['SCRIPT_FILENAME']));
// Check the currently set content type; at present we can't deal with anything else than HTML.
if (_boost_get_content_type() == 'text/html') {
boost_cache_set($_GET['q'], $buffer);
if (_boost_get_content_type() == 'text/html' && _boost_get_http_status() == 200) {
if (strlen($buffer) > 0) { // Sanity check
boost_cache_set($GLOBALS['_boost_path'], $buffer);
}
}
// Allow the page request to finish up normally
......@@ -253,14 +323,27 @@ function _boost_ob_handler($buffer) {
* has overridden the content type.
*/
function _boost_get_content_type($default = NULL) {
static $regex = '/^Content-Type:\s*([\w\d\/\-]+)/i';
static $regex = '!^Content-Type:\s*([\w\d\/\-]+)!i';
return _boost_get_http_header($regex, $default);
}
// The last Content-Type header is the one that counts:
/**
* Determines the HTTP response code that the current page request will be
* returning by examining the HTTP headers that have been output so far.
*/
function _boost_get_http_status($default = 200) {
static $regex = '!^HTTP/1.1\s+(\d+)!';
return (int)_boost_get_http_header($regex, $default);
}
function _boost_get_http_header($regex, $default = NULL) {
// The last header is the one that counts:
$headers = preg_grep($regex, explode("\n", drupal_set_header()));
if (!empty($headers) && preg_match($regex, array_pop($headers), $matches))
if (!empty($headers) && preg_match($regex, array_pop($headers), $matches)) {
return $matches[1]; // found it
return $default;
}
return $default; // no such luck
}
//////////////////////////////////////////////////////////////////////////////
......@@ -3,13 +3,14 @@
#
# Protect files and directories from prying eyes.
<FilesMatch "(\.(engine|inc|install|module|sh|.*sql|theme|tpl(\.php)?|xtmpl)|code-style\.pl|Entries.*|Repository|Root)$">
Order deny,allow
Deny from all
<FilesMatch "(\.(engine|inc|info|install|module|profile|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)|code-style\.pl|Entries.*|Repository|Root|Tag|Template)$">
Order allow,deny
</FilesMatch>
# Set some options.
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Customized error messages.
......@@ -21,29 +22,43 @@ DirectoryIndex index.php
# Override PHP settings. More in sites/default/settings.php
# but the following cannot be changed at runtime.
# PHP 4, Apache 1
# PHP 4, Apache 1.
<IfModule mod_php4.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# PHP 4, Apache 2
# PHP 4, Apache 2.
<IfModule sapi_apache2.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# PHP 5, Apache 1 and 2
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# Reduce the time dynamically generated pages are cache-able.
# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
# Enable expirations.
ExpiresActive On
# Cache all files for 2 weeks after access (A).
ExpiresDefault A1209600
# Do not cache dynamically generated pages.
ExpiresByType text/html A1
</IfModule>
......@@ -51,17 +66,21 @@ DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine on
# If your site can be accessed both with and without the prefix www.
# you can use one of the following settings to force user to use only one option:
# If your site can be accessed both with and without the 'www.' prefix, you
# can use one of the following settings to redirect users to your preferred
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
#
# If you want the site to be accessed WITH the www. only, adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
# RewriteRule .* http://www.example.com/ [L,R=301]
# To redirect all users to access the site WITH the 'www.' prefix,
# (http://example.com/... will be redirected to http://www.example.com/...)
# adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
# RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301]
#
# If you want the site to be accessed only WITHOUT the www. , adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} !^example\.com$ [NC]
# RewriteRule .* http://example.com/ [L,R=301]
# To redirect all users to access the site WITHOUT the 'www.' prefix,
# (http://www.example.com/... will be redirected to http://example.com/...)
# adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
# RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
# Modify the RewriteBase if you are using Drupal in a subdirectory and
# the rewrite rules are not working properly.
......@@ -79,21 +98,38 @@ DirectoryIndex index.php
#RewriteCond %{QUERY_STRING} ^mod=([^&]+)$
#RewriteRule module.php index.php?q=%1 [L]
# Rewrite rules for static page caching provided by the Boost module
# Rewrite rules for static page caching provided by the Boost module:
# BOOST START
RewriteCond %{REQUEST_URI} !^/cache
<IfModule mod_headers.c>
Header add Expires "Sun, 19 Nov 1978 05:00:00 GMT"
Header add Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
</IfModule>
<IfModule mod_mime.c>
AddCharset utf-8 .html
</IfModule>
RewriteCond %{REQUEST_METHOD} ^GET$
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} ^$
RewriteCond %{HTTP_COOKIE} !DRUPAL_UID
RewriteCond %{DOCUMENT_ROOT}/cache/%{HTTP_HOST}/0/%{REQUEST_URI} -d
RewriteCond %{DOCUMENT_ROOT}/cache/%{HTTP_HOST}/0/%{REQUEST_URI}/index.html -f
RewriteRule ^(.*)$ cache/%{HTTP_HOST}/0/$1/index.html [L]
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}/0/index.html -f
RewriteRule ^(.*)$ cache/%{SERVER_NAME}/0/index.html [L]
RewriteCond %{REQUEST_METHOD} ^GET$
RewriteCond %{REQUEST_URI} !^/cache
RewriteCond %{REQUEST_URI} !^/user/login
RewriteCond %{REQUEST_URI} !^/admin
RewriteCond %{QUERY_STRING} ^$
RewriteCond %{HTTP_COOKIE} !DRUPAL_UID
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}/0%{REQUEST_URI} -d
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}/0%{REQUEST_URI}/index.html -f
RewriteRule ^(.*)$ cache/%{SERVER_NAME}/0/$1/index.html [L]
RewriteCond %{REQUEST_METHOD} ^GET$
RewriteCond %{REQUEST_URI} !^/cache
RewriteCond %{REQUEST_URI} !^/user/login
RewriteCond %{REQUEST_URI} !^/admin
RewriteCond %{QUERY_STRING} ^$
RewriteCond %{HTTP_COOKIE} !DRUPAL_UID
RewriteCond %{DOCUMENT_ROOT}/cache/%{HTTP_HOST}/0/%{REQUEST_URI}.html -f
RewriteRule ^(.*)$ cache/%{HTTP_HOST}/0/$1.html [L]
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}/0%{REQUEST_URI}.html -f
RewriteRule ^(.*)$ cache/%{SERVER_NAME}/0/$1.html [L]
# BOOST END
# Rewrite current-style URLs of the form 'index.php?q=x'.
......@@ -102,4 +138,3 @@ DirectoryIndex index.php
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
</IfModule>
# $Id$
......@@ -3,13 +3,14 @@
#
# Protect files and directories from prying eyes.
<FilesMatch "(\.(engine|inc|install|module|sh|.*sql|theme|tpl(\.php)?|xtmpl)|code-style\.pl|Entries.*|Repository|Root)$">
Order deny,allow
Deny from all
<FilesMatch "(\.(engine|inc|info|install|module|profile|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)|code-style\.pl|Entries.*|Repository|Root|Tag|Template)$">
Order allow,deny
</FilesMatch>
# Set some options.
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Customized error messages.
......@@ -21,29 +22,43 @@ DirectoryIndex index.php
# Override PHP settings. More in sites/default/settings.php
# but the following cannot be changed at runtime.
# PHP 4, Apache 1
# PHP 4, Apache 1.
<IfModule mod_php4.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# PHP 4, Apache 2
# PHP 4, Apache 2.
<IfModule sapi_apache2.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# PHP 5, Apache 1 and 2
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# Reduce the time dynamically generated pages are cache-able.
# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
# Enable expirations.
ExpiresActive On
# Cache all files for 2 weeks after access (A).
ExpiresDefault A1209600
# Do not cache dynamically generated pages.
ExpiresByType text/html A1
</IfModule>
......@@ -51,17 +66,21 @@ DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine on
# If your site can be accessed both with and without the prefix www.
# you can use one of the following settings to force user to use only one option:
# If your site can be accessed both with and without the 'www.' prefix, you
# can use one of the following settings to redirect users to your preferred
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
#
# If you want the site to be accessed WITH the www. only, adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
# RewriteRule .* http://www.example.com/ [L,R=301]
# To redirect all users to access the site WITH the 'www.' prefix,
# (http://example.com/... will be redirected to http://www.example.com/...)
# adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
# RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301]
#
# If you want the site to be accessed only WITHOUT the www. , adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} !^example\.com$ [NC]
# RewriteRule .* http://example.com/ [L,R=301]
# To redirect all users to access the site WITHOUT the 'www.' prefix,
# (http://www.example.com/... will be redirected to http://example.com/...)
# adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
# RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
# Modify the RewriteBase if you are using Drupal in a subdirectory and
# the rewrite rules are not working properly.
......@@ -85,4 +104,3 @@ DirectoryIndex index.php
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
</IfModule>
# $Id$