Hello,
considering what we said in this thread, here is the complete patch.

To sum up :
- len : it's now the max number of characters for the value, preventing 
garbaged results.
- a new option "prefix" is added, this allows to use dynamic cookie names (e.g. 
ASPSESSIONIDXXX).
Previously in the thread, I wanted to use the value found with "capture cookie" 
but when i started to update the documentation, I found this solution quite 
weird.
I've made a small rework to not depend on "capture cookie".
-  There's the posssiblity to define the URL parser mode (path parameters or 
query string).

This patch is based on the haproxy-ss-20091129 snapshot.

Le mercredi 18 novembre 2009 06:40:52, Willy Tarreau a écrit :
> I think that if it causes some trouble, we can optionally limit the
> position of the argument in the query-string. But right now I don't
> think it is much of a problem, because :
>    1) we don't check the query string when we find the cookie in the
>       headers (at least I believe :-))

Well, after a last minute check, the URL parameters were parsed before the 
client cookies.
I've interverted the 2 steps so that URL parameters are only parsed if no 
client cookie is found.

Aleksandar, Willy, is this ok for you ?

-- 
Cyril Bonté
diff -Naur haproxy-ss-20091129/doc/configuration.txt haproxy-ss-20091129-appsession-options/doc/configuration.txt
--- haproxy-ss-20091129/doc/configuration.txt	2009-11-28 08:21:29.000000000 +0100
+++ haproxy-ss-20091129-appsession-options/doc/configuration.txt	2009-11-29 19:57:34.000000000 +0100
@@ -861,7 +861,8 @@
   See section 7 about ACL usage.
 
 
-appsession <cookie> len <length> timeout <holdtime> [request-learn]
+appsession <cookie> len <length> timeout <holdtime>
+           [request-learn] [prefix] [mode <path-parameters|query-string>]
   Define session stickiness on an existing application cookie.
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    no    |   yes  |   yes
@@ -869,7 +870,7 @@
     <cookie>   this is the name of the cookie used by the application and which
                HAProxy will have to learn for each new session.
 
-    <length>   this is the number of characters that will be memorized and
+    <length>   this is the max number of characters that will be memorized and
                checked in each cookie value.
 
     <holdtime> this is the time after which the cookie will be removed from
@@ -884,13 +885,34 @@
                the application's session and the correct server is selected.
                It is recommended to specify this option to improve reliability.
 
+    prefix     When this option is specified, haproxy will match on the cookie
+               prefix (or URL parameter prefix). The appsession value is the
+               data following this prefix.
+
+               Example :
+               appsession ASPSESSIONID len 64 timeout 3h prefix
+
+               This will match the cookie ASPSESSIONIDXXXX=XXXXX,
+               the appsession value will be XXXX=XXXXX.
+
+    mode       This option allows to change the URL parser mode.
+               2 modes are currently supported :
+               - path-parameters :
+                 The parser looks for the appsession in the path parameters
+                 part (each parameter is separated by a semi-colon), which is
+                 convenient for JSESSIONID for example.
+                 This is the default mode if the option is not set.
+               - query-string :
+                 In this mode, the parser will look for the appsession in the
+                 query string.
+ 
   When an application cookie is defined in a backend, HAProxy will check when
   the server sets such a cookie, and will store its value in a table, and
   associate it with the server's identifier. Up to <length> characters from
   the value will be retained. On each connection, haproxy will look for this
-  cookie both in the "Cookie:" headers, and as a URL parameter in the query
-  string. If a known value is found, the client will be directed to the server
-  associated with this value. Otherwise, the load balancing algorithm is
+  cookie both in the "Cookie:" headers, and as a URL parameter (depending on
+  the mode used). If a known value is found, the client will be directed to the
+  server associated with this value. Otherwise, the load balancing algorithm is
   applied. Cookies are automatically removed from memory when they have been
   unused for a duration longer than <holdtime>.
 
diff -Naur haproxy-ss-20091129/include/proto/proto_http.h haproxy-ss-20091129-appsession-options/include/proto/proto_http.h
--- haproxy-ss-20091129/include/proto/proto_http.h	2009-11-28 08:21:29.000000000 +0100
+++ haproxy-ss-20091129-appsession-options/include/proto/proto_http.h	2009-11-29 18:38:54.000000000 +0100
@@ -75,7 +75,7 @@
 int apply_filter_to_req_line(struct session *t, struct buffer *req, struct hdr_exp *exp);
 int apply_filters_to_request(struct session *t, struct buffer *req, struct hdr_exp *exp);
 int apply_filters_to_response(struct session *t, struct buffer *rtr, struct hdr_exp *exp);
