=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2011-07-26 23:09:58 +0000
+++ src/cache_cf.cc	2011-08-01 16:38:20 +0000
@@ -2244,6 +2244,28 @@
                 fatalf("parse_peer: non-parent carp peer %s/%d\n", p->host, p->http_port);
 
             p->options.carp = 1;
+        } else if (!strncasecmp(token, "carp-key", 8)) {
+            if (p->options.carp != 1)
+                fatalf("parse_peer: carp-key specified on non-carp peer %s/%d\n", p->host, p->http_port);
+            p->options.carp_key.set=1;
+            char *nextkey=token+strlen("carp-key="), *key=nextkey;
+            for (; key; key = nextkey) {
+            	nextkey=strchr(key,',');
+            	if (nextkey) ++nextkey; // skip the comma, if a comma is found.ut that
+            	if (0==strncasecmp(key,"scheme",6)) {
+            		p->options.carp_key.scheme=1;
+            	} else if (0==strncasecmp(key,"host",4)) {
+            		p->options.carp_key.host=1;
+            	} else if (0==strncasecmp(key,"port",4)) {
+            		p->options.carp_key.port=1;
+            	} else if (0==strncasecmp(key,"path",4)) {
+            		p->options.carp_key.path=1;
+            	} else if (0==strncasecmp(key,"params",6)) {
+            		p->options.carp_key.params=1;
+            	} else {
+            		fatalf("invalid carp-key '%s'",key);
+            	}
+            }
         } else if (!strcasecmp(token, "userhash")) {
 #if USE_AUTH
             if (p->type != PEER_PARENT)

=== modified file 'src/carp.cc'
--- src/carp.cc	2010-10-28 18:52:59 +0000
+++ src/carp.cc	2011-08-01 14:38:36 +0000
@@ -35,8 +35,10 @@
  */
 
 #include "squid.h"
+#include "HttpRequest.h"
 #include "mgr/Registration.h"
 #include "Store.h"
+#include "URLScheme.h"
 
 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
 
@@ -164,35 +166,69 @@
 carpSelectParent(HttpRequest * request)
 {
     int k;
-    const char *c;
     peer *p = NULL;
     peer *tp;
     unsigned int user_hash = 0;
     unsigned int combined_hash;
     double score;
     double high_score = 0;
-    const char *key = NULL;
 
     if (n_carp_peers == 0)
         return NULL;
 
-    key = urlCanonical(request);
-
     /* calculate hash key */
-    debugs(39, 2, "carpSelectParent: Calculating hash for " << key);
-
-    for (c = key; *c != 0; c++)
-        user_hash += ROTATE_LEFT(user_hash, 19) + *c;
+    debugs(39, 2, "carpSelectParent: Calculating hash for " << urlCanonical(request));
 
     /* select peer */
     for (k = 0; k < n_carp_peers; k++) {
+    	String key;
         tp = carp_peers[k];
+        if (tp->options.carp_key.set) {
+            //this code follows urlCanonical's pattern.
+            //   corner cases should use the canonical URL
+            if (tp->options.carp_key.scheme) {
+                // temporary, until bug 1961 URL handling is fixed.
+                const URLScheme sch = request->protocol;
+                key.append(sch.const_str());
+                if (key.size()) //if the scheme is not empty
+                    key.append("://");
+            }
+            if (tp->options.carp_key.host) {
+                key.append(request->GetHost());
+            }
+            if (tp->options.carp_key.port) {
+                static char portbuf[7];
+                snprintf(portbuf,7,":%d", request->port);
+                key.append(portbuf);
+            }
+            if (tp->options.carp_key.path) {
+                String::size_type pos;
+                if ((pos=request->urlpath.find('?'))!=String::npos)
+                    key.append(request->urlpath.substr(0,pos));
+                else
+                    key.append(request->urlpath);
+            }
+            if (tp->options.carp_key.params) {
+                String::size_type pos;
+                if ((pos=request->urlpath.find('?'))!=String::npos)
+                    key.append(request->urlpath.substr(pos,request->urlpath.size()));
+            }
+    	}
+        // if the url-based key is empty, e.g. because the user is
+        // asking to balance on the path but the request doesn't supply any,
+        // then fall back to canonical URL
+
+        if (key.size()==0)
+            key=urlCanonical(request);
+
+        for (const char *c = key.rawBuf(), *e=key.rawBuf()+key.size(); c < e; c++)
+            user_hash += ROTATE_LEFT(user_hash, 19) + *c;
         combined_hash = (user_hash ^ tp->carp.hash);
         combined_hash += combined_hash * 0x62531965;
         combined_hash = ROTATE_LEFT(combined_hash, 21);
         score = combined_hash * tp->carp.load_multiplier;
-        debugs(39, 3, "carpSelectParent: " << tp->name << " combined_hash " << combined_hash  <<
-               " score " << std::setprecision(0) << score);
+        debugs(39, 3, "carpSelectParent: key=" << key << " name=" << tp->name << " combined_hash=" << combined_hash  <<
+               " score=" << std::setprecision(0) << score);
 
         if ((score > high_score) && peerHTTPOkay(tp, request)) {
             p = tp;

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2011-07-27 01:41:00 +0000
+++ src/cf.data.pre	2011-08-01 14:04:58 +0000
@@ -2168,6 +2168,14 @@
 			than the Squid default location.
 	
 	
+	==== CARP OPTIONS ====
+	
+	carp-key=key-specification
+			use a different key than the full URL to hash against the peer.
+			the key-specification is a comma-separated list of the keywords			
+			scheme, host, port, path, params
+			Order is not important.
+	
 	==== ACCELERATOR / REVERSE-PROXY OPTIONS ====
 	
 	originserver	Causes this parent to be contacted as an origin server.

=== modified file 'src/structs.h'
--- src/structs.h	2011-06-23 08:33:13 +0000
+++ src/structs.h	2011-08-01 14:01:22 +0000
@@ -855,6 +855,14 @@
 #endif
         unsigned int allow_miss:1;
         unsigned int carp:1;
+        struct {
+        	unsigned int set:1; //If false, whole url is to be used. Overrides others
+        	unsigned int scheme:1;
+        	unsigned int host:1;
+        	unsigned int port:1;
+        	unsigned int path:1;
+        	unsigned int params:1;
+        } carp_key;
 #if USE_AUTH
         unsigned int userhash:1;
 #endif

