diff --git a/cdn.basic.inc b/cdn.basic.inc index c74ef68..4fd0150 100644 --- a/cdn.basic.inc +++ b/cdn.basic.inc @@ -96,17 +96,25 @@ function _cdn_basic_parse_raw_mapping($mapping_raw) { // file extensions. if (strpos($line, '|') !== FALSE) { $parts = explode('|', $line); - $cdn_url = rtrim(trim($parts[0]), '/'); // Remove whitespace and a trailing slash. - $extensions = explode(' ', trim(str_replace('.', '', strtolower($parts[1])))); // Conver to lower case, remove periods, whitespace and split on ' '. + $cdn_urls = explode(' ', trim($parts[0])); + $extensions = explode(' ', trim(str_replace('.', '', strtolower($parts[1])))); // Convert to lower case, remove periods, whitespace and split on ' '. } else { - $cdn_url = trim($line); + $cdn_urls = explode(' ', trim($line)); $extensions = array('*'); // Use the asterisk as a wildcard. } + // Remove trailing slash from URLs. + for ($i = 0; $i < count($cdn_urls); $i++) { + $cdn_urls[$i] = rtrim($cdn_urls[$i], '/'); + } + // Create the mapping lookup table. foreach ($extensions as $extension) { - $mapping[$extension][] = $cdn_url; + if (!isset($mapping[$extension])) { + $mapping[$extension] = array(); + } + $mapping[$extension] = array_merge($mapping[$extension], $cdn_urls); } } } diff --git a/cdn.module b/cdn.module index eeabbb4..3b6996a 100644 --- a/cdn.module +++ b/cdn.module @@ -177,7 +177,17 @@ function cdn_file_url_alter(&$original_uri) { $cdn_url = $picked_server['url']; $server = $picked_server['server']; } - // The file is available from at least one server, simply pick the first. + // The file is available from multiple servers; pick a server randomly + // (yet consistently: this uses consistent hashing!), so that each server + // will serve approximately the same number of files. + else if (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']; + } + // The file is only avaible from one server. else { $cdn_url = $servers[0]['url']; $server = $servers[0]['server']; diff --git a/cdn.test b/cdn.test index a1e361e..5898914 100644 --- a/cdn.test +++ b/cdn.test @@ -143,9 +143,9 @@ class CDNOriginPullTestCase extends CDNTestCase { // Ensure the parsing of the raw mapping works correctly. $this->assertMapping('', array()); $this->assertMapping('http://cdn-a.com', array('*' => array('http://cdn-a.com'))); - $this->assertMapping('http://cdn-a.com/', array('*' => array('http://cdn-a.com/'))); + $this->assertMapping('http://cdn-a.com/', array('*' => array('http://cdn-a.com'))); $this->assertMapping('//cdn-a.com', array('*' => array('//cdn-a.com'))); - $this->assertMapping('//cdn-a.com/', array('*' => array('//cdn-a.com/'))); + $this->assertMapping('//cdn-a.com/', array('*' => array('//cdn-a.com'))); $parsed_mapping = array( 'css' => array('http://cdn-a.com'), 'jpg' => array('http://cdn-a.com'), @@ -158,6 +158,17 @@ class CDNOriginPullTestCase extends CDNTestCase { "http://cdn-a.com|.css .jpg .jpeg .png\nhttp://cdn-b.com|.zip\nhttp://cdn-c.com", $parsed_mapping ); + $parsed_mapping = array( + 'css' => array('http://cdn-a.com', 'http://cdn-d.com'), + 'jpg' => array('http://cdn-a.com', 'http://cdn-d.com'), + 'jpeg' => array('http://cdn-a.com', 'http://cdn-d.com'), + 'png' => array('http://cdn-a.com', 'http://cdn-d.com', 'http://cdn-b.com'), + '*' => array('http://cdn-c.com'), + ); + $this->assertMapping( + "http://cdn-a.com http://cdn-d.com|.css .jpg .jpeg .png\nhttp://cdn-b.com|.png\nhttp://cdn-c.com", + $parsed_mapping + ); // When a HTTPS request is performed and the CDN is not marked to support // HTTPS, then it should fall back to the default CDN mapping.