Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.831
diff -u -9 -p -r1.831 common.inc
--- includes/common.inc	23 Nov 2008 16:54:47 -0000	1.831
+++ includes/common.inc	27 Nov 2008 21:35:21 -0000
@@ -1926,19 +1926,19 @@ function drupal_get_css($css = NULL) {
   $output = '';
   if (!isset($css)) {
     $css = drupal_add_css();
   }
   $no_module_preprocess = '';
   $no_theme_preprocess = '';
 
   $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
   $directory = file_directory_path();
-  $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
+  $is_writable = is_dir($directory) && is_writable($directory);
 
   // A dummy query-string is added to filenames, to gain control over
   // browser-caching. The string changes on every update or full cache
   // flush, forcing browsers to load a new copy of the files, as the
   // URL changed.
   $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1);
 
   foreach ($css as $media => $types) {
     // If CSS preprocessing is off, we still need to output the styles.
@@ -1976,36 +1976,36 @@ function drupal_get_css($css = NULL) {
               $output .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . base_path() . $file . $query_string . '" />' . "\n";
             }
           }
         }
       }
     }
 
     if ($is_writable && $preprocess_css) {
       $filename = md5(serialize($types) . $query_string) . '.css';
-      $preprocess_file = drupal_build_css_cache($types, $filename);
-      $output .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . base_path() . $preprocess_file . '" />' . "\n";
+      $preprocess_url = drupal_build_css_cache($types, $filename);
+      $output .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . check_plain($preprocess_url) . '" />' . "\n";
     }
   }
 
   return $no_module_preprocess . $output . $no_theme_preprocess;
 }
 
 /**
  * Aggregate and optimize CSS files, putting them in the files directory.
  *
  * @param $types
  *   An array of types of CSS files (e.g., screen, print) to aggregate and
  *   compress into one file.
  * @param $filename
  *   The name of the aggregate CSS file.
  * @return
- *   The name of the CSS file.
+ *   The URL of the CSS file.
  */
 function drupal_build_css_cache($types, $filename) {
   $data = '';
 
   // Create the css/ within the files folder.
   $csspath = file_create_path('css');
   file_check_directory($csspath, FILE_CREATE_DIRECTORY);
 
   if (!file_exists($csspath . '/' . $filename)) {
@@ -2027,19 +2027,19 @@ function drupal_build_css_cache($types, 
     // @import rules must proceed any other style, so we move those to the top.
     $regexp = '/@import[^;]+;/i';
     preg_match_all($regexp, $data, $matches);
     $data = preg_replace($regexp, '', $data);
     $data = implode('', $matches[0]) . $data;
 
     // Create the CSS file.
     file_unmanaged_save_data($data, $csspath . '/' . $filename, FILE_EXISTS_REPLACE);
   }
-  return $csspath . '/' . $filename;
+  return file_create_url($csspath . '/' . $filename);
 }
 
 /**
  * Helper function for drupal_build_css_cache().
  *
  * This function will prefix all paths within a CSS file.
  */
 function _drupal_build_css_path($matches, $base = NULL) {
   static $_base;
@@ -2364,19 +2364,19 @@ function drupal_get_js($scope = 'header'
     }
   }
 
   $output = '';
   $preprocessed = '';
   $no_preprocess = '';
   $files = array();
   $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
   $directory = file_directory_path();
-  $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
+  $is_writable = is_dir($directory) && is_writable($directory);
 
   // A dummy query-string is added to filenames, to gain control over
   // browser-caching. The string changes on every update or full cache
   // flush, forcing browsers to load a new copy of the files, as the
   // URL changed. Files that should not be cached (see drupal_add_js())
   // get REQUEST_TIME as query-string instead, to enforce reload on every
   // page request.
   $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1);
 
@@ -2408,20 +2408,20 @@ function drupal_get_js($scope = 'header'
           $files[$item['data']] = $item;
         }
         break;
     }
   }
 
   // Aggregate any remaining JS files that haven't already been output.
   if ($is_writable && $preprocess_js && count($files) > 0) {
     $filename = md5(serialize($files) . $query_string) . '.js';
-    $preprocess_file = drupal_build_js_cache($files, $filename);
-    $preprocessed .= '<script type="text/javascript" src="' . base_path() . $preprocess_file . '"></script>' . "\n";
+    $preprocess_url = drupal_build_js_cache($files, $filename);
+    $preprocessed .= '<script type="text/javascript" src="' . check_plain($preprocess_url) . '"></script>' . "\n";
   }
 
   // Keep the order of JS files consistent as some are preprocessed and others are not.
   // Make sure any inline or JS setting variables appear last after libraries have loaded.
   return $preprocessed . $no_preprocess . $output;
 }
 
 /**
  * Assist in adding the tableDrag JavaScript behavior to a themed table.
@@ -2556,19 +2556,19 @@ function drupal_add_tabledrag($table_id,
 
 /**
  * Aggregate JS files, putting them in the files directory.
  *
  * @param $files
  *   An array of JS files to aggregate and compress into one file.
  * @param $filename
  *   The name of the aggregate JS file.
  * @return
- *   The name of the JS file.
+ *   The URL of the JS file.
  */
 function drupal_build_js_cache($files, $filename) {
   $contents = '';
 
   // Create the js/ within the files folder.
   $jspath = file_create_path('js');
   file_check_directory($jspath, FILE_CREATE_DIRECTORY);
 
   if (!file_exists($jspath . '/' . $filename)) {
@@ -2578,19 +2578,19 @@ function drupal_build_js_cache($files, $
         // Append a ';' after each JS file to prevent them from running together.
         $contents .= file_get_contents($path) . ';';
       }
     }
 
     // Create the JS file.
     file_unmanaged_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE);
   }
 
