Jim Jagielski wrote:

If we state that the evaluation takes place in the occurence of stickysession attributes
and suggest

stickysession=Cookie:JSESSIONID stickysession=Path:;jsessionid to the user

it will perform faster in average.

As I promised to write the patch, I would do it the way Jim suggested it for backward compatibility, however I would also add a table "sticky_type" containing the "Cookie vs.
Path vs. Env"-prefix (in a parsed way) to the balancer struct.

Is this OK for everybody?


Patch for trunk...

Hi,

attached is the patch for trunk with documentation & Co.

Could anybody review it & commit?

Cheers,


Georg

Index: docs/manual/mod/mod_proxy.xml
===================================================================
--- docs/manual/mod/mod_proxy.xml       (revision 526077)
+++ docs/manual/mod/mod_proxy.xml       (working copy)
@@ -707,9 +707,19 @@
     </td></tr>
     <tr><td>stickysession</td>
         <td>-</td>
-        <td>Balancer sticky session name. The value is usually set to something
-        like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>,
-        and it depends on the backend application server that support sessions.
+        <td>Balancer sticky session name. Multiple definitions are possible and
+        are interpreted in the sequence of their definition. The
+        value can be preceeded with a specification <code>Cookie</code>,
+        <code>Path</code> or <code>Env</code> to restrict the search.
+        Without this specification, the identifier is searched in the URL path
+        and then in the cookie. The value is usually set to something
+        like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>, e.g.
+        <br/><indent>
+        <code>stickysession=Cookie:JSESSIONID
+              stickysession=Path:;jsessionid</code>
+        </indent>
+        for Java Servlets and depends on the backend application server
+        that support sessions.
     </td></tr>
     <tr><td>timeout</td>
         <td>0</td>
@@ -721,7 +731,7 @@
     <p>A sample balancer setup</p>
     <example>
       ProxyPass /special-area http://special.example.com/ smax=5 max=10<br />
-      ProxyPass / balancer://mycluster/ stickysession=jsessionid 
nofailover=On<br />
+      ProxyPass / balancer://mycluster/ stickysession=Cookie:JSESSIONID 
stickysession=Path:;jsessionid nofailover=On<br />
       &lt;Proxy balancer://mycluster&gt;<br />
       <indent>
         BalancerMember http://1.2.3.4:8009<br />
Index: docs/manual/mod/mod_proxy.html.en
===================================================================
--- docs/manual/mod/mod_proxy.html.en   (revision 526077)
+++ docs/manual/mod/mod_proxy.html.en   (working copy)
@@ -892,9 +892,19 @@
     </td></tr>
     <tr><td>stickysession</td>
         <td>-</td>
-        <td>Balancer sticky session name. The value is usually set to something
-        like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>,
-        and it depends on the backend application server that support sessions.
+        <td>Balancer sticky session name. Multiple definitions are possible and
+        are interpreted in the sequence of their definition. The
+        value can be preceeded with a specification <code>Cookie</code>,
+        <code>Path</code> or <code>Env</code> to restrict the search.
+        Without this specification, the identifier is searched in the URL path
+        and then in the cookie. The value is usually set to something
+        like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>, e.g.
+        <br /><span class="indent">
+        <code>stickysession=Cookie:JSESSIONID
+              stickysession=Path:;jsessionid</code>
+        </span>
+        for Java Servlets and depends on the backend application server
+        that support sessions.
     </td></tr>
     <tr><td>timeout</td>
         <td>0</td>
@@ -906,7 +916,7 @@
     <p>A sample balancer setup</p>
     <div class="example"><p><code>
       ProxyPass /special-area http://special.example.com/ smax=5 max=10<br />
-      ProxyPass / balancer://mycluster/ stickysession=jsessionid 
nofailover=On<br />
+      ProxyPass / balancer://mycluster/ stickysession=Cookie:JSESSIONID 
stickysession=Path:;jsessionid nofailover=On<br />
       &lt;Proxy balancer://mycluster&gt;<br />
       <span class="indent">
         BalancerMember http://1.2.3.4:8009<br />
Index: docs/manual/new_features_2_4.xml
===================================================================
--- docs/manual/new_features_2_4.xml    (revision 526077)
+++ docs/manual/new_features_2_4.xml    (working copy)
@@ -39,8 +39,12 @@
 
   <section id="module">
     <title>Module Enhancements</title>
