Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1251
diff -u -p -r1.1251 common.inc
--- includes/common.inc	12 Nov 2010 02:59:30 -0000	1.1251
+++ includes/common.inc	12 Nov 2010 13:20:13 -0000
@@ -347,22 +347,28 @@ function drupal_get_html_head() {
  *
  * This function can be called as long the HTML header hasn't been sent.
  *
- * @param $url
- *   A url for the feed.
+ * @param $path_or_external_url
+ *   The internal path or external URL of the feed. This should be either a raw
+ *   path (not having passed through url()) or an external URL.
  * @param $title
  *   The title of the feed.
  */
-function drupal_add_feed($url = NULL, $title = '') {
+function drupal_add_feed($path_or_external_url = NULL, $title = '') {
   $stored_feed_links = &drupal_static(__FUNCTION__, array());
 
-  if (isset($url)) {
-    $stored_feed_links[$url] = theme('feed_icon', array('url' => $url, 'title' => $title));
+  if (isset($path_or_external_url)) {
+    $stored_feed_links[$path_or_external_url] = theme('feed_icon', array('url' => $path_or_external_url, 'title' => $title));
 
-    drupal_add_html_head_link(array('rel' => 'alternate',
-                          'type' => 'application/rss+xml',
-                          'title' => $title,
-                          'href' => $url));
+    drupal_add_html_head_link(array(
+      'rel' => 'alternate',
+      'type' => 'application/rss+xml',
+      'title' => $title,
+      // Force the URL to be absolute, for consistency with other <link> tags
+      // output by Drupal.
+      'href' => url($path_or_external_url, array('absolute' => TRUE)),
+    ));
   }
+
   return $stored_feed_links;
 }
 
@@ -738,7 +744,7 @@ function drupal_access_denied() {
  *     call may take. The default is 30 seconds. If a timeout occurs, the error
  *     code is set to the HTTP_REQUEST_TIMEOUT constant.
  *   - context: A context resource created with stream_context_create().
- * 
+ *
  * @return object
  *   An object that can have one or more of the following components:
  *   - request: A string containing the request body that was sent.
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.622
diff -u -p -r1.622 theme.inc
--- includes/theme.inc	30 Oct 2010 03:57:20 -0000	1.622
+++ includes/theme.inc	12 Nov 2010 13:20:13 -0000
@@ -1866,7 +1866,8 @@ function theme_more_help_link($variables
  *
  * @param $variables
  *   An associative array containing:
- *   - url: The url of the feed.
+ *   - url: The internal path or external URL of the feed. This should be
+ *     either a raw path (not having passed through url()) or an external URL.
  *   - title: A descriptive title of the feed.
  */
 function theme_feed_icon($variables) {
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.580
diff -u -p -r1.580 forum.module
--- modules/forum/forum.module	6 Nov 2010 23:24:33 -0000	1.580
+++ modules/forum/forum.module	12 Nov 2010 13:20:13 -0000
@@ -982,7 +982,7 @@ function template_preprocess_forums(&$va
 
     if ($variables['tid'] && !in_array($variables['tid'], variable_get('forum_containers', array()))) {
       $variables['topics'] = theme('forum_topic_list', $variables);
-      drupal_add_feed('taxonomy/term/' . $variables['tid'] . '/0/feed', 'RSS - ' . $title);
+      drupal_add_feed('taxonomy/term/' . $variables['tid'] . '/feed', 'RSS - ' . $title);
     }
     else {
       $variables['topics'] = '';
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1317
diff -u -p -r1.1317 node.module
--- modules/node/node.module	9 Nov 2010 08:36:38 -0000	1.1317
+++ modules/node/node.module	12 Nov 2010 13:20:14 -0000
@@ -2521,8 +2521,7 @@ function node_page_default() {
     $nodes = node_load_multiple($nids);
     $build = node_view_multiple($nodes);
 
-    $feed_url = url('rss.xml', array('absolute' => TRUE));
-    drupal_add_feed($feed_url, variable_get('site_name', 'Drupal') . ' ' . t('RSS'));
+    drupal_add_feed('rss.xml', variable_get('site_name', 'Drupal') . ' ' . t('RSS'));
     $build['pager'] = array(
       '#theme' => 'pager',
       '#weight' => 5,
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.130
diff -u -p -r1.130 common.test
--- modules/simpletest/tests/common.test	8 Oct 2010 15:36:12 -0000	1.130
+++ modules/simpletest/tests/common.test	12 Nov 2010 13:20:14 -0000
@@ -2191,3 +2191,85 @@ class DrupalGetRdfNamespacesTestCase ext
     $this->assertTrue(!isset($ns['dc']), t('A prefix with conflicting namespaces is discarded.'));
   }
 }
+
+/**
+ * Basic tests for drupal_add_feed().
+ */
+class DrupalAddFeedTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'drupal_add_feed() tests',
+      'description' => 'Make sure that drupal_add_feed() works correctly with various constructs.',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Test drupal_add_feed() with paths, URLs, and titles.
+   */
+  function testBasicFeedAddNoTitle() {
+    $path = $this->randomName(12);
+    $external_url = 'http://' . $this->randomName(12) . '/' . $this->randomName(12);
+    $fully_qualified_local_url = url($this->randomName(12), array('absolute' => TRUE));
+
+    $path_for_title = $this->randomName(12);
+    $external_for_title = 'http://' . $this->randomName(12) . '/' . $this->randomName(12);
+    $fully_qualified_for_title = url($this->randomName(12), array('absolute' => TRUE));
+
+    // Possible permutations of drupal_add_feed() to test.
+    // - 'input_url': the path passed to drupal_add_feed(),
+    // - 'output_url': the expected URL to be found in the header.
+    // - 'title' == the title of the feed as passed into drupal_add_feed().
+    $urls = array(
+      'path without title' => array(
+        'input_url' => $path,
+        'output_url' => url($path, array('absolute' => TRUE)),
+        'title' => '',
+      ),
+      'external url without title' => array(
+        'input_url' => $external_url,
+        'output_url' => $external_url,
+        'title' => '',
+      ),
+      'local url without title' => array(
+        'input_url' => $fully_qualified_local_url,
+        'output_url' => $fully_qualified_local_url,
+        'title' => '',
+      ),
+      'path with title' => array(
+        'input_url' => $path_for_title,
+        'output_url' => url($path_for_title, array('absolute' => TRUE)),
+        'title' => $this->randomName(12),
+      ),
+      'external url with title' => array(
+        'input_url' => $external_for_title,
+        'output_url' => $external_for_title,
+        'title' => $this->randomName(12),
+      ),
+      'local url with title' => array(
+        'input_url' => $fully_qualified_for_title,
+        'output_url' => $fully_qualified_for_title,
+        'title' => $this->randomName(12),
+      ),
+    );
+
+    foreach ($urls as $description => $feed_info) {
+      drupal_add_feed($feed_info['input_url'], $feed_info['title']);
+    }
+
+    $this->drupalSetContent(drupal_get_html_head());
+    foreach ($urls as $description => $feed_info) {
+      $this->assertPattern($this->urlToRSSLinkPattern($feed_info['output_url'], $feed_info['title']), t('Found correct feed header for %description', array('%description' => $description)));
+    }
+  }
+
+  /**
+   * Create a pattern representing the RSS feed in the page.
+   */
+  function urlToRSSLinkPattern($url, $title = '') {
+    // Escape any regular expression characters in the url ('?' is the worst).
+    $url = preg_replace('/([+?.*])/', '[$0]', $url);
+    $generated_pattern = '%<link +rel="alternate" +type="application/rss.xml" +title="' . $title . '" +href="' . $url . '" */>%';
+    return $generated_pattern;
+  }
+}
