commit 75a168cae5409a00340035160e5aa26f64a66def
Author: Mark Thomas <markt@apache.org>
Date:   Fri Aug 24 12:31:54 2018 +0000

    Refactor normalisation of request URIs to a common location and align the normalisation implementation for mod_jk with that implemented by Tomcat.
    
    git-svn-id: https://svn.apache.org/repos/asf/tomcat/jk/trunk@1838836 13f79535-47bb-0310-9956-ffa450edef68

Index: tomcat-connectors-1.2.43-src/native/apache-2.0/mod_jk.c
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/apache-2.0/mod_jk.c
+++ tomcat-connectors-1.2.43-src/native/apache-2.0/mod_jk.c
@@ -261,6 +261,7 @@ typedef struct
 struct jk_request_conf
 {
     rule_extension_t *rule_extensions;
+    char *orig_uri;
     int jk_handled;
 };
 
@@ -722,18 +723,6 @@ static void dump_options(server_rec *srv
         jk_log(conf->log, JK_LOG_DEBUG, "JkOption '%s' set in server '%s'%s",
                "RejectUnsafeURI", server_name,
                JK_OPT_DEFAULT & JK_OPT_REJECTUNSAFE ? " (default)" : "");
-    if (options & JK_OPT_COLLAPSEALL)
-        jk_log(conf->log, JK_LOG_DEBUG, "JkOption '%s' set in server '%s'%s",
-               "CollapseSlashesAll", server_name,
-               JK_OPT_DEFAULT & JK_OPT_COLLAPSEALL ? " (default)" : "");
-    if (options & JK_OPT_COLLAPSENONE)
-        jk_log(conf->log, JK_LOG_DEBUG, "JkOption '%s' set in server '%s'%s",
-               "CollapseSlashesNone", server_name,
-               JK_OPT_DEFAULT & JK_OPT_COLLAPSENONE ? " (default)" : "");
-    if (options & JK_OPT_COLLAPSEUNMOUNT)
-        jk_log(conf->log, JK_LOG_DEBUG, "JkOption '%s' set in server '%s'%s",
-               "CollapseSlashesUnmount", server_name,
-               JK_OPT_DEFAULT & JK_OPT_COLLAPSEUNMOUNT ? " (default)" : "");
 }
 
 /* ========================================================================= */