-    <!-- <dl>
-    </dl> -->
+    <dl>
+      <dt><module>mod_proxy</module></dt>
+      <dd>Extension of the stickysession attribute in ProxyPass to allow
+          multiple definitions and to specify a prefix to the value like
+          'Path', 'Cookie' or 'Env' to limit the search.</dd>
+    </dl>
   </section>
 
   <section id="programs">
Index: docs/manual/new_features_2_4.html.en
===================================================================
--- docs/manual/new_features_2_4.html.en        (revision 526077)
+++ docs/manual/new_features_2_4.html.en        (working copy)
@@ -40,7 +40,12 @@
 <div class="section">
 <h2><a name="module" id="module">Module Enhancements</a></h2>
     
-    
+    <dl>
+      <dt><code class="module"><a 
href="./mod/mod_proxy.html">mod_proxy</a></code></dt>
+      <dd>Extension of the stickysession attribute in ProxyPass to allow
+          multiple definitions and to specify a prefix to the value like
+          'Path', 'Cookie' or 'Env' to limit the search.</dd>
+    </dl>
   </div><div class="top"><a href="#page-header"><img alt="top" 
src="./images/up.gif" /></a></div>
 <div class="section">
 <h2><a name="programs" id="programs">Program Enhancements</a></h2>
Index: CHANGES
===================================================================
--- CHANGES     (revision 526077)
+++ CHANGES     (working copy)
@@ -2,6 +2,10 @@
 Changes with Apache 2.3.0
   [Remove entries to the current 2.0 and 2.2 section below, when backported]
 
+  *) mod_proxy: Extension of the stickysession attribute in ProxyPass to allow
+     multiple definitions and to specify a prefix to the value like
+     'Path', 'Cookie' or 'Env' to limit the search. [Georg v. Zezschwitz]
+
   *) mod_ssl: initialize thread locks before initializing the hardware
      acceleration library, so the latter can make use of the former. 
      PR 20951. [adunn at ncipher.com, reviewed by Sander Temme]
Index: modules/proxy/mod_proxy_balancer.c
===================================================================
--- modules/proxy/mod_proxy_balancer.c  (revision 526077)
+++ modules/proxy/mod_proxy_balancer.c  (working copy)
@@ -241,19 +241,42 @@
 static proxy_worker *find_session_route(proxy_balancer *balancer,
                                         request_rec *r,
                                         char **route,
-                                        char **url)
+                                        char **url,
+                                        struct proxy_sticky_entry 
**sticky_entry)
 {
     proxy_worker *worker = NULL;
+    struct proxy_sticky_entry *entry;
+    int i;
 
     if (!balancer->sticky)
         return NULL;
-    /* Try to find the sticky route inside url */
-    *route = get_path_param(r->pool, *url, balancer->sticky);
-    if (!*route)
-        *route = get_cookie_param(r, balancer->sticky);
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+    
+    entry = (struct proxy_sticky_entry*) balancer->sticky->elts;
+    *route = NULL;
+    for (i = balancer->sticky->nelts; i>0; i--, entry++) {
+        if (entry->method & PROXY_STICKY_PATH) {
+            /* Try to find the sticky route inside url */
+            *route = get_path_param(r->pool, *url, entry->name);
+        }
+        if (!*route && (entry->method & PROXY_STICKY_COOKIE)) {
+            /* Try to find the sticky route by cookie */
+            *route = get_cookie_param(r, entry->name);
+        }
+        if (!*route && (entry->method & PROXY_STICKY_ENV)) {
+            /* Try to find the sticky route by environment */
+            const char *troute = apr_table_get(r->subprocess_env, entry->name);
+            if (troute)
+                *route = apr_pstrdup(r->pool, troute);
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                             "proxy: BALANCER: Found value %s for "
-                            "stickysession %s", *route, balancer->sticky);
+                            "stickysession %s", *route, entry->name);
+        if (*route) {
+            *sticky_entry = entry;
+            break;
+        }
+    }
+    
     /*
      * If we found a value for sticksession, find the first '.' within.
      * Everything after '.' (if present) is our route.
@@ -404,6 +427,7 @@
     proxy_worker *runtime;
     char *route = NULL;
     apr_status_t rv;
+    struct proxy_sticky_entry *sticky_entry = NULL;
 
     *worker = NULL;
     /* Step 1: check if the url is for us
@@ -429,7 +453,7 @@
     force_recovery(*balancer, r->server);
 
     /* Step 4: find the session route */
