Hi,
This is a initial patch to add a http header based persistance to haproxy. This is very initial (i need a better way to setup my special Host hash method :))
This is a patch for haproxy 1.3.15.4
Comment/suggestions/tests are welcome :)
diff -ru haproxy-1.3.15.4/include/types/backend.h haproxy-cur/include/types/backend.h
--- haproxy-1.3.15.4/include/types/backend.h 2008-09-14 18:42:28.000000000 +0200 +++ haproxy-cur/include/types/backend.h 2008-10-01 10:40:45.000000000 +0200 @@ -43,6 +43,7 @@ #define BE_LB_ALGO_UH (BE_LB_PROP_L7 | 0x03) /* balance on URI hash */ #define BE_LB_ALGO_PH (BE_LB_PROP_L7 | 0x04) /* balance on URL parameter hash */ #define BE_LB_ALGO_LC (BE_LB_PROP_DYN | 0x05) /* fast weighted round-robin mode (dynamic) */ +#define BE_LB_ALGO_HH (BE_LB_PROP_L7 | 0x06) /* balance on Http Header value */
/* various constants */
diff -ru haproxy-1.3.15.4/include/types/proxy.h haproxy-cur/include/types/proxy.h
--- haproxy-1.3.15.4/include/types/proxy.h 2008-09-14 18:42:28.000000000 +0200 +++ haproxy-cur/include/types/proxy.h 2008-09-30 12:01:10.000000000 +0200 @@ -166,6 +166,8 @@ char *url_param_name; /* name of the URL parameter used for hashing */ int url_param_len; /* strlen(url_param_name), computed only once */ unsigned url_param_post_limit; /* if checking POST body for URI parameter, max body to wait for */
+ char *header_name; /* name of the header parameter used for hashing */
+ int header_len; /* strlen(header_name), computed only once */
char *appsession_name; /* name of the cookie to look for */ int appsession_name_len; /* strlen(appsession_name), computed only once */ int appsession_len; /* length of the appsession cookie value to be used */ diff -ru haproxy-1.3.15.4/src/backend.c haproxy-cur/src/backend.c --- haproxy-1.3.15.4/src/backend.c 2008-09-14 18:42:28.000000000 +0200 +++ haproxy-cur/src/backend.c 2008-10-01 11:24:17.000000000 +0200 @@ -1281,6 +1281,94 @@ return NULL;
+/* + * This function tries to find a running server for the proxy <px> following + * the Header parameter hash method. It looks for a specific parameter in the + * URL and hashes it to compute the server ID. This is useful to optimize + * performance by avoiding bounces between servers in contexts where sessions + * are shared but cookies are not usable. If the parameter is not found, NULL + * is returned. If any server is found, it will be returned. If no valid server + * is found, NULL is returned. + */ +struct server *get_server_hh(struct session *s) +{Received on 2008/10/01 12:10
+ unsigned long hash = 0;
+ struct http_txn *txn = &s->txn;
+ struct buffer *req = s->req;
+ struct http_msg *msg = &txn->req;
+ struct proxy *px = s->be;
+ unsigned int plen = px->header_len;
+ unsigned long body;
+ unsigned long len;
+ const char *params;
+ struct hdr_ctx ctx;
+ const char *p;
+
+ /* tot_weight appears to mean srv_count */
+ if (px->lbprm.tot_weight == 0)
+ return NULL;
+
+ body = msg->sol[msg->eoh] == '\r' ? msg->eoh + 2 : msg->eoh + 1;
+ len = req->l - body;
+ params = req->data + body;
+
+ if ( len == 0 )
+ return NULL;
+
+ if (px->lbprm.map.state & PR_MAP_RECALC)
+ recalc_server_map(px);
+
+ ctx.idx = 0;
+
+ /* if the message is chunked, we skip the chunk size, but use the value as len */
+ http_find_header2(px->header_name, plen, msg->sol, &txn->hdr_idx, &ctx);
+ if ( ctx.idx ) {
+ /* Found a the header_name in the headers
+ we will compute the hash based on this value ctx.val */
+ len = ctx.vlen;
+ p = (char *)ctx.line + ctx.val;
+ DPRINTF (stderr, "Found value for %s: '%.*s', length %i\n",
+ px->header_name,
+ (int)len, p, (int)len
+ );
+
+ DPRINTF (stderr, "Hashing on: ");
+#if 1
+ while (len) {
+ DPRINTF (stderr, "%c",*p);
+ hash = *p + (hash << 6) + (hash << 16) - hash;
+ len--;
+ p++;
+ }
+#else
+ p += len - 1;
+ int dohash = 0;
+ /* special computation, use only main domain name, not tld/host
+ going back from the end of string, start hashing at first
+ dot stop at next.
+ This is only compatible with 'Host' header, need a special
+ option to activate this
+ */
+ while (len) {
+ if (*p == '.') {
+ if (!dohash) {dohash = 1;}
+ else { break; }
+ } else {
+ if (dohash) {
+ DPRINTF (stderr, "%c",*p);
+ hash = *p + (hash << 6) + (hash << 16) - hash;
+ }
+ }
+ len--;
+ p--;
+ }
+#endif
+ DPRINTF (stderr, "\n");
+ return px->lbprm.map.srv[hash % px->lbprm.tot_weight];
+ }
+ return NULL;
+} + /* * This function applies the load-balancing algorithm to the session, as @@ -1400,6 +1488,19 @@ } } break;
+ case BE_LB_ALGO_HH:
+ /* Header Parameter hashing */
+ s->srv = get_server_hh(s);
+
+ if (!s->srv) {
+ /* parameter not found, fall back to round robin on the map */
+ s->srv = get_server_rr_with_conns(s->be, s->prev_srv);
+ if (!s->srv) {
+ err = SRV_STATUS_FULL;
+ goto out;
+ }
+ }
+ break;
default: /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; @@ -2077,6 +2178,18 @@ curproxy->url_param_post_limit = 3; /* minimum example: S=3 or \r\nS=6& */ } }
+ else if (!strcmp(args[0], "header")) {
+ if (!*args[1]) {
+ snprintf(err, errlen, "'balance header' requires an http header field name.");
+ return -1;
+ }
+ curproxy->lbprm.algo &= ~BE_LB_ALGO;
+ curproxy->lbprm.algo |= BE_LB_ALGO_HH;
+ if (curproxy->header_name)
+ free(curproxy->header_name);
+ curproxy->header_name = strdup(args[1]);
+ curproxy->header_len = strlen(args[1]);
+ }
else { snprintf(err, errlen, "'balance' only supports 'roundrobin', 'leastconn', 'source', 'uri' and 'url_param' options."); return -1;
This archive was generated by hypermail 2.2.0 : 2008/10/01 12:16 CEST