-  return $jspath . '/' . $filename;
+  return file_create_url($jspath . '/' . $filename);
 }
 
 /**
  * Delete all cached JS files.
  */
 function drupal_clear_js_cache() {
   file_scan_directory(file_create_path('js'), '/.*/', '/(\.\.?|CVS)$/', 'file_unmanaged_delete', TRUE);
   variable_set('javascript_parsed', array());
 }
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.110
diff -u -9 -p -r1.110 system.admin.inc
--- modules/system/system.admin.inc	26 Nov 2008 13:54:05 -0000	1.110
+++ modules/system/system.admin.inc	27 Nov 2008 21:35:21 -0000
@@ -1373,26 +1373,27 @@ function system_performance_settings() {
   $form['block_cache']['block_cache'] = array(
     '#type' => 'radios',
     '#title' => t('Block cache'),
     '#default_value' => variable_get('block_cache', CACHE_DISABLED),
     '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL => t('Enabled (recommended)')),
     '#disabled' => count(module_implements('node_grants')),
     '#description' => t('Note that block caching is inactive when modules defining content access restrictions are enabled.'),
   );
 
+  $preprocess_warning = variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE && !variable_get('reverse_proxy', 0);
   $form['bandwidth_optimizations'] = array(
     '#type' => 'fieldset',
     '#title' => t('Bandwidth optimizations'),
-    '#description' => '<p>' . t('Drupal can automatically optimize external resources like CSS and JavaScript, which can reduce both the size and number of requests made to your website. CSS files can be aggregated and compressed into a single file, while JavaScript files are aggregated (but not compressed). These optional optimizations may reduce server load, bandwidth requirements, and page loading times.') . '</p><p>' . t('These options are disabled if you have not set up your files directory, or if your download method is set to private.') . '</p>'
+    '#description' => '<p>' . t('Drupal can automatically optimize external resources like CSS and JavaScript, which can reduce both the size and number of requests made to your website. CSS files can be aggregated and compressed into a single file, while JavaScript files are aggregated (but not compressed). These optional optimizations may reduce server load, bandwidth requirements, and page loading times.') . '</p>' . ($preprocess_warning ? '<p class="warning">' : '<p>') . t('If your download method is set to private, enabling these options actually increases the server load, unless your server is behind a reverse proxy (if you don\'t know what this is, you probably don\'t have one).') . '</p><p>' . t('These options are disabled if you have not set up your files directory.') . '</p>'
   );
 
   $directory = file_directory_path();
-  $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
+  $is_writable = is_dir($directory) && is_writable($directory);
   $form['bandwidth_optimizations']['preprocess_css'] = array(
     '#type' => 'radios',
     '#title' => t('Optimize CSS files'),
     '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable),
     '#disabled' => !$is_writable,
     '#options' => array(t('Disabled'), t('Enabled')),
     '#description' => t('This option can interfere with theme development and should only be enabled in a production environment.'),
   );
   $form['bandwidth_optimizations']['preprocess_js'] = array(
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.649
diff -u -9 -p -r1.649 system.module
--- modules/system/system.module	26 Nov 2008 18:56:16 -0000	1.649
+++ modules/system/system.module	27 Nov 2008 21:35:21 -0000
@@ -2246,9 +2246,32 @@ function theme_meta_generator_header($ve
   drupal_set_header('X-Generator: Drupal ' . $version . ' (http://drupal.org)');
 }
 
 /**
  * Implementation of hook_image_toolkits().
  */
 function system_image_toolkits() {
   return array('gd');
 }
+
+/**
+ * Implementation of hook_file_download().
+ */
+function system_file_download($filepath) {
+  $file = file_create_path($filepath);
+  // Allow download of files generated by drupal_build_css_cache() and
+  // drupal_build_js_cache().
+  if (preg_match('!^(js|css)/[a-f0-9]{32}\.\1$!', $filepath, $matches) && file_exists($file)) {
+    // Replace the headers set in drupal_page_header() with more cache-friendly
+    // ones. Allow caching for 2 weeks, corresponding to the setting for static
+    // files as specified in .htaccess. The filename is changed on a full cache
+    // flush, forcing browsers to load a new copy of the files, as the URL
+    // changed.
+    $headers = array(
+      'Content-Type: ' . ($matches[1] == 'js' ? 'application/x-javascript' : 'text/css'),
+      'Expires: ' . gmdate(DATE_RFC1123, REQUEST_TIME + 1209600),
+      'Last-Modified: ' . gmdate(DATE_RFC1123, filemtime($file)),
+      'Cache-Control: public',
+    );
+    file_transfer($file, $headers);
+  }
+}