-    runtime = find_session_route(*balancer, r, &route, url);
+    runtime = find_session_route(*balancer, r, &route, url, &sticky_entry);
     if (runtime) {
         int i, total_factor = 0;
         proxy_worker *workers;
@@ -513,12 +537,12 @@
     access_status = rewrite_url(r, *worker, url);
     /* Add the session route to request notes if present */
     if (route) {
-        apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky);
+        apr_table_setn(r->notes, "session-sticky", sticky_entry->name);
         apr_table_setn(r->notes, "session-route", route);
 
         /* Add session info to env. */
         apr_table_setn(r->subprocess_env,
-                       "BALANCER_SESSION_STICKY", (*balancer)->sticky);
+                       "BALANCER_SESSION_STICKY", sticky_entry->name);
         apr_table_setn(r->subprocess_env,
                        "BALANCER_SESSION_ROUTE", route);
     }
@@ -581,6 +605,30 @@
     }
 }
 
+static void print_balancer_sticky(request_rec *r,
+                                  apr_array_header_t *sticky)
+{
+    int i;
+    struct proxy_sticky_entry *entries;
+
+    if (sticky==NULL)
+        return;
+    entries = (struct proxy_sticky_entry *) sticky->elts;
+    for (i = 0; i < sticky->nelts; i++) {
+         if (i>0)
+             ap_rputs (" ", r);
+         switch (entries[i].method) {
+             case PROXY_STICKY_COOKIE:
+                 ap_rputs ("Cookie:", r); break;
+             case PROXY_STICKY_PATH:
+                 ap_rputs ("Path:", r); break;
+             case PROXY_STICKY_ENV:
+                 ap_rputs ("Env:", r); break;
+         }
+         ap_rputs (entries[i].name, r);
+    }
+}
+
 /* Manages the loadfactors and member status
  */
 static int balancer_handler(request_rec *r)
@@ -645,10 +693,30 @@
     if (bsel) {
         const char *val;
         if ((val = apr_table_get(params, "ss"))) {
-            if (strlen(val))
-                bsel->sticky = apr_pstrdup(conf->pool, val);
-            else
-                bsel->sticky = NULL;
+            char *tok_cntx;
+            bsel->sticky = apr_array_make(conf->pool, 1,
+                                          sizeof (struct proxy_sticky_entry));
+            char *v = apr_strtok(apr_pstrdup(r->pool, val), "+", &tok_cntx);
+            while (v!=NULL) {
+                struct proxy_sticky_entry * entry =
+                   (struct proxy_sticky_entry *) apr_array_push(bsel->sticky);
+                const char *colon_pos = strchr (v, ':');
+                entry->method = PROXY_STICKY_COOKIE | PROXY_STICKY_PATH;
+                if (colon_pos!=NULL) {
+                    while (*++colon_pos==' ')
+                        continue;
+                    entry->name = apr_pstrdup(conf->pool, colon_pos);
+                    if (!strncasecmp ("cookie:", v, 7))
+                        entry->method = PROXY_STICKY_COOKIE;
+                    else if (!strncasecmp ("path:", v, 5))
+                        entry->method = PROXY_STICKY_PATH;
+                    else if (!strncasecmp ("env:", v, 4))
+                        entry->method = PROXY_STICKY_ENV;
+                } else {
+                    entry->name = apr_pstrdup(conf->pool, v);
+                }
+                v = apr_strtok(NULL, "+", &tok_cntx);
+            }
         }
         if ((val = apr_table_get(params, "tm"))) {
             int ival = atoi(val);
@@ -753,9 +821,9 @@
                       "\">", NULL);
             ap_rvputs(r, balancer->name, "</a></h3>\n\n", NULL);
             ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>"
-                
"<th>StickySession</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>"
-                "</tr>\n<tr>", r);
-            ap_rvputs(r, "<td>", balancer->sticky, NULL);
+                "<th>StickySession</th><th>Timeout</th><th>FailoverAttempts"
+                "</th><th>Method</th></tr>\n<tr><td>", r);
+            print_balancer_sticky (r, balancer->sticky);
             ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
                 apr_time_sec(balancer->timeout));
             ap_rprintf(r, "<td>%d</td>\n", balancer->max_attempts);
