cache_util.c revision 3cc1b27028708643374be2b8de220ce509429594
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor/* Licensed to the Apache Software Foundation (ASF) under one or more
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * contributor license agreements. See the NOTICE file distributed with
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * this work for additional information regarding copyright ownership.
0a05fab9aadd37834734ffe106fc8ad4488fb3e3rbowen * The ASF licenses this file to You under the Apache License, Version 2.0
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * (the "License"); you may not use this file except in compliance with
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * the License. You may obtain a copy of the License at
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * Unless required by applicable law or agreed to in writing, software
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * distributed under the License is distributed on an "AS IS" BASIS,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * See the License for the specific language governing permissions and
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * limitations under the License.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor/* -------------------------------------------------------------- */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzorextern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor/* Determine if "url" matches the hostname, scheme and port and path
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * in "filter". All but the path comparisons are case-insensitive.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzorstatic int uri_meets_conditions(const apr_uri_t *filter, const int pathlen,
1b1b6ae3d9cf8a22cd74249fe56d4fab443f9e21lgentis /* Scheme, hostname port and local part. The filter URI and the
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * URI we test may have the following shapes:
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * <scheme>[:://<hostname>[:<port>][/<path>]]
1b1b6ae3d9cf8a22cd74249fe56d4fab443f9e21lgentis * That is, if there is no scheme then there must be only the path,
1b1b6ae3d9cf8a22cd74249fe56d4fab443f9e21lgentis * and we check only the path; if there is a scheme, we check the
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * scheme for equality, and then if present we match the hostname,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * and then if present match the port, and finally the path if any.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * Note that this means that "/<path>" only matches local paths,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * and to match proxied paths one *must* specify the scheme.
d89089206f40f9a6d58528ff85050447d4a52d53lgentis /* Is the filter is just for a local path or a proxy URI? */
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis /* The URI scheme must be present and identical except for case. */
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis if (!url->scheme || strcasecmp(filter->scheme, url->scheme)) {
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis /* If the filter hostname is null or empty it matches any hostname,
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis * if it begins with a "*" it matches the _end_ of the URI hostname
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis * excluding the "*", if it begins with a "." it matches the _end_
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis * of the URI * hostname including the ".", otherwise it must match
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis * the URI hostname exactly. */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor const size_t uhostlen = url->hostname ? strlen(url->hostname) : 0;
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor const size_t fhostlen = strlen(filter->hostname + 1);
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor const size_t uhostlen = url->hostname ? strlen(url->hostname) : 0;
d89089206f40f9a6d58528ff85050447d4a52d53lgentis else if (!url->hostname || strcasecmp(filter->hostname, url->hostname)) {
d89089206f40f9a6d58528ff85050447d4a52d53lgentis /* If the filter port is empty it matches any URL port.
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * If the filter or URL port are missing, or the URL port is
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * empty, they default to the port for their scheme. */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* NOTE: ap_port_of_scheme will return 0 if given NULL input */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor const unsigned fport = filter->port_str ? filter->port
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor const unsigned uport = (url->port_str && url->port_str[0])
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* For HTTP caching purposes, an empty (NULL) path is equivalent to
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * a single "/" path. RFCs 3986/2396
d89089206f40f9a6d58528ff85050447d4a52d53lgentis /* Url has met all of the filter conditions so far, determine
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * if the paths match.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzorstatic cache_provider_list *get_provider(request_rec *r, struct cache_enable *ent,
d89089206f40f9a6d58528ff85050447d4a52d53lgentis /* Fetch from global config and add to the list. */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor provider = ap_lookup_provider(CACHE_PROVIDER_GROUP, ent->type,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* Log an error! */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor newp = apr_pcalloc(r->pool, sizeof(cache_provider_list));
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzorcache_provider_list *cache_get_providers(request_rec *r,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_module);
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* per directory cache disable */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* global cache disable */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor if (uri_meets_conditions(&ent[i].url, ent[i].pathlen, &uri)) {
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* Stop searching now. */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* loop through all the per directory cacheenable entries */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* loop through all the global cacheenable entries */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor if (uri_meets_conditions(&ent[i].url, ent[i].pathlen, &uri)) {
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor/* do a HTTP/1.1 age calculation */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzorCACHE_DECLARE(apr_int64_t) ap_cache_current_age(cache_info *info,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor apr_time_t apparent_age, corrected_received_age, response_delay,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor apparent_age = MAX(0, info->response_time - info->date);
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor corrected_received_age = MAX(apparent_age, age_value_usec);
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor response_delay = info->response_time - info->request_time;
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor corrected_initial_age = corrected_received_age + response_delay;
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * Try obtain a cache wide lock on the given cache key.
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * If we return APR_SUCCESS, we obtained the lock, and we are clear to
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * proceed to the backend. If we return APR_EEXIST, then the lock is
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * already locked, someone else has gone to refresh the backend data
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * already, so we must return stale data with a warning in the mean
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * time. If we return anything else, then something has gone pear
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * shaped, and we allow the request through to the backend regardless.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * This lock is created from the request pool, meaning that should
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * something go wrong and the lock isn't deleted on return of the
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * request headers from the backend for whatever reason, at worst the
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * lock will be cleaned up when the request dies or finishes.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * If something goes truly bananas and the lock isn't deleted when the
67972b58b9a56ebc101bea2e9758569b973dd5cand * request dies, the lock will be trashed when its max-age is reached,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * or when a request arrives containing a Cache-Control: no-cache. At
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * no point is it possible for this lock to permanently deny access to
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor * the backend.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzorapr_status_t cache_try_lock(cache_server_conf *conf, cache_request_rec *cache,
576c49cd335618ad4b5351bd1c5f2cfd7584dba4lgentis const char *lockname;
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor const char *path;
560024d8ef457c288c07cee03dd8db0ab28c2fb8lgentis /* no locks configured, leave */
560024d8ef457c288c07cee03dd8db0ab28c2fb8lgentis /* lock already obtained earlier? if so, success */
560024d8ef457c288c07cee03dd8db0ab28c2fb8lgentis apr_pool_userdata_get(&dummy, CACHE_LOCKFILE_KEY, r->pool);
560024d8ef457c288c07cee03dd8db0ab28c2fb8lgentis /* create the key if it doesn't exist */
aca6eead4bc5fed9b497b0ca21027c1bcba56054lgentis * Try to use the key of a possible open but stale cache
d89089206f40f9a6d58528ff85050447d4a52d53lgentis * entry if we have one.
1b1b6ae3d9cf8a22cd74249fe56d4fab443f9e21lgentis if ((h != NULL) &&
1b1b6ae3d9cf8a22cd74249fe56d4fab443f9e21lgentis cache->key = apr_pstrdup(r->pool, h->cache_obj->key);
21b0af80656190938e7dd1d9b0a83dc803626824lgentis /* create a hashed filename from the key, and save it for later */
21b0af80656190938e7dd1d9b0a83dc803626824lgentis lockname = ap_cache_generate_name(r->pool, 0, 0, cache->key);
560024d8ef457c288c07cee03dd8db0ab28c2fb8lgentis /* lock files represent discrete just-went-stale URLs "in flight", so
560024d8ef457c288c07cee03dd8db0ab28c2fb8lgentis * we support a simple two level directory structure, more is overkill.
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor /* make the directories */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor path = apr_pstrcat(r->pool, conf->lockpath, dir, NULL);
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor if (APR_SUCCESS != (status = apr_dir_make_recursive(path,
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00778)
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor "Could not create a cache lock directory: %s",
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor lockname = apr_pstrcat(r->pool, path, "/", lockname, NULL);
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor apr_pool_userdata_set(lockname, CACHE_LOCKNAME_KEY, NULL, r->pool);
d89089206f40f9a6d58528ff85050447d4a52d53lgentis /* is an existing lock file too old? */
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor if (!(APR_STATUS_IS_ENOENT(status)) && APR_SUCCESS != status) {
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00779)
21d31c05096a45954f47863580572da87c902d34lgentis "Could not stat a cache lock file: %s",
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor if ((status == APR_SUCCESS) && (((now - finfo.mtime) > conf->lockmaxage)
fbad7185dd78ec6e09c5b191693deda9d4bfa08cgryzor ap_log_rerror(APLOG_MARK, APLOG_INFO, status, r, APLOGNO(00780)
return status;
void *dummy;
const char *lockname;
return APR_SUCCESS;
if (bb) {
apr_bucket *e;
int eos_found = 0;
e = APR_BUCKET_NEXT(e))
if (APR_BUCKET_IS_EOS(e)) {
if (!eos_found) {
return APR_SUCCESS;
if (dummy) {
if (!lockname) {
&cache_module);
&cache_module);
request_rec *r)
const char *cc_req;
const char *warn_head;
&cache_module);
r->unparsed_uri);
maxstale = 0;
minfresh = 0;
maxstale = 0;
r->unparsed_uri);
r->unparsed_uri);
r->unparsed_uri);
const char **str)
apr_size_t i;
if (s != NULL) {
i = s - list;
while (apr_isspace(*s))
*str = s;
return NULL;
int i, ch;
apr_time_t j;
ch = x[i];
int i, ch;
for (i = k = d = 0; d < ndepth; ++d) {
k += nlength;
char *token;
int quoted = 0;
return NULL;
++str;
return NULL;
while (**last) {
if (!quoted) {
++*last;
++*last;
quoted = 0;
++*last;
++*last;
if (**last) {
++*last;
++*last;
if (**last) {
++*last;
return token;
char *last;
if (pragma_header) {
while (token) {
if (cc_header) {
while (token) {
switch (token[0]) {
int found = 0;
if (cc_header) {
while (token) {
switch (token[0]) {
while (header) {
&slast);
while (header) {
&slast);
return found;
apr_table_t *t,
server_rec *s)
char **header;
if (t == NULL) {
&cache_module);
return headers_out;
r->server);
return headers_out;
r->err_headers_out);
if (r->content_type
if (ctype) {
if (r->content_encoding
r->content_encoding);
return headers_out;
apr_pool_t *p;
const char *first;
const char **elt;
const char *key)
state.p = p;
return NULL;