From: Francesco Chemolli Date: Tue, 2 Aug 2011 05:15:45 +0000 (+0200) Subject: Add support for using custom keys in CARP parents X-Git-Tag: take08~55^2~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=de03b59646490546d76c65df0c5ca48a5c5cdaa6;p=thirdparty%2Fsquid.git Add support for using custom keys in CARP parents Add a new carp-key option to CARP parents, specifying what parts of an URL to use in the parent selection algorithm. --- diff --git a/src/cache_cf.cc b/src/cache_cf.cc index 130c2f188d..ad87dff450 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -2244,6 +2244,28 @@ parse_peer(peer ** head) 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=", 9)) { + 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, any + 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) diff --git a/src/carp.cc b/src/carp.cc index f5f6f3624a..de5ee22b78 100644 --- a/src/carp.cc +++ b/src/carp.cc @@ -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 @@ peer * 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; diff --git a/src/cf.data.pre b/src/cf.data.pre index 7b55033137..84e44a4052 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -2168,6 +2168,14 @@ DOC_START 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. diff --git a/src/structs.h b/src/structs.h index 4d1710c5e3..fe6c7ea01b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -855,6 +855,14 @@ struct peer { #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