-void manage_client_side_appsession(struct session *t, const char *buf);
+void manage_client_side_appsession(struct session *t, const char *buf, int len);
 void manage_client_side_cookies(struct session *t, struct buffer *req);
 void manage_server_side_cookies(struct session *t, struct buffer *rtr);
 void check_response_for_cacheability(struct session *t, struct buffer *rtr);
diff -Naur haproxy-ss-20091129/include/types/proxy.h haproxy-ss-20091129-appsession-options/include/types/proxy.h
--- haproxy-ss-20091129/include/types/proxy.h	2009-11-28 08:21:29.000000000 +0100
+++ haproxy-ss-20091129-appsession-options/include/types/proxy.h	2009-11-29 17:08:44.000000000 +0100
@@ -123,7 +123,13 @@
 #define PR_O2_LOGHCHKS	0x00000800	/* log health checks */
 #define PR_O2_INDEPSTR	0x00001000	/* independant streams, don't update rex on write */
 #define PR_O2_SOCKSTAT	0x00002000	/* collect & provide separate statistics for sockets */
-#define PR_O2_AS_REQL	0x00004000      /* appsession: learn the session id from the request */
+
+/* appsession */
+#define PR_O2_AS_REQL	0x00004000      /* learn the session id from the request */
+#define PR_O2_AS_PFX	0x00008000      /* match on the cookie prefix */
+#define PR_O2_AS_M_PP	0x00000000      /* path-parameters mode (the default mode) */
+#define PR_O2_AS_M_QS	0x00010000      /* query-string mode */
+#define PR_O2_AS_M_ANY	(PR_O2_AS_M_PP | PR_O2_AS_M_QS)
 
 struct error_snapshot {
 	struct timeval when;		/* date of this event, (tv_sec == 0) means "never" */
diff -Naur haproxy-ss-20091129/src/cfgparse.c haproxy-ss-20091129-appsession-options/src/cfgparse.c
--- haproxy-ss-20091129/src/cfgparse.c	2009-11-28 08:21:29.000000000 +0100
+++ haproxy-ss-20091129-appsession-options/src/cfgparse.c	2009-11-29 17:11:13.000000000 +0100
@@ -1541,7 +1541,7 @@
 			err_code |= ERR_WARN;
 
 		if (*(args[5]) == 0) {
-			Alert("parsing [%s:%d] : '%s' expects 'appsession' <cookie_name> 'len' <len> 'timeout' <timeout>.\n",
+			Alert("parsing [%s:%d] : '%s' expects 'appsession' <cookie_name> 'len' <len> 'timeout' <timeout> [options*].\n",
 			      file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
@@ -1568,9 +1568,34 @@
 
 		cur_arg = 6;
 		curproxy->options2 &= ~PR_O2_AS_REQL;
+		curproxy->options2 &= ~PR_O2_AS_M_ANY;
+		curproxy->options2 |= PR_O2_AS_M_PP;
 		while (*(args[cur_arg])) {
-			if (!strcmp(args[cur_arg], "request-learn"))
+			if (!strcmp(args[cur_arg], "request-learn")) {
 				curproxy->options2 |= PR_O2_AS_REQL;
+			} else if (!strcmp(args[cur_arg], "prefix")) {
+				curproxy->options2 |= PR_O2_AS_PFX;
+			} else if (!strcmp(args[cur_arg], "mode")) {
+				if (!*args[cur_arg + 1]) {
+					Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
+					      file, linenum, args[0], args[cur_arg]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+				
+				cur_arg++;
+				if (!strcmp(args[cur_arg], "query-string")) {
+					curproxy->options2 &= ~PR_O2_AS_M_ANY;
+					curproxy->options2 |= PR_O2_AS_M_QS;
+				} else if (!strcmp(args[cur_arg], "path-parameters")) {
+					curproxy->options2 &= ~PR_O2_AS_M_ANY;
+					curproxy->options2 |= PR_O2_AS_M_PP;
+				} else {
+					Alert("parsing [%s:%d] : unknown mode '%s'\n", file, linenum, args[cur_arg]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+			}
 			cur_arg++;
 		}
 	} /* Url App Session */
diff -Naur haproxy-ss-20091129/src/proto_http.c haproxy-ss-20091129-appsession-options/src/proto_http.c
--- haproxy-ss-20091129/src/proto_http.c	2009-11-28 08:21:29.000000000 +0100
+++ haproxy-ss-20091129-appsession-options/src/proto_http.c	2009-11-29 19:31:58.000000000 +0100
@@ -2473,17 +2473,7 @@
 	}
 
 	/*
-	 * 7: the appsession cookie was looked up very early in 1.2,
-	 * so let's do the same now.
-	 */
-
-	/* It needs to look into the URI */
-	if (s->be->appsession_name) {
-		get_srv_from_appsession(s, &req->data[msg->som], msg->sl.rq.l);
-	}
-
-	/*
-	 * 8: Now we can work with the cookies.
+	 * 7: Now we can work with the cookies.
 	 * Note that doing so might move headers in the request, but
 	 * the fields will stay coherent and the URI will not move.
 	 * This should only be performed in the backend.
@@ -2493,6 +2483,16 @@
 		manage_client_side_cookies(s, req);
 
 	/*
+	 * 8: the appsession cookie was looked up very early in 1.2,
+	 * so let's do the same now.
+	 */
+
+	/* It needs to look into the URI */
+	if ((s->sessid == NULL) && s->be->appsession_name) {
+		get_srv_from_appsession(s, &req->data[msg->som + msg->sl.rq.u], msg->sl.rq.u_l);
+	}
+
+	/*
 	 * 9: add X-Forwarded-For if either the frontend or the backend
 	 * asks for it.
 	 */
@@ -3778,11 +3778,15 @@
  * Try to retrieve the server associated to the appsession.
  * If the server is found, it's assigned to the session.
  */
-void manage_client_side_appsession(struct session *t, const char *buf) {
+void manage_client_side_appsession(struct session *t, const char *buf, int len) {
 	struct http_txn *txn = &t->txn;
 	appsess *asession = NULL;
 	char *sessid_temp = NULL;
 
+	if (len > t->be->appsession_len) {
+		len = t->be->appsession_len;
+	}
+
 	if (t->be->options2 & PR_O2_AS_REQL) {
 		/* request-learn option is enabled : store the sessid in the session for future use */
 		if (t->sessid != NULL) {
@@ -3796,8 +3800,8 @@
 			return;
 		}
 
-		memcpy(t->sessid, buf, t->be->appsession_len);
-		t->sessid[t->be->appsession_len] = 0;
+		memcpy(t->sessid, buf, len);
+		t->sessid[len] = 0;
 	}
 
 	if ((sessid_temp = pool_alloc2(apools.sessid)) == NULL) {
@@ -3806,8 +3810,8 @@
 		return;
 	}
 
-	memcpy(sessid_temp, buf, t->be->appsession_len);
-	sessid_temp[t->be->appsession_len] = 0;
+	memcpy(sessid_temp, buf, len);
+	sessid_temp[len] = 0;
 
 	asession = appsession_hash_lookup(&(t->be->htbl_proxy), sessid_temp);
 	/* free previously allocated memory */
@@ -4075,12 +4079,25 @@
 					}
 				}
 
-				if ((t->be->appsession_name != NULL) &&
-				    (memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
-					/* first, let's see if the cookie is our appcookie*/
+				if (t->be->appsession_name != NULL) {
+					int cmp_len, value_len;
+					char *value_begin;
+
+					if (t->be->options2 & PR_O2_AS_PFX) {
+						cmp_len = MIN(p4 - p1, t->be->appsession_name_len);
+						value_begin = p1 + t->be->appsession_name_len;
+						value_len = p4 - p1 - t->be->appsession_name_len;
+					} else {
+						cmp_len = p2 - p1;
+						value_begin = p3;
+						value_len = p4 - p3;
+					}
 
-					/* Cool... it's the right one */
-					manage_client_side_appsession(t, p3);
+					/* let's see if the cookie is our appcookie */
+					if (memcmp(p1, t->be->appsession_name, cmp_len) == 0) {
+						/* Cool... it's the right one */
+						manage_client_side_appsession(t, value_begin, value_len);
+					}
 #if defined(DEBUG_HASH)
 					Alert("manage_client_side_cookies\n");
 					appsession_hash_dump(&(t->be->htbl_proxy));
@@ -4493,24 +4510,37 @@
 				}
 			}
 			/* next, let's see if the cookie is our appcookie */
-			else if ((t->be->appsession_name != NULL) &&
-			         (memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
+			else if (t->be->appsession_name != NULL) {
+				int cmp_len, value_len;
+				char *value_begin;
+
+				if (t->be->options2 & PR_O2_AS_PFX) {
+					cmp_len = MIN(p4 - p1, t->be->appsession_name_len);
+					value_begin = p1 + t->be->appsession_name_len;
+					value_len = MIN(t->be->appsession_len, p4 - p1 - t->be->appsession_name_len);
+				} else {
+					cmp_len = p2 - p1;
+					value_begin = p3;
+					value_len = MIN(t->be->appsession_len, p4 - p3);
+				}
 
-				/* Cool... it's the right one */
+			    if (memcmp(p1, t->be->appsession_name, cmp_len) == 0) {
+					/* Cool... it's the right one */
 
-				if (t->sessid != NULL) {
-					/* free previously allocated memory as we don't need it anymore */
-					pool_free2(apools.sessid, t->sessid);
-				}
-				/* Store the sessid in the session for future use */
-				if ((t->sessid = pool_alloc2(apools.sessid)) == NULL) {
-					Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
-					send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
-					return;
+					if (t->sessid != NULL) {
+						/* free previously allocated memory as we don't need it anymore */
+						pool_free2(apools.sessid, t->sessid);
+					}
+					/* Store the sessid in the session for future use */
+					if ((t->sessid = pool_alloc2(apools.sessid)) == NULL) {
+						Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
+						send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
+						return;
+					}
+					memcpy(t->sessid, value_begin, value_len);
+					t->sessid[value_len] = 0;
 				}
-				memcpy(t->sessid, p3, t->be->appsession_len);
-				t->sessid[t->be->appsession_len] = 0;
-			}/* end if ((t->proxy->appsession_name != NULL) ... */
+			} /* end if ((t->be->appsession_name != NULL) ... */
 			break; /* we don't want to loop again since there cannot be another cookie on the same line */
 		} /* we're now at the end of the cookie value */
 		/* keep the link from this header to next one */
@@ -4653,25 +4683,66 @@
  */
 void get_srv_from_appsession(struct session *t, const char *begin, int len)
 {
-	char *request_line;
+	char *end_params, *first_param, *cur_param, *next_param;
+	char separator;
+	int value_len;
+
+	int mode = t->be->options2 & PR_O2_AS_M_ANY;
 
 	if (t->be->appsession_name == NULL ||
-	    (t->txn.meth != HTTP_METH_GET && t->txn.meth != HTTP_METH_POST) ||
-	    (request_line = memchr(begin, ';', len)) == NULL ||
-	    ((1 + t->be->appsession_name_len + 1 + t->be->appsession_len) > (begin + len - request_line)))
+	    (t->txn.meth != HTTP_METH_GET && t->txn.meth != HTTP_METH_POST)) {
 		return;
+	}
 
-	/* skip ';' */
-	request_line++;
+	first_param = NULL;
+	switch (mode) {
+	case PR_O2_AS_M_PP:
+		first_param = memchr(begin, ';', len);
+		break;
+	case PR_O2_AS_M_QS:
+		first_param = memchr(begin, '?', len);
+		break;
+	}
 
-	/* look if we have a jsessionid */
-	if (strncasecmp(request_line, t->be->appsession_name, t->be->appsession_name_len) != 0)
+	if (first_param == NULL) {
 		return;
+	}
 
-	/* skip jsessionid= */
-	request_line += t->be->appsession_name_len + 1;
+	switch (mode) {
+	case PR_O2_AS_M_PP:
+		if ((end_params = memchr(first_param, '?', len - (begin - first_param))) == NULL) {
+			end_params = (char *) begin + len;
+		}
+		separator = ';';
+		break;
+	case PR_O2_AS_M_QS:
+		end_params = (char *) begin + len;
+		separator = '&';
+		break;
+	default:
+		/* unknown mode, shouldn't happen */
+		return;
+	}
 	
-	manage_client_side_appsession(t, request_line);
+	cur_param = next_param = end_params;
+	while (cur_param > first_param) {
+		cur_param--;
+		if ((cur_param[0] == separator) || (cur_param == first_param)) {
+			/* let's see if this is the appsession parameter */
+			if ((cur_param + t->be->appsession_name_len + 1 < next_param) &&
+				((t->be->options2 & PR_O2_AS_PFX) || cur_param[t->be->appsession_name_len + 1] == '=') &&
+				(strncasecmp(cur_param + 1, t->be->appsession_name, t->be->appsession_name_len) == 0)) {
+				/* Cool... it's the right one */
+				cur_param += t->be->appsession_name_len + (t->be->options2 & PR_O2_AS_PFX ? 1 : 2);
+				value_len = MIN(t->be->appsession_len, next_param - cur_param);
+				if (value_len > 0) {
+					manage_client_side_appsession(t, cur_param, value_len);
+				}
+				break;
+			}
+			next_param = cur_param;
+		}
+	}
 #if defined(DEBUG_HASH)
 	Alert("get_srv_from_appsession\n");
 	appsession_hash_dump(&(t->be->htbl_proxy));

Reply via email to