@@ -819,6 +808,7 @@ static int init_ws_service(apache_privat
     int size;
     request_rec *r = private_data->r;
     char *ssl_temp = NULL;
+    char *uri = NULL;
     const char *reply_timeout = NULL;
     const char *sticky_ignore = NULL;
     const char *stateless = NULL;
@@ -1009,6 +999,8 @@ static int init_ws_service(apache_privat
      * uri is use for compatibilty with mod_rewrite with old Tomcats
      */
 
+    uri = rconf->orig_uri ? rconf->orig_uri : r->uri;
+
     switch (conf->options & JK_OPT_FWDURIMASK) {
 
     case JK_OPT_FWDURICOMPATUNPARSED:
@@ -1023,17 +1015,17 @@ static int init_ws_service(apache_privat
         break;
 
     case JK_OPT_FWDURICOMPAT:
-        s->req_uri = r->uri;
+        s->req_uri = uri;
         break;
 
     case JK_OPT_FWDURIPROXY:
-        size = 3 * (int)strlen(r->uri) + 1;
+        size = 3 * (int)strlen(uri) + 1;
         s->req_uri = apr_palloc(r->pool, size);
-        jk_canonenc(r->uri, s->req_uri, size);
+        jk_canonenc(uri, s->req_uri, size);
         break;
 
     case JK_OPT_FWDURIESCAPED:
-        s->req_uri = ap_escape_uri(r->pool, r->uri);
+        s->req_uri = ap_escape_uri(r->pool, uri);
         break;
 
     default:
@@ -2316,11 +2308,10 @@ static const char *jk_set_options(cmd_pa
         mask = 0;
 
         if (action == '-' &&
-            (!strncasecmp(w, "ForwardURI", strlen("ForwardURI")) ||
-             !strncasecmp(w, "CollapseSlashes", strlen("CollapseSlashes"))))
+            (!strncasecmp(w, "ForwardURI", strlen("ForwardURI")))) {
             return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w,
                                "': option can not be disabled", NULL);
-
+        }
         if (!strcasecmp(w, "ForwardURICompat")) {
             opt = JK_OPT_FWDURICOMPAT;
             mask = JK_OPT_FWDURIMASK;
@@ -2751,6 +2742,15 @@ static int jk_handler(request_rec * r)
         return DECLINED;
     }
 
+    rconf = (jk_request_conf_t *)ap_get_module_config(r->request_config, &jk_module);
+    if (rconf == NULL) {
+        rconf = apr_palloc(r->pool, sizeof(jk_request_conf_t));
+        rconf->jk_handled = JK_FALSE;
+        rconf->rule_extensions = NULL;
+        rconf->orig_uri = NULL;
+        ap_set_module_config(r->request_config, &jk_module, rconf);
+    }
+
     worker_name = apr_table_get(r->notes, JK_NOTE_WORKER_NAME);
 
     if (worker_name == NULL) {
@@ -2774,8 +2774,6 @@ static int jk_handler(request_rec * r)
                 worker_name_extension = JK_TRUE;
                 parse_rule_extensions(w, e, xconf->log);
                 worker_name = w;
-                rconf = (jk_request_conf_t *)ap_get_module_config(r->request_config,
-                                                                  &jk_module);
                 rconf->rule_extensions = e;
             }
         }
@@ -2800,11 +2798,20 @@ static int jk_handler(request_rec * r)
             }
             else {
                 rule_extension_t *e;
-                worker_name = map_uri_to_worker_ext(xconf->uw_map, r->uri,
+                char *clean_uri;
+                clean_uri = apr_pstrdup(r->pool, r->uri);
+                rc = jk_servlet_normalize(clean_uri, xconf->log);
+                if (rc != 0) {
+                    return HTTP_BAD_REQUEST;
+                }
+
+                worker_name = map_uri_to_worker_ext(xconf->uw_map, clean_uri,
                                                     NULL, &e, NULL, xconf->log);
-                rconf = (jk_request_conf_t *)ap_get_module_config(r->request_config,
-                                                                  &jk_module);
-                rconf->rule_extensions = e;
+                if (worker_name) {
+                    rconf->rule_extensions = e;
+                    rconf->orig_uri = r->uri;
+                    r->uri = clean_uri;
+                }
             }
 
             if (worker_name == NULL && worker_env.num_of_workers) {
@@ -2824,8 +2831,6 @@ static int jk_handler(request_rec * r)
               " r->proxyreq=%d",
               r->handler, STRNULL_FOR_NULL(worker_name), r->proxyreq);
 
-    rconf = (jk_request_conf_t *)ap_get_module_config(r->request_config,
-                                                      &jk_module);
     rconf->jk_handled = JK_TRUE;
 
     /* If this is a proxy request, we'll notify an error */
@@ -3702,21 +3707,9 @@ static int jk_post_config(apr_pool_t * p
                             uri_worker_map_switch(sconf->uw_map, sconf->log);
                             uri_worker_map_load(sconf->uw_map, sconf->log);
                         }
-                        switch (sconf->options & JK_OPT_COLLAPSEMASK) {
-                        case JK_OPT_COLLAPSEALL:
-                            sconf->uw_map->collapse_slashes = JK_COLLAPSE_ALL;
-                            break;
-                        case JK_OPT_COLLAPSENONE:
-                            sconf->uw_map->collapse_slashes = JK_COLLAPSE_NONE;
-                            break;
-                        case JK_OPT_COLLAPSEUNMOUNT:
-                            sconf->uw_map->collapse_slashes = JK_COLLAPSE_UNMOUNT;
-                            break;
-                        default:
+                        if (sconf->options & JK_OPT_COLLAPSEMASK) {
                             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
-                                         "Collapse slashes value %d ignored, setting to %d",
-                                         sconf->options & JK_OPT_COLLAPSEMASK, JK_COLLAPSE_DEFAULT);
-                            sconf->uw_map->collapse_slashes = JK_COLLAPSE_DEFAULT;
+                                         "Deprecated CollapseSlashes setting ignored");
                         }
                     }
                     else {
@@ -3790,6 +3783,7 @@ static int jk_translate(request_rec * r)
     jk_request_conf_t *rconf = apr_palloc(r->pool, sizeof(jk_request_conf_t));
     rconf->jk_handled = JK_FALSE;
     rconf->rule_extensions = NULL;
+    rconf->orig_uri = NULL;
     ap_set_module_config(r->request_config, &jk_module, rconf);
 
     if (!r->proxyreq) {
@@ -3799,7 +3793,10 @@ static int jk_translate(request_rec * r)
                                                       &jk_module);
 
         if (conf) {
+            char *clean_uri;
+            int rc;
             const char *worker;
+
             if ((r->handler != NULL) && (!strcmp(r->handler, JK_HANDLER))) {
                 /* Somebody already set the handler, probably manual config
                  * or "native" configuration, no need for extra overhead
@@ -3819,6 +3816,12 @@ static int jk_translate(request_rec * r)
                 return DECLINED;
             }
 
+            clean_uri = apr_pstrdup(r->pool, r->uri);
+            rc = jk_servlet_normalize(clean_uri, conf->log);
+            if (rc != 0) {
+                return HTTP_BAD_REQUEST;
+            }
+
             /* Special case to make sure that apache can serve a directory
                listing if there are no matches for the DirectoryIndex and
                Tomcat webapps are mapped into apache using JkAutoAlias. */
@@ -3828,11 +3831,8 @@ static int jk_translate(request_rec * r)
 
                 /* Append the request uri to the JkAutoAlias directory and
                    determine if the file exists. */
-                char *clean_uri;
                 apr_finfo_t finfo;
                 finfo.filetype = APR_NOFILE;
-                clean_uri = apr_pstrdup(r->pool, r->uri);
-                ap_no2slash(clean_uri);
                 /* Map uri to a context static file */
                 if (strlen(clean_uri) > 1) {
                     char *context_path = NULL;
@@ -3864,10 +3864,13 @@ static int jk_translate(request_rec * r)
             }
             else {
                 rule_extension_t *e;
-                worker = map_uri_to_worker_ext(conf->uw_map, r->uri,
+                worker = map_uri_to_worker_ext(conf->uw_map, clean_uri,
                                                NULL, &e, NULL, conf->log);
-                rconf->rule_extensions = e;
-                ap_set_module_config(r->request_config, &jk_module, rconf);
+                if (worker) {
+                    rconf->rule_extensions = e;
+                    rconf->orig_uri = r->uri;
+                    r->uri = clean_uri;
+                }
             }
 
             if (worker) {
@@ -3884,8 +3887,6 @@ static int jk_translate(request_rec * r)
                 return OK;
             }
             else if (conf->alias_dir != NULL) {
-                char *clean_uri = apr_pstrdup(r->pool, r->uri);
-                ap_no2slash(clean_uri);
                 /* Automatically map uri to a context static file */
                 if (JK_IS_DEBUG_LEVEL(conf->log))
                     jk_log(conf->log, JK_LOG_DEBUG,
@@ -3993,6 +3994,7 @@ static int jk_map_to_storage(request_rec
         rconf = apr_palloc(r->pool, sizeof(jk_request_conf_t));
         rconf->jk_handled = JK_FALSE;
         rconf->rule_extensions = NULL;
+        rconf->orig_uri = NULL;
         ap_set_module_config(r->request_config, &jk_module, rconf);
     }
 
@@ -4003,7 +4005,10 @@ static int jk_map_to_storage(request_rec
                                                       &jk_module);
 
         if (conf) {
+            char *clean_uri;
+            int rc;
             const char *worker;
+
             if ((r->handler != NULL) && (!strcmp(r->handler, JK_HANDLER))) {
                 /* Somebody already set the handler, probably manual config
                  * or "native" configuration, no need for extra overhead
@@ -4022,6 +4027,13 @@ static int jk_map_to_storage(request_rec
 
                 return DECLINED;
             }
+
+            clean_uri = apr_pstrdup(r->pool, r->uri);
+            rc = jk_servlet_normalize(clean_uri, conf->log);
+            if (rc != 0) {
+                return HTTP_BAD_REQUEST;
+            }
+
             if (!conf->uw_map) {
                 if (JK_IS_DEBUG_LEVEL(conf->log))
                     jk_log(conf->log, JK_LOG_DEBUG,
@@ -4032,10 +4044,13 @@ static int jk_map_to_storage(request_rec
             }
             else {
                 rule_extension_t *e;
-                worker = map_uri_to_worker_ext(conf->uw_map, r->uri,
+                worker = map_uri_to_worker_ext(conf->uw_map, clean_uri,
                                                NULL, &e, NULL, conf->log);
-                rconf->rule_extensions = e;
-                ap_set_module_config(r->request_config, &jk_module, rconf);
+                if (worker) {
+                    rconf->rule_extensions = e;
+                    rconf->orig_uri = r->uri;
+                    r->uri = clean_uri;
+                }
             }
 
             if (worker) {
@@ -4052,23 +4067,12 @@ static int jk_map_to_storage(request_rec
                     jk_log(conf->log, JK_LOG_DEBUG,
                            "no match for %s found",
                            r->uri);
-                if (conf->strip_session == JK_TRUE &&
-                    conf->strip_session_name) {
-                    char *jsessionid;
+                if (conf->strip_session == JK_TRUE && conf->strip_session_name) {
                     if (r->uri) {
-                        jsessionid = strstr(r->uri, conf->strip_session_name);
-                        if (jsessionid) {
-                            if (JK_IS_DEBUG_LEVEL(conf->log))
-                                jk_log(conf->log, JK_LOG_DEBUG,
-                                       "removing session identifier [%s] for non servlet url [%s]",
-                                       jsessionid, r->uri);
-                            *jsessionid = '\0';
-                        }
+                        jk_strip_session_id(r->uri, conf->strip_session_name, conf->log);
                     }
                     if (r->filename) {
-                        jsessionid = strstr(r->filename, conf->strip_session_name);
-                        if (jsessionid)
-                            *jsessionid = '\0';
+                        jk_strip_session_id(r->filename, conf->strip_session_name, conf->log);
                     }
                     return DECLINED;
                 }
Index: tomcat-connectors-1.2.43-src/native/common/jk_util.c
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/common/jk_util.c
+++ tomcat-connectors-1.2.43-src/native/common/jk_util.c
@@ -1860,7 +1860,7 @@ static int jk_is_some_property(const cha
         size_t suffix_len = strlen(suffix);
         size_t sep_len = strlen(sep);
         size_t sep_off = sep_len + suffix_len;
-        
+
         if (prp_name_len >= sep_off) {
             if (!strncmp(prp_name + prp_name_len - sep_off, sep, sep_len) &&
                 !strncmp(prp_name + prp_name_len - suffix_len, suffix, suffix_len)) {
@@ -2193,23 +2193,147 @@ int jk_wildchar_match(const char *str, c
     return (str[x] != '\0');
 }
 
-void jk_no2slash(char *name)
+int jk_servlet_normalize(char *path, jk_logger_t *logger)
 {
-    char *d, *s;
+    int l, w;
+
+    if (JK_IS_DEBUG_LEVEL(logger)) {
+        jk_log(logger, JK_LOG_DEBUG, "URI on entering jk_servlet_normalize: [%s]", path);
+    }
+
+    // This test allows the loops below to start at index 1 rather than 0.
+    if (path[0] != '/') {
+        if (path[0] == '*' && path[1] == '\0') {
+            /* Most likely an "OPTIONS *" request */
+            return 0;
+        }
+        jk_log(logger, JK_LOG_WARNING, "Uri [%s] does not start with '/'.", path);
+        return JK_NORMALIZE_BAD_PATH;
+    }
+
+    /* First pass.
+     * Remove path parameters ;foo=bar/ from any path segment
+     */
+    for (l = 1, w = 1; path[l] != '\0';) {
+        if (path[l] == ';') {
+            l++;
+            while (path[l] != '/' && path[l] != '\0') {
+                l++;
+            }
+        }
+        else
+            path[w++] = path[l++];
+    }
+    path[w] = '\0';
 
-    s = d = name;
+    /*
+     * Second pass.
+     * Collapse ///// sequences to /
+     */
+    for (l = 1, w = 1; path[l] != '\0';) {
+        if (path[w - 1] == '/' && (path[l] == '/')) {
+            l++;
+        }
+        else
+            path[w++] = path[l++];
+    }
+    path[w] = '\0';
+
+    /* Third pass.
+     * Remove /./ segments
+     * Both leading and trailing segments will be removed.
+     */
+    for (l = 1, w = 1; path[l] != '\0';) {
+        if (path[l] == '.' &&
+                (path[l + 1] == '/' || path[l + 1] == '\0') &&
+                (l == 0 || path[l - 1] == '/')) {
+            l++;
+            if (path[l] == '/') {
+                l++;
+            }
+        }
+        else
+            path[w++] = path[l++];
+    }
+    path[w] = '\0';
 
-    while (*s) {
-        if ((*d++ = *s) == '/') {
+    /* Fourth pass.
+     * Remove /xx/../ segments
+     * Trailing segments will be removed but leading /../ segments are an error
+     * condition.
+     */
+    for (l = 1, w = 1; path[l] != '\0';) {
+        if (path[l] == '.' && path[l + 1] == '.' &&
+                (path[l + 2] == '/' || path[l + 2] == '\0') &&
+                (l == 0 || path[l - 1] == '/')) {
+
+            // Wind w back to remove the previous segment
+            if (w == 1) {
+                jk_log(logger,
+                        JK_LOG_EMERG,
+                        "[%s] contains a '/../' sequence that tries to escape above the root.",
+                        path);
+                return JK_NORMALIZE_TRAVERSAL;
+            }
             do {
-                ++s;
-            } while (*s == '/');
+                w--;
+            } while (w != 0 && path[w - 1] != '/');
+
+            // Move l forward to the next segment
+            l += 2;
+
+            if (path[l] == '/') {
+                l++;
+            }
         }
-        else {
-            ++s;
+        else
+            path[w++] = path[l++];
+    }
+    path[w] = '\0';
+
+    if (JK_IS_DEBUG_LEVEL(logger)) {
+        jk_log(logger, JK_LOG_DEBUG, "URI on exiting jk_servlet_normalize: [%s]", path);
+    }
+
+    return 0;
+}
+
+int jk_strip_session_id(char* path, char* session_name, jk_logger_t *logger) {
+
+    char *jsessionid;
+
+    jsessionid = strstr(path, session_name);
+    if (jsessionid) {
+        int i;
+        int j;
+        if (JK_IS_DEBUG_LEVEL(logger)) {
+            jk_log(logger, JK_LOG_DEBUG,
+                    "removing session identifier for non servlet uri [%s]", path);
+        }
+        // Found a session path parameter.
+        // Need to skip at least as many characters as there are in
+        // strip_session_name
+        i = (int) strlen(session_name);
+        j = 0;
+        // Increment i until the first character after the parameter
+        while (jsessionid[i] != '\0' && jsessionid[i] != ';' && jsessionid[i] != '/') {
+            i++;
+        }
+        // Copy until the end
+        while (jsessionid[i] != '\0') {
+            jsessionid[j++] = jsessionid[i++];
+        }
+        // Terminate
+        jsessionid[j] = '\0';
+
+        if (JK_IS_DEBUG_LEVEL(logger)) {
+            jk_log(logger, JK_LOG_DEBUG,
+                    "result of removing session identifier for non servlet uri is [%s]", path);
         }
+        return 1;
     }
-    *d = '\0';
+
+    return 0;
 }
 
 #ifdef _MT_CODE_PTHREAD
Index: tomcat-connectors-1.2.43-src/native/common/jk_util.h
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/common/jk_util.h
+++ tomcat-connectors-1.2.43-src/native/common/jk_util.h
@@ -60,7 +60,7 @@ int jk_log(jk_logger_t *l,
 
 int jk_check_attribute_length(const char *name, const char *value,
                               jk_logger_t *l);
-    
+
 const char *jk_get_worker_host(jk_map_t *m, const char *wname, const char *def);
 
 const char *jk_get_worker_source(jk_map_t *m, const char *wname, const char *def);
@@ -190,7 +190,7 @@ int jk_is_unique_property(const char *pr
 int jk_is_deprecated_property(const char *prp_name);
 
 int jk_check_buffer_size();
-    
+
 int jk_is_valid_property(const char *prp_name);
 
 int jk_get_worker_stdout(jk_map_t *m, const char *wname, const char **stdout_name);
@@ -248,7 +248,12 @@ int is_http_status_fail(unsigned int htt
 
 int jk_wildchar_match(const char *str, const char *exp, int icase);
 
-void jk_no2slash(char *name);
+int jk_servlet_normalize(char *path, jk_logger_t *logger);
+
+int jk_strip_session_id(char* path, char* session_name, jk_logger_t *logger);
+
+#define JK_NORMALIZE_BAD_PATH   -1
+#define JK_NORMALIZE_TRAVERSAL  -2
 
 #define TC32_BRIDGE_TYPE    32
 #define TC33_BRIDGE_TYPE    33
Index: tomcat-connectors-1.2.43-src/native/iis/jk_isapi_plugin.c
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/iis/jk_isapi_plugin.c
+++ tomcat-connectors-1.2.43-src/native/iis/jk_isapi_plugin.c
@@ -133,9 +133,6 @@ static char HTTP_WORKER_HEADER_INDEX[RES
 #define AUTH_COMPLETE_TAG             "auth_complete"
 #define REJECT_UNSAFE_TAG             "reject_unsafe"
 #define COLLAPSE_SLASHES_TAG          "collapse_slashes"
-#define COLLAPSE_SLASHES_ALL_VERB     "all"
-#define COLLAPSE_SLASHES_NONE_VERB    "none"
-#define COLLAPSE_SLASHES_UNMOUNT_VERB "unmount"
 #define WATCHDOG_INTERVAL_TAG         "watchdog_interval"
 #define ENABLE_CHUNKED_ENCODING_TAG   "enable_chunked_encoding"
 #define ERROR_PAGE_TAG                "error_page"
@@ -174,7 +171,6 @@ static char HTTP_WORKER_HEADER_INDEX[RES
 
 #define BAD_REQUEST         -1
 #define BAD_PATH            -2
-#define BAD_NORMALIZATION   -3
 #define MAX_SERVERNAME      1024
 #define MAX_INSTANCEID      32
 
@@ -511,7 +507,6 @@ static int  strip_session = 0;
 static int  use_auth_notification_flags = 1;
 static int  chunked_encoding_enabled = JK_FALSE;
 static int  reject_unsafe = 0;
-static int  collapse_slashes = JK_COLLAPSE_DEFAULT;
 static volatile int  watchdog_interval = 0;
 static HANDLE watchdog_handle = NULL;
 static char error_page_buf[INTERNET_MAX_URL_LENGTH] = {0};
@@ -660,90 +655,6 @@ static int unescape_url(char *url)
         return 0;
 }
 
-static int getparents(char *name)
-{
-    int l, w;
-
-    jk_log(logger, JK_LOG_DEBUG, "URI on entering getparents: [%s]", name);
-
-    // This test allows the loops below to start at index 1 rather than 0.
-    if (name[0] != '/') {
-    	return BAD_PATH;
-    }
-
-    /*
-     * First pass.
-     * Collapse ///// sequences to /
-     */
-    for (l = 1, w = 1; name[l] != '\0';) {
-        if (name[w - 1] == '/' && (name[l] == '/')) {
-        	l++;
-        }
-        else
-            name[w++] = name[l++];
-    }
-    name[w] = '\0';
-
-    /* Second pass.
-     * Remove /./ segments including those with path parameters such as
-     * /.;foo=bar/
-     * Both leading and trailing segments will be removed.
-     */
-    for (l = 1, w = 1; name[l] != '\0';) {
-        if (name[l] == '.' &&
-        		(name[l + 1] == '/' || name[l + 1] == ';' || name[l + 1] == '\0') &&
-				(l == 0 || name[l - 1] == '/')) {
-        	l++;
-        	while (name[l] != '/' && name[l] != '\0') {
-        		l++;
-        	}
-        	if (name[l] != '\0') {
-        		l++;
-        	}
-        }
-        else
-            name[w++] = name[l++];
-    }
-    name[w] = '\0';
-
-    /* Third pass.
-     * Remove /xx/../ segments including those with path parameters such as
-     * /xxx/..;foo=bar/
-     * Trailing segments will be removed but leading /../ segments are an error
-     * condition.
-     */
-    for (l = 1, w = 1; name[l] != '\0';) {
-        if (name[l] == '.' && name[l + 1] == '.' &&
-        		(name[l + 2] == '/' || name[l + 2] == ';' || name[l + 2] == '\0') &&
-				(l == 0 || name[l - 1] == '/')) {
-
-        	// Wind w back to remove the previous segment
-        	if (w == 1) {
-        		return BAD_NORMALIZATION;
-        	}
-        	do {
-        		w--;
-        	} while (w != 0 && name[w - 1] != '/');
-
-        	// Move l forward to the next segment
-        	l += 2;
-
-        	while (name[l] != '/' && name [l] != '\0') {
-        		l++;
-        	}
-        	if (name[l] != '\0') {
-        		l++;
-        	}
-        }
-        else
-            name[w++] = name[l++];
-    }
-    name[w] = '\0';
-
-    jk_log(logger, JK_LOG_DEBUG, "URI on exiting getparents: [%s]", name);
-    return 0;
-}
-
 /* Apache code to escape a URL */
 
 #define T_OS_ESCAPE_PATH    (4)
@@ -1821,7 +1732,8 @@ static DWORD handle_notify_event(PHTTP_F
     jk_pool_t pool;
 
     char *uri_undec  = NULL;
-    char *uri  = NULL;
+    char *cleanuri  = NULL;
+    char *uri = NULL;
     char *host = NULL;
     char *translate = NULL;
     char szHB[HDR_BUFFER_SIZE] = "/";
@@ -1869,10 +1781,10 @@ static DWORD handle_notify_event(PHTTP_F
         else
             query = NULL;
     }
-    if (uri_select_option == URI_SELECT_OPT_UNPARSED) {
-        /* Duplicate unparsed uri */
-        uri_undec = jk_pool_strdup(&pool, uri);
-    }
+
+    /* Duplicate unparsed uri */
+	uri_undec = jk_pool_strdup(&pool, uri);
+
     rc = unescape_url(uri);
     if (rc == BAD_REQUEST) {
         jk_log(logger, JK_LOG_ERROR,
@@ -1890,19 +1802,8 @@ static DWORD handle_notify_event(PHTTP_F
         rv = SF_STATUS_REQ_FINISHED;
         goto cleanup;
     }
-    rc = getparents(uri);
-    if (rc == BAD_PATH) {
-        jk_log(logger, JK_LOG_EMERG,
-               "[%s] does not start with '/'.",
-               uri);
-        write_error_response(pfc, 404);
-        rv = SF_STATUS_REQ_FINISHED;
-        goto cleanup;
-    }
-    if (rc == BAD_NORMALIZATION) {
-        jk_log(logger, JK_LOG_EMERG,
-               "[%s] contains a '/../' sequence that tries to escape above the root.",
-               uri);
+    cleanuri = jk_pool_strdup(&pool, uri);
+    if (jk_servlet_normalize(cleanuri, logger)) {
         write_error_response(pfc, 404);
         rv = SF_STATUS_REQ_FINISHED;
         goto cleanup;
@@ -1918,7 +1819,7 @@ static DWORD handle_notify_event(PHTTP_F
         else
             host = szHB;
     }
-    worker = map_uri_to_worker_ext(uw_map, uri, host,
+    worker = map_uri_to_worker_ext(uw_map, cleanuri, host,
                                    &extensions, &worker_index, logger);
     /*
      * Check if somebody is feading us with his own TOMCAT data headers.
@@ -2074,18 +1975,11 @@ static DWORD handle_notify_event(PHTTP_F
     }
     else {
         if (JK_IS_DEBUG_LEVEL(logger))
-            jk_log(logger, JK_LOG_DEBUG,
-                   "[%s] is not a servlet url", uri);
+            jk_log(logger, JK_LOG_DEBUG, "[%s] is not a servlet url", uri_undec);
         if (strip_session) {
-            char *jsessionid = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
-            if (jsessionid) {
-                if (JK_IS_DEBUG_LEVEL(logger))
-                    jk_log(logger, JK_LOG_DEBUG,
-                           "removing session identifier [%s] for non servlet url [%s]",
-                           jsessionid, uri);
-                *jsessionid = '\0';
-                pfp->SetHeader(pfc, "url", uri);
-            }
+        	if (jk_strip_session_id(uri_undec, JK_PATH_SESSION_IDENTIFIER, logger)) {
+        		pfp->SetHeader(pfc, "url", uri_undec);
+        	}
         }
     }
 cleanup:
@@ -2229,7 +2123,7 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSI
         lpEcb->ServerSupportFunction(lpEcb->ConnID, HSE_REQ_SET_FLUSH_FLAG,
                 (LPVOID) TRUE, NULL, NULL);
     }
-    
+
     if (init_ws_service(&private_data, &s, &worker_name)) {
         jk_endpoint_t *e = NULL;
         jk_worker_t *worker = wc_get_worker_for_name(worker_name, logger);
@@ -2743,7 +2637,6 @@ static int init_jk(char *serverName)
             uw_map->reject_unsafe = 1;
         else
             uw_map->reject_unsafe = 0;
-        uw_map->collapse_slashes = collapse_slashes;
         uw_map->reload = worker_mount_reload;
         if (worker_mount_file[0]) {
             uw_map->fname = worker_mount_file;
@@ -2873,17 +2766,6 @@ int parse_uri_select(const char *uri_sel
     return -1;
 }
 
-int parse_collapse_slashes(const char *collapse_slashes)
-{
-    if (!strcasecmp(collapse_slashes, COLLAPSE_SLASHES_ALL_VERB))
-        return JK_COLLAPSE_ALL;
-    if (!strcasecmp(collapse_slashes, COLLAPSE_SLASHES_NONE_VERB))
-        return JK_COLLAPSE_NONE;
-    if (!strcasecmp(collapse_slashes, COLLAPSE_SLASHES_UNMOUNT_VERB))
-        return JK_COLLAPSE_UNMOUNT;
-    return -1;
-}
-
 static int read_registry_init_data(void)
 {
     char tmpbuf[MAX_PATH];
@@ -2993,14 +2875,8 @@ static int read_registry_init_data(void)
         }
     }
     if (get_config_parameter(src, COLLAPSE_SLASHES_TAG, tmpbuf, sizeof(tmpbuf))) {
-        int opt = parse_collapse_slashes(tmpbuf);
-        if (opt >= 0) {
-            collapse_slashes = opt;
-        }
-        else {
-            jk_log(logger, JK_LOG_ERROR, "Invalid value '%s' for configuration item '"
-                   COLLAPSE_SLASHES_TAG "'", tmpbuf);
-        }
+        jk_log(logger, JK_LOG_ERROR, "Configuration item '" COLLAPSE_SLASHES_TAG
+                "' is deprecated and will be ignored");
     }
     shm_config_size = get_config_int(src, SHM_SIZE_TAG, -1);
     worker_mount_reload = get_config_int(src, WORKER_MOUNT_RELOAD_TAG, JK_URIMAP_DEF_RELOAD);
@@ -3144,7 +3020,7 @@ static int init_ws_service(isapi_private
             JK_TRACE_EXIT(logger);
             return JK_FALSE;
         }
-        getparents(s->req_uri);
+        jk_servlet_normalize(s->req_uri, logger);
     } else {
         GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string, "");
         GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name), "");
Index: tomcat-connectors-1.2.43-src/xdocs/miscellaneous/changelog.xml
===================================================================
--- tomcat-connectors-1.2.43-src.orig/xdocs/miscellaneous/changelog.xml
+++ tomcat-connectors-1.2.43-src/xdocs/miscellaneous/changelog.xml
@@ -427,6 +427,26 @@
       <update>
         Minor documentation improvements. (rjung)
       </update>
+      <scode>
+        Common: Optimize path parameter handling. (rjung)
+      </scode>
+      <fix>
+        Improve path parameter parsing so that the session ID specified by the
+        <code>session_path</code> worker property for load-balanced workers can
+        be extracted from a path parameter in any segment of the URI, rather
+        than only from the final segment. (markt)
+      </fix>
+      <fix>
+        Apache: Improve path parameter handling so that
+        <code>JkStripSession</code> can remove session IDs that are specified on
+        path parameters in any segment of the URI rather than only the final
+        segment. (markt)
+      </fix>
+      <fix>
+        IIS: Improve path parameter handling so that <code>strip_session</code>
+        can remove session IDs that are specified on path parameters in any
+        segment of the URI rather than only the final segment. (markt)
+      </fix>
     </changelog>
   </subsection>
 </section>
Index: tomcat-connectors-1.2.43-src/native/common/jk_uri_worker_map.c
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/common/jk_uri_worker_map.c
+++ tomcat-connectors-1.2.43-src/native/common/jk_uri_worker_map.c
@@ -176,10 +176,9 @@ static void uri_worker_map_dump(jk_uri_w
         int i, off;
         if (JK_IS_DEBUG_LEVEL(l)) {
             jk_log(l, JK_LOG_DEBUG, "uri map dump %s: id=%d, index=%d file='%s' reject_unsafe=%d "
-                  "collapse_slashes=%d reload=%d modified=%d checked=%d",
+                  "reload=%d modified=%d checked=%d",
                    reason, uw_map->id, uw_map->index, STRNULL_FOR_NULL(uw_map->fname),
-                   uw_map->reject_unsafe, uw_map->collapse_slashes,
-                   uw_map->reload, uw_map->modified, uw_map->checked);
+                   uw_map->reject_unsafe, uw_map->reload, uw_map->modified, uw_map->checked);
         }
         for (i = 0; i <= 1; i++) {
             jk_log(l, JK_LOG_DEBUG, "generation %d: size=%d nosize=%d capacity=%d",
@@ -245,7 +244,6 @@ int uri_worker_map_alloc(jk_uri_worker_m
         uw_map->index = 0;
         uw_map->fname = NULL;
         uw_map->reject_unsafe = 0;
-        uw_map->collapse_slashes = JK_COLLAPSE_DEFAULT;
         uw_map->reload = JK_URIMAP_DEF_RELOAD;
         uw_map->modified = 0;
         uw_map->checked = 0;
@@ -1074,7 +1072,6 @@ const char *map_uri_to_worker_ext(jk_uri
     unsigned int i;
     unsigned int vhost_len;
     int reject_unsafe;
-    int collapse_slashes;
     size_t uri_len;
     size_t remain;
     int rv = -1;
@@ -1115,7 +1112,6 @@ const char *map_uri_to_worker_ext(jk_uri
         }
     }
     reject_unsafe = uw_map->reject_unsafe;
-    collapse_slashes = uw_map->collapse_slashes;
     vhost_len = 0;
     /*
      * In case we got a vhost, we prepend a slash
@@ -1143,8 +1139,8 @@ const char *map_uri_to_worker_ext(jk_uri
         }
         vhost_len += off;
     }
-    /* Make the copy of the provided uri and strip
-     * everything after the first ';' char.
+    /* Make the copy of the provided uri, check length
+     * and look for potentially unsafe constructs
      */
     uri_len = strlen(uri);
     remain = JK_MAX_URI_LEN - vhost_len;
@@ -1156,15 +1152,11 @@ const char *map_uri_to_worker_ext(jk_uri
             JK_TRACE_EXIT(l);
             return NULL;
         }
-        if (uri[i] == ';')
-            break;
-        else {
-            url[i + vhost_len] = uri[i];
-            if (reject_unsafe && (uri[i] == '%' || uri[i] == '\\')) {
-                jk_log(l, JK_LOG_INFO, "Potentially unsafe request url '%s' rejected", uri);
-                JK_TRACE_EXIT(l);
-                return NULL;
-            }
+        url[i + vhost_len] = uri[i];
+        if (reject_unsafe && (uri[i] == '%' || uri[i] == '\\')) {
+            jk_log(l, JK_LOG_INFO, "Potentially unsafe request url '%s' rejected", uri);
+            JK_TRACE_EXIT(l);
+            return NULL;
         }
     }
     url[i + vhost_len] = '\0';
@@ -1175,12 +1167,6 @@ const char *map_uri_to_worker_ext(jk_uri
             jk_log(l, JK_LOG_DEBUG, "Found session identifier '%s' in url '%s'",
                    url_rewrite, uri);
     }
-    if (collapse_slashes == JK_COLLAPSE_ALL) {
-        /* Remove multiple slashes
-         * No need to copy url, because it is local and
-         * the unchanged url is no longer needed */
-        jk_no2slash(url);
-    }
     if (JK_IS_DEBUG_LEVEL(l))
         jk_log(l, JK_LOG_DEBUG, "Attempting to map URI '%s' from %d maps",
                url, IND_THIS(uw_map->size));
@@ -1193,13 +1179,6 @@ const char *map_uri_to_worker_ext(jk_uri
     /* In case we found a match, check for the unmounts. */
     if (rv >= 0 && IND_THIS(uw_map->nosize)) {
         int rc;
-        if (collapse_slashes == JK_COLLAPSE_UNMOUNT) {
-            /* Remove multiple slashes when looking for
-             * unmount to prevent trivial unmount bypass attack.
-             * No need to copy url, because it is local and
-             * the unchanged url is no longer needed */
-            jk_no2slash(url);
-        }
         /* Again first including vhost. */
         rc = is_nomatch(uw_map, url, rv, l);
         /* If no unmount was found, try without vhost. */
Index: tomcat-connectors-1.2.43-src/native/common/jk_global.h
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/common/jk_global.h
+++ tomcat-connectors-1.2.43-src/native/common/jk_global.h
@@ -292,7 +292,7 @@ extern "C"
 #define JK_OPT_COLLAPSENONE         0x2000
 #define JK_OPT_COLLAPSEUNMOUNT      0x4000
 
-#define JK_OPT_DEFAULT              (JK_OPT_FWDURIDEFAULT | JK_OPT_FWDKEYSIZE | JK_OPT_COLLAPSEUNMOUNT)
+#define JK_OPT_DEFAULT              (JK_OPT_FWDURIDEFAULT | JK_OPT_FWDKEYSIZE)
 
 /* Check for EBCDIC systems */
 
Index: tomcat-connectors-1.2.43-src/native/common/jk_uri_worker_map.h
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/common/jk_uri_worker_map.h
+++ tomcat-connectors-1.2.43-src/native/common/jk_uri_worker_map.h
@@ -58,11 +58,6 @@ extern "C"
 #define MATCH_TYPE_STOPPED          0x4000
  */
 
-#define JK_COLLAPSE_ALL             0x0001
-#define JK_COLLAPSE_NONE            0x0002
-#define JK_COLLAPSE_UNMOUNT         0x0003
-#define JK_COLLAPSE_DEFAULT         JK_COLLAPSE_UNMOUNT
-
 #define SOURCE_TYPE_WORKERDEF       0x0001
 #define SOURCE_TYPE_JKMOUNT         0x0002
 #define SOURCE_TYPE_URIMAP          0x0003
@@ -172,9 +167,7 @@ struct jk_uri_worker_map
 
     JK_CRIT_SEC cs;
     /* should we forward potentially unsafe URLs */
-    int reject_unsafe;    
-    /* how to handle multiple adjacent slashes in URLs */
-    int collapse_slashes;    
+    int reject_unsafe;
     /* uriworkermap filename */
     const char *fname;    
     /* uriworkermap reload check interval */
Index: tomcat-connectors-1.2.43-src/native/common/jk_lb_worker.c
===================================================================
--- tomcat-connectors-1.2.43-src.orig/native/common/jk_lb_worker.c
+++ tomcat-connectors-1.2.43-src/native/common/jk_lb_worker.c
@@ -431,8 +431,7 @@ void jk_lb_push(lb_worker_t *p, int lock
 static char *get_path_param(jk_ws_service_t *s, const char *name)
 {
     char *id_start = NULL;
-    for (id_start = strstr(s->req_uri, name);
-         id_start; id_start = strstr(id_start + 1, name)) {
+    for (id_start = strstr(s->req_uri, name); id_start; id_start = strstr(id_start + 1, name)) {
         if (id_start[strlen(name)] == '=') {
             /*
              * Session path-cookie was found, get it's value
@@ -455,6 +454,12 @@ static char *get_path_param(jk_ws_servic
                 if ((id_end = strchr(id_start, ';')) != NULL) {
                     *id_end = '\0';
                 }
+                /*
+                 * Remove any trailing URI segments.
+                 */
+                if ((id_end = strchr(id_start, '/')) != NULL) {
+                    *id_end = '\0';
+                }
                 return id_start;
             }
         }
@@ -1225,9 +1230,11 @@ static int JK_METHOD service(jk_endpoint
     if (p->worker->sequence < p->worker->s->h.sequence)
         jk_lb_pull(p->worker, JK_FALSE, l);
     for (i = 0; i < num_of_workers; i++) {
+        lb_sub_worker_t *rec;
+        ajp_worker_t *aw;
         jk_log(l, JK_LOG_DEBUG, "LB - num_of_workers: %d, retry: %d, lb_retries: %d", num_of_workers, i, p->worker->lb_retries);
-        lb_sub_worker_t *rec = &(p->worker->lb_workers[i]);
-        ajp_worker_t *aw = (ajp_worker_t *)rec->worker->worker_private;
+        rec = &(p->worker->lb_workers[i]);
+        aw = (ajp_worker_t *)rec->worker->worker_private;
         if (rec->s->state == JK_LB_STATE_BUSY) {
             if ((aw->busy_limit <= 0 || aw->s->busy < aw->busy_limit) &&
                 ajp_has_endpoint(rec->worker, l)) {
@@ -1764,7 +1771,7 @@ static int JK_METHOD validate(jk_worker_
                         jk_log(l, JK_LOG_DEBUG,
                                "Balanced worker %s already configured (sequence=%d)",
                                p->lb_workers[i].name, p->lb_workers[i].s->h.sequence);
-                    }           
+                    }
                     if (!wc_create_worker(p->lb_workers[i].name, 0,
                                           props,
                                           &(p->lb_workers[i].worker),