@@ -841,10 +909,10 @@
             ap_rvputs(r, bsel->name, "</h3>\n", NULL);
             ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
             ap_rvputs(r, r->uri, "\">\n<dl>", NULL);
-            ap_rputs("<table><tr><td>StickySession Identifier:</td><td><input 
name=\"ss\" type=text ", r);
-            if (bsel->sticky)
-                ap_rvputs(r, "value=\"", bsel->sticky, "\"", NULL);
-            ap_rputs("></td><tr>\n<tr><td>Timeout:</td><td><input name=\"tm\" 
type=text ", r);
+            ap_rputs("<table>", r);
+            ap_rputs("<tr><td>StickySession:</td><td><input name=\"ss\" 
type=text value=\"", r);
+            print_balancer_sticky (r, bsel->sticky);
+            ap_rputs("\"></td></tr><tr><td>Timeout:</td><td><input name=\"tm\" 
type=text ", r);
             ap_rprintf(r, "value=\"%" APR_TIME_T_FMT "\"></td></tr>\n",
                        apr_time_sec(bsel->timeout));
             ap_rputs("<tr><td>Failover Attempts:</td><td><input name=\"fa\" 
type=text ", r);
Index: modules/proxy/mod_proxy.c
===================================================================
--- modules/proxy/mod_proxy.c   (revision 526077)
+++ modules/proxy/mod_proxy.c   (working copy)
@@ -272,14 +272,36 @@
                                       const char *key,
                                       const char *val)
 {
-
     int ival;
     if (!strcasecmp(key, "stickysession")) {
-        /* Balancer sticky session name.
-         * Set to something like JSESSIONID or
-         * PHPSESSIONID, etc..,
-         */
-        balancer->sticky = apr_pstrdup(p, val);
+         /* Balancer sticky session name.
+          * Set to something like JSESSIONID or
+          * PHPSESSIONID, etc..,
+          */
+        struct proxy_sticky_entry *entry;
+
+        if (balancer->sticky==NULL)
+            balancer->sticky =
+                apr_array_make(p, 1, sizeof (struct proxy_sticky_entry));
+        entry = (struct proxy_sticky_entry *) apr_array_push(balancer->sticky);
+        const char *colon_pos = strchr (val, ':'); 
+        if (colon_pos!=NULL) {
+            while (*++colon_pos==' ')
+                continue;
+            entry->name = apr_pstrdup(p, colon_pos);
+            if (!strncasecmp ("cookie:", val, 7))
+                entry->method = PROXY_STICKY_COOKIE;
+            else if (!strncasecmp ("path:", val, 5))
+                entry->method = PROXY_STICKY_PATH;
+            else if (!strncasecmp ("env:", val, 4))
+                entry->method = PROXY_STICKY_ENV;
+            else
+                return "stickysession must be preceeded by Cookie|Path|Env "
+                        "followed by a colon";
+        } else {
+            entry->method = PROXY_STICKY_COOKIE | PROXY_STICKY_PATH;
+            entry->name = apr_pstrdup(p, val);
+        }
     }
     else if (!strcasecmp(key, "nofailover")) {
         /* If set to 'on' the session will break
@@ -324,7 +346,7 @@
         return "unknown lbmethod";
     }
     else {
-        return "unknown Balancer parameter";
+        return apr_pstrcat (p, "unknown Balancer parameter ", key);
     }
     return NULL;
 }
@@ -1121,9 +1143,8 @@
     char *f = cmd->path;
     char *r = NULL;
     char *word;
-    apr_table_t *params = apr_table_make(cmd->pool, 5);
-    const apr_array_header_t *arr;
-    const apr_table_entry_t *elts;
+    apr_array_header_t *params = apr_array_make(cmd->pool, 10, sizeof(char*));
+    const char **elts;
     int i;
 
     while (*arg) {
@@ -1152,7 +1173,8 @@
             }
             else
                 *val++ = '\0';
-            apr_table_setn(params, word, val);
+            * (const char**) (apr_array_push (params)) = word;
+            * (const char**) (apr_array_push (params)) = val;
         }
     };
 
@@ -1165,8 +1187,7 @@
     if (r[0] == '!' && r[1] == '\0')
         return NULL;
 
-    arr = apr_table_elts(params);
-    elts = (const apr_table_entry_t *)arr->elts;
+    elts = (const char **) params->elts;
     /* Distinguish the balancer from worker */
     if (strncasecmp(r, "balancer:", 9) == 0) {
         proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
@@ -1177,9 +1198,20 @@
             if (err)
                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
         }
-        for (i = 0; i < arr->nelts; i++) {
-            const char *err = set_balancer_param(conf, cmd->pool, balancer, 
elts[i].key,
-                                                 elts[i].val);
+        else {
+            if (params->nelts > 0 &&
+                  (balancer->sticky || balancer->sticky_force ||
+                   balancer->max_attempts_set || balancer->timeout)) {
+                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+                         "repeated parameter definition for balancer %s "
+                         "overwrites previous definitions",
+                         balancer->name);
+                balancer->sticky = NULL;
+            }
+        }
+        for (i = 0; i < params->nelts; i+=2) {
+            const char *err = set_balancer_param(conf, cmd->pool, balancer,
+                                                 elts[i], elts[i+1]);
             if (err)
                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
         }
@@ -1196,9 +1228,9 @@
         }
         PROXY_COPY_CONF_PARAMS(worker, conf);
 
-        for (i = 0; i < arr->nelts; i++) {
-            const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
-                                               elts[i].val);
+        for (i = 0; i < params->nelts; i+=2) {
+            const char *err = set_worker_param(cmd->pool, worker,
+                                               elts[i], elts[i+1]);
             if (err)
                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
         }
@@ -1975,7 +2007,7 @@
  */
 static int proxy_status_hook(request_rec *r, int flags)
 {
-    int i, n;
+    int i, j, n;
     void *sconf = r->server->module_config;
     proxy_server_conf *conf = (proxy_server_conf *)
         ap_get_module_config(sconf, &proxy_module);
@@ -1993,7 +2025,22 @@
         ap_rputs("\n\n<table border=\"0\"><tr>"
                  "<th>SSes</th><th>Timeout</th><th>Method</th>"
                  "</tr>\n<tr>", r);
-        ap_rvputs(r, "<td>", balancer->sticky, NULL);
+        ap_rputs("<td>", r);
+        if (balancer->sticky!=NULL) {
+            struct proxy_sticky_entry *sticky =
+                        (struct proxy_sticky_entry*) balancer->sticky->elts;
+            for (j = 0; j < balancer->sticky->nelts; j++) {
+                switch (sticky[j].method) {
+                case PROXY_STICKY_COOKIE:
+                    ap_rputs ("Cookie:", r); break;
+                case PROXY_STICKY_PATH:
+                    ap_rputs ("Path:", r); break;
+                case PROXY_STICKY_ENV:
+                    ap_rputs ("Env:", r); break;
+                }
+                ap_rvputs (r, sticky[j].name, " ", NULL);
+            }
+        }
         ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
                    apr_time_sec(balancer->timeout));
         ap_rprintf(r, "<td>%s</td>\n",
Index: modules/proxy/mod_proxy.h
===================================================================
--- modules/proxy/mod_proxy.h   (revision 526077)
+++ modules/proxy/mod_proxy.h   (working copy)
@@ -348,7 +348,7 @@
 struct proxy_balancer {
     apr_array_header_t *workers; /* array of proxy_workers */
     const char *name;            /* name of the load balancer */
-    const char *sticky;          /* sticky session identifier */
+    apr_array_header_t *sticky;  /* array of sticky session identifiers */
     int         sticky_force;    /* Disable failover for sticky sessions */
     apr_interval_time_t timeout; /* Timeout for waiting on free connection */
     int                 max_attempts; /* Number of attempts before failing */
@@ -373,6 +373,15 @@
     void            *context;   /* general purpose storage */
 };
 
+struct proxy_sticky_entry {
+    const char *name;            /* key to look for, e.g. JSESSIONID */
+    int        method;           /* where to search, e.g. PROXY_STICKY_ENV */
+};
+
+#define PROXY_STICKY_COOKIE  1   /* Search for stickiness by Cookie */
+#define PROXY_STICKY_PATH    2   /* Search for stickiness by URL / Path */
+#define PROXY_STICKY_ENV     4   /* Search for stickiness by Environment */
+
 #if APR_HAS_THREADS
 #define PROXY_THREAD_LOCK(x)      apr_thread_mutex_lock((x)->mutex)
 #define PROXY_THREAD_UNLOCK(x)    apr_thread_mutex_unlock((x)->mutex)

Reply via email to