Initial patch for "balance header" function

From: Benoit <maverick#maverick.eu.org>
Date: Wed, 01 Oct 2008 12:10:33 +0200

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)
+{

+ 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;
Received on 2008/10/01 12:10

This archive was generated by hypermail 2.2.0 : 2008/10/01 12:16 CEST