appsession-like session-stickiness based on url_param

From: Benedikt Fraunhofer <fraunhof#traced.net>
Date: Tue, 8 Jul 2008 21:09:05 +0200


Hi List,

I tried to add an "TrustClientSessionInUrlParam" Option to haproxy.

The goal was to add something like appsession stickiness even if the server does not reply with an appropriate set-cookie header (or maybe better explained stickiness based simply on an url parameter). So haproxy should recognize the first request containing the JSESSIONID-like url parameter and store the selected server in its appsession hash for future requests containing the same sessionid. Simple url_param balancing did not work in our case, since the failing of a single server would mean that all sessions would be rebalanced as we can't use appsession-like-url-params or cookies in any way.

This is in no way a complete solution, better a question if this is the right direction to move on.

While haproxy allocates a slot in the persistence-table while reading request headers, it does not add an entry for the selected frontend if the server did not include a "Set-Cookie"-header before. One of our application needs session-stickiness but is unable to send additional headers.

what I did was the following (the full patch is attached)

  1. added
    --
    appses *hashptr
    --
    to the session-struct in types/session.h to have a place where one can put the server elected from the load balancing algorithm.
  2. added
    --
    if(1==1 && t->hashptr != NULL && t->hashptr->serverid == NULL) { t->hashptr->serverid = malloc(sizeof(char)*(strlen(t->srv->id) + 1)); memcpy(t->hashptr->serverid, t->srv->id, strlen(t->srv->id) + 1); tv_add(&t->hashptr->expire, &now, &t->be->timeout.appsession); }
    --

to manage_server_side_cookies in proto_http.c. Note that the 1==1 should be replaced by something to check if the "trustClientSession" option is set in the config.

3) changed get_srv_from_appsession in proto_http.c (as diff)
--

    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)

+       || ((1 + t->be->appsession_name_len + 1 +
t->be->appsession_len) > (begin + len - request_line))
+        || (
+              (request_line = memchr(begin, ';', len)) == NULL
+           && (request_line = memchr(begin, '?', len)) == NULL
+           && (request_line = memchr(begin, '&', len)) == NULL
+           )

+ )
+ {

        return;
+ }

+ if( (request_line = strcasestr(begin, t->be->appsession_name)) == NULL)

+    {
+        return;
+    }

    /* skip ';' */
- request_line++;

+    /* BFR: done above */
+   // request_line++;
+
+   /* look if we have a jsessionid */
+   /* BFR: we should be able to get rid of the following strncasecmp
+       since the  strcasestr above returns a pointer pointing to the
beginning
+       of the string in t->be->appsession_name in the current line.
+       if the return above did not return (:) a simple
+           request_line += t->be->appsession_name_len+2;
+       should do.

+ */

    if (strncasecmp(request_line, t->be->appsession_name, t->be->appsession_name_len) != 0

--

and then store the pointer to the appsess with
--

     t->hashptr = asession_temp;
--

after the appsession_hash_lookup call. This is imho needed, since there's no "context" where state could be held and the sessionid is gone when haproxy starts to parse server headers. Also note the lines looking for either ";", "?" or "&". The current appsess handling only notices ";" as this is what is used for JSESSION. Changing this to ";", "?" and "&" should make it work for url_param (TODO: look for ending '&', ' ') as well.

TODO: handle change of server when hashtable-entry points to "down"-server (quickhack: always (over)write server when reading server response? (=remove the "t->hashptr->serverid == NULL" condition above))

Are there any objections or opinions beside the performance impact? Memory leaks or other things i might have missed? I was a bit unsure about why stickiness is only available for JSESSION-like URL appendices and Cookies and only of the server always responds with the correct "set-cookie" header. This might open up some denial of service attack when someone can figure out the bernstein-like hash function and sends sessions that map to the same bucket/slot. But this is already the case as the slots get allocated as soon as an apssession-id is seen (without the overhead of mallocing space for the server-id-string).

Another question that came to my mind: Am i right in the assumption that hashtable-stickiness does not survive "haproxy -sf" since in the "-sf" case only the listener sockets are shut down, then the new instance takes those and starts with an empty hashtable?

Thx for any input or comment in advance

Yours

Beni.

Received on 2008/07/08 21:09

This archive was generated by hypermail 2.2.0 : 2008/07/08 21:15 CEST