From: William A. Rowe Jr Date: Thu, 6 Sep 2001 16:48:15 +0000 (+0000) Subject: Optimize location_walk. We build an array of incremental matches, and X-Git-Tag: 2.0.26~308 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c1228bf9dbf5d8118dbdf61379a03f15f823881;p=apache Optimize location_walk. We build an array of incremental matches, and on attempting a subreq/redirect or simply a second pass, and for each match in series, if the section is a match, we reuse the merge result for that section. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@90925 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/server/request.c b/server/request.c index b022a77712..bbe115a68e 100644 --- a/server/request.c +++ b/server/request.c @@ -1082,21 +1082,57 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r) #endif /* defined REPLACE_PATH_INFO_METHOD */ +typedef struct walk_walked_t { + ap_conf_vector_t *matched; /* A dir_conf sections we matched */ + ap_conf_vector_t *merged; /* The dir_conf merged result */ +} walk_walked_t; + +typedef struct walk_cache_t { + const char *cached; /* The identifier we matched */ + ap_conf_vector_t **dir_conf_tested;/* The sections we matched against */ + ap_conf_vector_t *per_dir_result; /* per_dir_config += walked result */ + apr_array_header_t *walked; /* The list of walk_walked_t results */ +} walk_cache_t; AP_DECLARE(int) ap_location_walk(request_rec *r) { + ap_conf_vector_t *now_merged = NULL; core_server_config *sconf = ap_get_module_config(r->server->module_config, &core_module); - ap_conf_vector_t *per_uri_defaults = NULL; ap_conf_vector_t **locations = (ap_conf_vector_t **) sconf->sec_url->elts; - ap_conf_vector_t **loc_done = NULL; - int len, num_loc = sconf->sec_url->nelts; - char *test_location; - ap_conf_vector_t *this_conf; - ap_conf_vector_t *entry_config; + int num_loc = sconf->sec_url->nelts; core_dir_config *entry_core; - char *entry_uri; - int j; + walk_cache_t *cache; + walk_walked_t *last_walk; + const char *entry_uri; + int len, j; + + /* Find the most relevant, recent entry to work from. That would be + * this request (on the second call), or the parent request of a + * subrequest, or the prior request of an internal redirect. + */ + if ((apr_pool_userdata_get((void **)&cache, + "ap_location_walk::cache", r->pool) + != APR_SUCCESS) || !cache) + { + if ((r->main && (apr_pool_userdata_get((void **)&cache, + "ap_location_walk::cache", + r->main->pool) + == APR_SUCCESS) && cache) + || (r->prev && (apr_pool_userdata_get((void **)&cache, + "ap_location_walk::cache", + r->prev->pool) + == APR_SUCCESS) && cache)) { + cache = apr_pmemdup(r->pool, cache, sizeof(*cache)); + cache->walked = apr_array_copy(r->pool, cache->walked); + } + else { + cache = apr_pcalloc(r->pool, sizeof(*cache)); + cache->walked = apr_array_make(r->pool, 4, sizeof(walk_walked_t)); + } + apr_pool_userdata_set(cache, "ap_location_walk::cache", + apr_pool_cleanup_null, r->pool); + } /* If the initial request creation logic failed to reset the * per_dir_config, we will do so here. @@ -1105,58 +1141,65 @@ AP_DECLARE(int) ap_location_walk(request_rec *r) if (!r->per_dir_config) r->per_dir_config = r->server->lookup_defaults; - - /* No tricks here, there are no to parse in this vhost + /* No tricks here, there are no to parse in this vhost. + * We cache NULL because it's possible that another vhost had some + * different locations, and we are throwing those away. */ if (!num_loc) { - apr_pool_userdata_set(NULL, "ap_location_walk::dir_conf", - apr_pool_cleanup_null, r->pool); - apr_pool_userdata_set(NULL, "ap_location_walk::loc_done", - apr_pool_cleanup_null, r->pool); - apr_pool_userdata_set(NULL, "ap_location_walk::last_uri", - apr_pool_cleanup_null, r->pool); return OK; } - apr_pool_userdata_get((void **)&entry_uri, "ap_location_walk::last_uri", r->pool); - apr_pool_userdata_get((void **)&loc_done, "ap_location_walk::loc_done", r->pool); + /* Location and LocationMatch differ on their behaviour w.r.t. multiple + * slashes. Location matches multiple slashes with a single slash, + * LocationMatch doesn't. An exception, for backwards brokenness is + * absoluteURIs... in which case neither match multiple slashes. + */ + if (r->uri[0] != '/') { + entry_uri = r->uri; + } + else { + char *uri = apr_pstrdup(r->pool, r->uri); + ap_no2slash(uri); + entry_uri = uri; + } - /* If we have an ap_location_walk::last_uri that matches r->uri, - * and the vhost's list of locations hasn't changed, - * we will go through the location_walk entries. + /* If we have an cache->cached location that matches r->uri, + * and the vhost's list of locations hasn't changed, we can skip + * rewalking the location_walk entries. */ - if (!entry_uri || (loc_done != locations) - || (strcmp(r->uri, entry_uri) != 0)) - { - /* Location and LocationMatch differ on their behaviour w.r.t. multiple - * slashes. Location matches multiple slashes with a single slash, - * LocationMatch doesn't. An exception, for backwards brokenness is - * absoluteURIs... in which case neither match multiple slashes. + if (cache->cached && (cache->dir_conf_tested == locations) + && (strcmp(entry_uri, cache->cached) == 0)) { + /* Well this looks really familiar! If our end-result (per_dir_result) + * didn't change, we have absolutely nothing to do :) + * Otherwise (as is the case with most dir_merged/file_merged requests) + * we must merge our dir_conf_merged onto this new r->per_dir_config. */ - if (r->uri[0] != '/') { - test_location = r->uri; - } - else { - test_location = apr_pstrdup(r->pool, r->uri); - ap_no2slash(test_location); - } - - /* Go through the location entries, and check for matches. */ - - /* we apply the directive sections in some order; - * should really try them with the most general first. + if (cache->per_dir_result == r->per_dir_config) + return OK; + if (cache->walked->nelts) + now_merged = ((walk_walked_t*)cache->walked->elts) + [cache->walked->nelts - 1].merged; + } + else { + /* We start now_merged from NULL since we want to build + * a locations list that can be merged to any vhost. + */ + int matches = cache->walked->nelts; + last_walk = (walk_walked_t*)cache->walked->elts; + cache->cached = entry_uri; + cache->dir_conf_tested = locations; + + /* Go through the location entries, and check for matches. + * We apply the directive sections in given order, we should + * really try them with the most general first. */ for (j = 0; j < num_loc; ++j) { - entry_config = locations[j]; - - entry_core = ap_get_module_config(entry_config, &core_module); + entry_core = ap_get_module_config(locations[j], &core_module); entry_uri = entry_core->d; len = strlen(entry_uri); - this_conf = NULL; - /* Test the regex, fnmatch or string as appropriate. * If it's a strcmp, and the pattern was * not slash terminated, then this uri must be slash @@ -1165,60 +1208,57 @@ AP_DECLARE(int) ap_location_walk(request_rec *r) if (entry_core->r ? ap_regexec(entry_core->r, r->uri, 0, NULL, 0) : (entry_core->d_is_fnmatch - ? apr_fnmatch(entry_uri, test_location, FNM_PATHNAME) - : (strncmp(test_location, entry_uri, len) + ? apr_fnmatch(entry_uri, cache->cached, FNM_PATHNAME) + : (strncmp(cache->cached, entry_uri, len) || (entry_uri[len - 1] != '/' - && test_location[len] != '/' - && test_location[len] != '\0')))) { + && cache->cached[len] != '/' + && cache->cached[len] != '\0')))) { continue; } - if (per_uri_defaults) - per_uri_defaults = ap_merge_per_dir_configs(r->pool, - per_uri_defaults, - entry_config); - else - per_uri_defaults = entry_config; - } + /* If we merged this same section last time, reuse it + */ + if (matches) { + if (last_walk->matched == locations[j]) { + now_merged = last_walk->merged; + ++last_walk; + --matches; + continue; + } + /* We fell out of sync. This is our own copy of walked, + * so truncate the remaining matches and reset remaining. + */ + cache->walked->nelts -= matches; + matches = 0; + } - /* Set aside this walk result, in case we end up back here with - * the same uri again. - */ - apr_pool_userdata_set(per_uri_defaults, "ap_location_walk::dir_conf", - apr_pool_cleanup_null, r->pool); - apr_pool_userdata_set(locations, "ap_location_walk::loc_done", - apr_pool_cleanup_null, r->pool); - apr_pool_userdata_set(r->uri, "ap_location_walk::last_uri", - apr_pool_cleanup_null, r->pool); - } - else { - /* Well this looks familiar! If our end-result (dir_merged) hasn't - * changed, we have nothing to do :) This test really doesn't play well - * with other walkers who reset to the vhost default, but we will - * leave this escape in for simpler modules. - */ - apr_pool_userdata_get((void **)&per_uri_defaults, "ap_location_walk::dir_merged", - r->pool); - if (per_uri_defaults == r->per_dir_config) - return OK; + if (now_merged) + now_merged = ap_merge_per_dir_configs(r->pool, + now_merged, + locations[j]); + else + now_merged = locations[j]; - /* Well, we will need our per_uri_defaults from the last location walk. - * after all. + last_walk = (walk_walked_t*)apr_array_push(cache->walked); + last_walk->matched = locations[j]; + last_walk->merged = now_merged; + } + /* Whoops - everything matched in sequence, but the original walk + * found some additional matches. Truncate them. */ - apr_pool_userdata_get((void **)&per_uri_defaults, "ap_location_walk::dir_conf", - r->pool); + if (matches) + cache->walked->nelts -= matches; } - /* Merge our per_uri_defaults preconstruct onto the r->per_dir_configs, - * and note the end result for later optimization. + /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs, + * and note the end result to (potentially) skip this step next time. */ - if (per_uri_defaults) + if (now_merged) r->per_dir_config = ap_merge_per_dir_configs(r->pool, r->per_dir_config, - per_uri_defaults); + now_merged); + cache->per_dir_result = r->per_dir_config; - apr_pool_userdata_set(r->per_dir_config, "ap_location_walk::dir_merged", - apr_pool_cleanup_null, r->pool); return OK; }