]> granicus.if.org Git - apache/blob - modules/cache/cache_util.c
* According to RFC2616 13.2.3 1. negative results of age calculations should
[apache] / modules / cache / cache_util.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "mod_cache.h"
18
19 #include <ap_provider.h>
20
21 /* -------------------------------------------------------------- */
22
23 extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
24
25 extern module AP_MODULE_DECLARE_DATA cache_module;
26
27 /* Determine if "url" matches the hostname, scheme and port and path
28  * in "filter". All but the path comparisons are case-insensitive.
29  */
30 static int uri_meets_conditions(const apr_uri_t filter, const int pathlen,
31         const apr_uri_t url) {
32
33     /* Scheme, hostname port and local part. The filter URI and the
34      * URI we test may have the following shapes:
35      *   /<path>
36      *   <scheme>[:://<hostname>[:<port>][/<path>]]
37      * That is, if there is no scheme then there must be only the path,
38      * and we check only the path; if there is a scheme, we check the
39      * scheme for equality, and then if present we match the hostname,
40      * and then if present match the port, and finally the path if any.
41      *
42      * Note that this means that "/<path>" only matches local paths,
43      * and to match proxied paths one *must* specify the scheme.
44      */
45
46     /* Is the filter is just for a local path or a proxy URI? */
47     if (!filter.scheme) {
48         if (url.scheme || url.hostname) {
49             return 0;
50         }
51     }
52     else {
53         /* The URI scheme must be present and identical except for case. */
54         if (!url.scheme || strcasecmp(filter.scheme, url.scheme)) {
55             return 0;
56         }
57
58         /* If the filter hostname is null or empty it matches any hostname,
59          * if it begins with a "*" it matches the _end_ of the URI hostname
60          * excluding the "*", if it begins with a "." it matches the _end_
61          * of the URI * hostname including the ".", otherwise it must match
62          * the URI hostname exactly. */
63
64         if (filter.hostname && filter.hostname[0]) {
65             if (filter.hostname[0] == '.') {
66                 const size_t fhostlen = strlen(filter.hostname);
67                 const size_t uhostlen = url.hostname ? strlen(url.hostname) : 0;
68
69                 if (fhostlen > uhostlen || strcasecmp(filter.hostname,
70                         url.hostname + uhostlen - fhostlen)) {
71                     return 0;
72                 }
73             }
74             else if (filter.hostname[0] == '*') {
75                 const size_t fhostlen = strlen(filter.hostname + 1);
76                 const size_t uhostlen = url.hostname ? strlen(url.hostname) : 0;
77
78                 if (fhostlen > uhostlen || strcasecmp(filter.hostname + 1,
79                         url.hostname + uhostlen - fhostlen)) {
80                     return 0;
81                 }
82             }
83             else if (!url.hostname || strcasecmp(filter.hostname, url.hostname)) {
84                 return 0;
85             }
86         }
87
88         /* If the filter port is empty it matches any URL port.
89          * If the filter or URL port are missing, or the URL port is
90          * empty, they default to the port for their scheme. */
91
92         if (!(filter.port_str && !filter.port_str[0])) {
93             /* NOTE:  ap_port_of_scheme will return 0 if given NULL input */
94             const unsigned fport = filter.port_str ? filter.port
95                     : apr_uri_port_of_scheme(filter.scheme);
96             const unsigned uport = (url.port_str && url.port_str[0])
97                     ? url.port : apr_uri_port_of_scheme(url.scheme);
98
99             if (fport != uport) {
100                 return 0;
101             }
102         }
103     }
104
105     /* For HTTP caching purposes, an empty (NULL) path is equivalent to
106      * a single "/" path. RFCs 3986/2396
107      */
108     if (!url.path) {
109         if (*filter.path == '/' && pathlen == 1) {
110             return 1;
111         }
112         else {
113             return 0;
114         }
115     }
116
117     /* Url has met all of the filter conditions so far, determine
118      * if the paths match.
119      */
120     return !strncmp(filter.path, url.path, pathlen);
121 }
122
123 CACHE_DECLARE(cache_provider_list *)ap_cache_get_providers(request_rec *r,
124                                                   cache_server_conf *conf,
125                                                   apr_uri_t uri)
126 {
127     cache_provider_list *providers = NULL;
128     int i;
129
130     /* loop through all the cacheenable entries */
131     for (i = 0; i < conf->cacheenable->nelts; i++) {
132         struct cache_enable *ent =
133                                 (struct cache_enable *)conf->cacheenable->elts;
134         if (uri_meets_conditions(ent[i].url, ent[i].pathlen, uri)) {
135             /* Fetch from global config and add to the list. */
136             cache_provider *provider;
137             provider = ap_lookup_provider(CACHE_PROVIDER_GROUP, ent[i].type,
138                                           "0");
139             if (!provider) {
140                 /* Log an error! */
141             }
142             else {
143                 cache_provider_list *newp;
144                 newp = apr_pcalloc(r->pool, sizeof(cache_provider_list));
145                 newp->provider_name = ent[i].type;
146                 newp->provider = provider;
147
148                 if (!providers) {
149                     providers = newp;
150                 }
151                 else {
152                     cache_provider_list *last = providers;
153
154                     while (last->next) {
155                         last = last->next;
156                     }
157                     last->next = newp;
158                 }
159             }
160         }
161     }
162
163     /* then loop through all the cachedisable entries
164      * Looking for urls that contain the full cachedisable url and possibly
165      * more.
166      * This means we are disabling cachedisable url and below...
167      */
168     for (i = 0; i < conf->cachedisable->nelts; i++) {
169         struct cache_disable *ent =
170                                (struct cache_disable *)conf->cachedisable->elts;
171         if (uri_meets_conditions(ent[i].url, ent[i].pathlen, uri)) {
172             /* Stop searching now. */
173             return NULL;
174         }
175     }
176
177     return providers;
178 }
179
180
181 /* do a HTTP/1.1 age calculation */
182 CACHE_DECLARE(apr_int64_t) ap_cache_current_age(cache_info *info,
183                                                 const apr_time_t age_value,
184                                                 apr_time_t now)
185 {
186     apr_time_t apparent_age, corrected_received_age, response_delay,
187                corrected_initial_age, resident_time, current_age,
188                age_value_usec;
189
190     age_value_usec = apr_time_from_sec(age_value);
191
192     /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */
193
194     apparent_age = MAX(0, info->response_time - info->date);
195     corrected_received_age = MAX(apparent_age, age_value_usec);
196     response_delay = info->response_time - info->request_time;
197     corrected_initial_age = corrected_received_age + response_delay;
198     resident_time = now - info->response_time;
199     current_age = corrected_initial_age + resident_time;
200
201     if (current_age < 0) {
202         current_age = 0;
203     }
204
205     return apr_time_sec(current_age);
206 }
207
208 /**
209  * Try obtain a cache wide lock on the given cache key.
210  *
211  * If we return APR_SUCCESS, we obtained the lock, and we are clear to
212  * proceed to the backend. If we return APR_EEXISTS, then the lock is
213  * already locked, someone else has gone to refresh the backend data
214  * already, so we must return stale data with a warning in the mean
215  * time. If we return anything else, then something has gone pear
216  * shaped, and we allow the request through to the backend regardless.
217  *
218  * This lock is created from the request pool, meaning that should
219  * something go wrong and the lock isn't deleted on return of the
220  * request headers from the backend for whatever reason, at worst the
221  * lock will be cleaned up when the request dies or finishes.
222  *
223  * If something goes truly bananas and the lock isn't deleted when the
224  * request dies, the lock will be trashed when its max-age is reached,
225  * or when a request arrives containing a Cache-Control: no-cache. At
226  * no point is it possible for this lock to permanently deny access to
227  * the backend.
228  */
229 CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
230         request_rec *r, char *key) {
231     apr_status_t status;
232     const char *lockname;
233     const char *path;
234     char dir[5];
235     apr_time_t now = apr_time_now();
236     apr_finfo_t finfo;
237     apr_file_t *lockfile;
238     void *dummy;
239
240     finfo.mtime = 0;
241
242     if (!conf || !conf->lock || !conf->lockpath) {
243         /* no locks configured, leave */
244         return APR_SUCCESS;
245     }
246
247     /* lock already obtained earlier? if so, success */
248     apr_pool_userdata_get(&dummy, CACHE_LOCKFILE_KEY, r->pool);
249     if (dummy) {
250         return APR_SUCCESS;
251     }
252
253     /* create the key if it doesn't exist */
254     if (!key) {
255         cache_generate_key(r, r->pool, &key);
256     }
257
258     /* create a hashed filename from the key, and save it for later */
259     lockname = ap_cache_generate_name(r->pool, 0, 0, key);
260
261     /* lock files represent discrete just-went-stale URLs "in flight", so
262      * we support a simple two level directory structure, more is overkill.
263      */
264     dir[0] = '/';
265     dir[1] = lockname[0];
266     dir[2] = '/';
267     dir[3] = lockname[1];
268     dir[4] = 0;
269
270     /* make the directories */
271     path = apr_pstrcat(r->pool, conf->lockpath, dir, NULL);
272     if (APR_SUCCESS != (status = apr_dir_make_recursive(path,
273             APR_UREAD|APR_UWRITE|APR_UEXECUTE, r->pool))) {
274         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
275                      "Could not create a cache lock directory: %s",
276                      path);
277         return status;
278     }
279     lockname = apr_pstrcat(r->pool, path, "/", lockname, NULL);
280     apr_pool_userdata_set(lockname, CACHE_LOCKNAME_KEY, NULL, r->pool);
281
282     /* is an existing lock file too old? */
283     status = apr_stat(&finfo, lockname,
284                 APR_FINFO_MTIME | APR_FINFO_NLINK, r->pool);
285     if (!(APR_STATUS_IS_ENOENT(status)) && APR_SUCCESS != status) {
286         ap_log_error(APLOG_MARK, APLOG_ERR, APR_EEXIST, r->server,
287                      "Could not stat a cache lock file: %s",
288                      lockname);
289         return status;
290     }
291     if ((status == APR_SUCCESS) && (((now - finfo.mtime) > conf->lockmaxage)
292                                   || (now < finfo.mtime))) {
293         ap_log_error(APLOG_MARK, APLOG_INFO, status, r->server,
294                      "Cache lock file for '%s' too old, removing: %s",
295                      r->uri, lockname);
296         apr_file_remove(lockname, r->pool);
297     }
298
299     /* try obtain a lock on the file */
300     if (APR_SUCCESS == (status = apr_file_open(&lockfile, lockname,
301             APR_WRITE | APR_CREATE | APR_EXCL | APR_DELONCLOSE,
302             APR_UREAD | APR_UWRITE, r->pool))) {
303         apr_pool_userdata_set(lockfile, CACHE_LOCKFILE_KEY, NULL, r->pool);
304     }
305     return status;
306
307 }
308
309 /**
310  * Remove the cache lock, if present.
311  *
312  * First, try to close the file handle, whose delete-on-close should
313  * kill the file. Otherwise, just delete the file by name.
314  *
315  * If no lock name has yet been calculated, do the calculation of the
316  * lock name first before trying to delete the file.
317  *
318  * If an optional bucket brigade is passed, the lock will only be
319  * removed if the bucket brigade contains an EOS bucket.
320  */
321 CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
322         request_rec *r, char *key, apr_bucket_brigade *bb) {
323     void *dummy;
324     const char *lockname;
325
326     if (!conf || !conf->lock || !conf->lockpath) {
327         /* no locks configured, leave */
328         return APR_SUCCESS;
329     }
330     if (bb) {
331         apr_bucket *e;
332         int eos_found = 0;
333         for (e = APR_BRIGADE_FIRST(bb);
334              e != APR_BRIGADE_SENTINEL(bb);
335              e = APR_BUCKET_NEXT(e))
336         {
337             if (APR_BUCKET_IS_EOS(e)) {
338                 eos_found = 1;
339                 break;
340             }
341         }
342         if (!eos_found) {
343             /* no eos found in brigade, don't delete anything just yet,
344              * we are not done.
345              */
346             return APR_SUCCESS;
347         }
348     }
349     apr_pool_userdata_get(&dummy, CACHE_LOCKFILE_KEY, r->pool);
350     if (dummy) {
351         return apr_file_close((apr_file_t *)dummy);
352     }
353     apr_pool_userdata_get(&dummy, CACHE_LOCKNAME_KEY, r->pool);
354     lockname = (const char *)dummy;
355     if (!lockname) {
356         char dir[5];
357
358         /* create the key if it doesn't exist */
359         if (!key) {
360             cache_generate_key(r, r->pool, &key);
361         }
362
363         /* create a hashed filename from the key, and save it for later */
364         lockname = ap_cache_generate_name(r->pool, 0, 0, key);
365
366         /* lock files represent discrete just-went-stale URLs "in flight", so
367          * we support a simple two level directory structure, more is overkill.
368          */
369         dir[0] = '/';
370         dir[1] = lockname[0];
371         dir[2] = '/';
372         dir[3] = lockname[1];
373         dir[4] = 0;
374
375         lockname = apr_pstrcat(r->pool, conf->lockpath, dir, "/", lockname, NULL);
376     }
377     return apr_file_remove(lockname, r->pool);
378 }
379
380 CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
381                                             request_rec *r)
382 {
383     apr_status_t status;
384     apr_int64_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale;
385     apr_int64_t minfresh;
386     const char *cc_cresp, *cc_req;
387     const char *pragma;
388     const char *agestr = NULL;
389     const char *expstr = NULL;
390     char *val;
391     apr_time_t age_c = 0;
392     cache_info *info = &(h->cache_obj->info);
393     const char *warn_head;
394     cache_server_conf *conf =
395       (cache_server_conf *)ap_get_module_config(r->server->module_config,
396                                                 &cache_module);
397
398     /*
399      * We now want to check if our cached data is still fresh. This depends
400      * on a few things, in this order:
401      *
402      * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache. no-cache in
403      * either the request or the cached response means that we must
404      * revalidate the request unconditionally, overriding any expiration
405      * mechanism. It's equivalent to max-age=0,must-revalidate.
406      *
407      * - RFC2616 14.32 Pragma: no-cache This is treated the same as
408      * Cache-Control: no-cache.
409      *
410      * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate,
411      * proxy-revalidate if the max-stale request header exists, modify the
412      * stale calculations below so that an object can be at most <max-stale>
413      * seconds stale before we request a revalidation, _UNLESS_ a
414      * must-revalidate or proxy-revalidate cached response header exists to
415      * stop us doing this.
416      *
417      * - RFC2616 14.9.3 Cache-Control: s-maxage the origin server specifies the
418      * maximum age an object can be before it is considered stale. This
419      * directive has the effect of proxy|must revalidate, which in turn means
420      * simple ignore any max-stale setting.
421      *
422      * - RFC2616 14.9.4 Cache-Control: max-age this header can appear in both
423      * requests and responses. If both are specified, the smaller of the two
424      * takes priority.
425      *
426      * - RFC2616 14.21 Expires: if this request header exists in the cached
427      * entity, and it's value is in the past, it has expired.
428      *
429      */
430
431     /* This value comes from the client's initial request. */
432     cc_req = apr_table_get(r->headers_in, "Cache-Control");
433     pragma = apr_table_get(r->headers_in, "Pragma");
434
435     if (ap_cache_liststr(NULL, pragma, "no-cache", NULL)
436         || ap_cache_liststr(NULL, cc_req, "no-cache", NULL)) {
437
438         if (!conf->ignorecachecontrol) {
439             /* Treat as stale, causing revalidation */
440             return 0;
441         }
442
443         ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
444                      "Incoming request is asking for a uncached version of "
445                      "%s, but we know better and are ignoring it",
446                      r->unparsed_uri);
447     }
448
449     /* These come from the cached entity. */
450     cc_cresp = apr_table_get(h->resp_hdrs, "Cache-Control");
451     expstr = apr_table_get(h->resp_hdrs, "Expires");
452
453     if (ap_cache_liststr(NULL, cc_cresp, "no-cache", NULL)) {
454         /*
455          * The cached entity contained Cache-Control: no-cache, so treat as
456          * stale causing revalidation
457          */
458         return 0;
459     }
460
461     if ((agestr = apr_table_get(h->resp_hdrs, "Age"))) {
462         age_c = apr_atoi64(agestr);
463     }
464
465     /* calculate age of object */
466     age = ap_cache_current_age(info, age_c, r->request_time);
467
468     /* extract s-maxage */
469     if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "s-maxage", &val)
470         && val != NULL) {
471         smaxage = apr_atoi64(val);
472     }
473     else {
474         smaxage = -1;
475     }
476
477     /* extract max-age from request */
478     if (!conf->ignorecachecontrol
479         && cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val)
480         && val != NULL) {
481         maxage_req = apr_atoi64(val);
482     }
483     else {
484         maxage_req = -1;
485     }
486
487     /* extract max-age from response */
488     if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "max-age", &val)
489         && val != NULL) {
490         maxage_cresp = apr_atoi64(val);
491     }
492     else {
493         maxage_cresp = -1;
494     }
495
496     /*
497      * if both maxage request and response, the smaller one takes priority
498      */
499     if (maxage_req == -1) {
500         maxage = maxage_cresp;
501     }
502     else if (maxage_cresp == -1) {
503         maxage = maxage_req;
504     }
505     else {
506         maxage = MIN(maxage_req, maxage_cresp);
507     }
508
509     /* extract max-stale */
510     if (cc_req && ap_cache_liststr(r->pool, cc_req, "max-stale", &val)) {
511         if(val != NULL) {
512             maxstale = apr_atoi64(val);
513         }
514         else {
515             /*
516              * If no value is assigned to max-stale, then the client is willing
517              * to accept a stale response of any age (RFC2616 14.9.3). We will
518              * set it to one year in this case as this situation is somewhat
519              * similar to a "never expires" Expires header (RFC2616 14.21)
520              * which is set to a date one year from the time the response is
521              * sent in this case.
522              */
523             maxstale = APR_INT64_C(86400*365);
524         }
525     }
526     else {
527         maxstale = 0;
528     }
529
530     /* extract min-fresh */
531     if (!conf->ignorecachecontrol
532         && cc_req && ap_cache_liststr(r->pool, cc_req, "min-fresh", &val)
533         && val != NULL) {
534         minfresh = apr_atoi64(val);
535     }
536     else {
537         minfresh = 0;
538     }
539
540     /* override maxstale if must-revalidate or proxy-revalidate */
541     if (maxstale && ((cc_cresp &&
542                       ap_cache_liststr(NULL, cc_cresp,
543                                        "must-revalidate", NULL)) ||
544                      (cc_cresp &&
545                       ap_cache_liststr(NULL, cc_cresp,
546                                        "proxy-revalidate", NULL)))) {
547         maxstale = 0;
548     }
549
550     /* handle expiration */
551     if (((smaxage != -1) && (age < (smaxage - minfresh))) ||
552         ((maxage != -1) && (age < (maxage + maxstale - minfresh))) ||
553         ((smaxage == -1) && (maxage == -1) &&
554          (info->expire != APR_DATE_BAD) &&
555          (age < (apr_time_sec(info->expire - info->date) + maxstale - minfresh)))) {
556
557         warn_head = apr_table_get(h->resp_hdrs, "Warning");
558
559         /* it's fresh darlings... */
560         /* set age header on response */
561         apr_table_set(h->resp_hdrs, "Age",
562                       apr_psprintf(r->pool, "%lu", (unsigned long)age));
563
564         /* add warning if maxstale overrode freshness calculation */
565         if (!(((smaxage != -1) && age < smaxage) ||
566               ((maxage != -1) && age < maxage) ||
567               (info->expire != APR_DATE_BAD &&
568                (apr_time_sec(info->expire - info->date)) > age))) {
569             /* make sure we don't stomp on a previous warning */
570             if ((warn_head == NULL) ||
571                 ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
572                 apr_table_merge(h->resp_hdrs, "Warning",
573                                 "110 Response is stale");
574             }
575         }
576         /*
577          * If none of Expires, Cache-Control: max-age, or Cache-Control:
578          * s-maxage appears in the response, and the response header age
579          * calculated is more than 24 hours add the warning 113
580          */
581         if ((maxage_cresp == -1) && (smaxage == -1) &&
582             (expstr == NULL) && (age > 86400)) {
583
584             /* Make sure we don't stomp on a previous warning, and don't dup
585              * a 113 marning that is already present. Also, make sure to add
586              * the new warning to the correct *headers_out location.
587              */
588             if ((warn_head == NULL) ||
589                 ((warn_head != NULL) && (ap_strstr_c(warn_head, "113") == NULL))) {
590                 apr_table_merge(h->resp_hdrs, "Warning",
591                                 "113 Heuristic expiration");
592             }
593         }
594         return 1;    /* Cache object is fresh (enough) */
595     }
596
597     /*
598      * At this point we are stale, but: if we are under load, we may let
599      * a significant number of stale requests through before the first
600      * stale request successfully revalidates itself, causing a sudden
601      * unexpected thundering herd which in turn brings angst and drama.
602      *
603      * So.
604      *
605      * We want the first stale request to go through as normal. But the
606      * second and subsequent request, we must pretend to be fresh until
607      * the first request comes back with either new content or confirmation
608      * that the stale content is still fresh.
609      *
610      * To achieve this, we create a very simple file based lock based on
611      * the key of the cached object. We attempt to open the lock file with
612      * exclusive write access. If we succeed, woohoo! we're first, and we
613      * follow the stale path to the backend server. If we fail, oh well,
614      * we follow the fresh path, and avoid being a thundering herd.
615      *
616      * The lock lives only as long as the stale request that went on ahead.
617      * If the request succeeds, the lock is deleted. If the request fails,
618      * the lock is deleted, and another request gets to make a new lock
619      * and try again.
620      *
621      * At any time, a request marked "no-cache" will force a refresh,
622      * ignoring the lock, ensuring an extended lockout is impossible.
623      *
624      * A lock that exceeds a maximum age will be deleted, and another
625      * request gets to make a new lock and try again.
626      */
627     status = ap_cache_try_lock(conf, r, (char *)h->cache_obj->key);
628     if (APR_SUCCESS == status) {
629         /* we obtained a lock, follow the stale path */
630         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
631                      "Cache lock obtained for stale cached URL, "
632                      "revalidating entry: %s",
633                      r->unparsed_uri);
634         return 0;
635     }
636     else if (APR_EEXIST == status) {
637         /* lock already exists, return stale data anyway, with a warning */
638         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
639                      "Cache already locked for stale cached URL, "
640                      "pretend it is fresh: %s",
641                      r->unparsed_uri);
642
643         /* make sure we don't stomp on a previous warning */
644         warn_head = apr_table_get(h->resp_hdrs, "Warning");
645         if ((warn_head == NULL) ||
646             ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
647             apr_table_merge(h->resp_hdrs, "Warning",
648                         "110 Response is stale");
649         }
650
651         return 1;
652     }
653     else {
654         /* some other error occurred, just treat the object as stale */
655         ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
656                      "Attempt to obtain a cache lock for stale "
657                      "cached URL failed, revalidating entry anyway: %s",
658                      r->unparsed_uri);
659         return 0;
660     }
661
662 }
663
664 /*
665  * list is a comma-separated list of case-insensitive tokens, with
666  * optional whitespace around the tokens.
667  * The return returns 1 if the token val is found in the list, or 0
668  * otherwise.
669  */
670 CACHE_DECLARE(int) ap_cache_liststr(apr_pool_t *p, const char *list,
671                                     const char *key, char **val)
672 {
673     apr_size_t key_len;
674     const char *next;
675
676     if (!list) {
677         return 0;
678     }
679
680     key_len = strlen(key);
681     next = list;
682
683     for (;;) {
684
685         /* skip whitespace and commas to find the start of the next key */
686         while (*next && (apr_isspace(*next) || (*next == ','))) {
687             next++;
688         }
689
690         if (!*next) {
691             return 0;
692         }
693
694         if (!strncasecmp(next, key, key_len)) {
695             /* this field matches the key (though it might just be
696              * a prefix match, so make sure the match is followed
697              * by either a space or an equals sign)
698              */
699             next += key_len;
700             if (!*next || (*next == '=') || apr_isspace(*next) ||
701                 (*next == ',')) {
702                 /* valid match */
703                 if (val) {
704                     while (*next && (*next != '=') && (*next != ',')) {
705                         next++;
706                     }
707                     if (*next == '=') {
708                         next++;
709                         while (*next && apr_isspace(*next )) {
710                             next++;
711                         }
712                         if (!*next) {
713                             *val = NULL;
714                         }
715                         else {
716                             const char *val_start = next;
717                             while (*next && !apr_isspace(*next) &&
718                                    (*next != ',')) {
719                                 next++;
720                             }
721                             *val = apr_pstrmemdup(p, val_start,
722                                                   next - val_start);
723                         }
724                     }
725                     else {
726                         *val = NULL;
727                     }
728                 }
729                 return 1;
730             }
731         }
732
733         /* skip to the next field */
734         do {
735             next++;
736             if (!*next) {
737                 return 0;
738             }
739         } while (*next != ',');
740     }
741 }
742
743 /* return each comma separated token, one at a time */
744 CACHE_DECLARE(const char *)ap_cache_tokstr(apr_pool_t *p, const char *list,
745                                            const char **str)
746 {
747     apr_size_t i;
748     const char *s;
749
750     s = ap_strchr_c(list, ',');
751     if (s != NULL) {
752         i = s - list;
753         do
754             s++;
755         while (apr_isspace(*s))
756             ; /* noop */
757     }
758     else
759         i = strlen(list);
760
761     while (i > 0 && apr_isspace(list[i - 1]))
762         i--;
763
764     *str = s;
765     if (i)
766         return apr_pstrndup(p, list, i);
767     else
768         return NULL;
769 }
770
771 /*
772  * Converts apr_time_t expressed as hex digits to
773  * a true apr_time_t.
774  */
775 CACHE_DECLARE(apr_time_t) ap_cache_hex2usec(const char *x)
776 {
777     int i, ch;
778     apr_time_t j;
779     for (i = 0, j = 0; i < sizeof(j) * 2; i++) {
780         ch = x[i];
781         j <<= 4;
782         if (apr_isdigit(ch))
783             j |= ch - '0';
784         else if (apr_isupper(ch))
785             j |= ch - ('A' - 10);
786         else
787             j |= ch - ('a' - 10);
788     }
789     return j;
790 }
791
792 /*
793  * Converts apr_time_t to apr_time_t expressed as hex digits.
794  */
795 CACHE_DECLARE(void) ap_cache_usec2hex(apr_time_t j, char *y)
796 {
797     int i, ch;
798
799     for (i = (sizeof(j) * 2)-1; i >= 0; i--) {
800         ch = (int)(j & 0xF);
801         j >>= 4;
802         if (ch >= 10)
803             y[i] = ch + ('A' - 10);
804         else
805             y[i] = ch + '0';
806     }
807     y[sizeof(j) * 2] = '\0';
808 }
809
810 static void cache_hash(const char *it, char *val, int ndepth, int nlength)
811 {
812     apr_md5_ctx_t context;
813     unsigned char digest[16];
814     char tmp[22];
815     int i, k, d;
816     unsigned int x;
817     static const char enc_table[64] =
818     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
819
820     apr_md5_init(&context);
821     apr_md5_update(&context, (const unsigned char *) it, strlen(it));
822     apr_md5_final(digest, &context);
823
824     /* encode 128 bits as 22 characters, using a modified uuencoding
825      * the encoding is 3 bytes -> 4 characters* i.e. 128 bits is
826      * 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
827      */
828     for (i = 0, k = 0; i < 15; i += 3) {
829         x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
830         tmp[k++] = enc_table[x >> 18];
831         tmp[k++] = enc_table[(x >> 12) & 0x3f];
832         tmp[k++] = enc_table[(x >> 6) & 0x3f];
833         tmp[k++] = enc_table[x & 0x3f];
834     }
835
836     /* one byte left */
837     x = digest[15];
838     tmp[k++] = enc_table[x >> 2];    /* use up 6 bits */
839     tmp[k++] = enc_table[(x << 4) & 0x3f];
840
841     /* now split into directory levels */
842     for (i = k = d = 0; d < ndepth; ++d) {
843         memcpy(&val[i], &tmp[k], nlength);
844         k += nlength;
845         val[i + nlength] = '/';
846         i += nlength + 1;
847     }
848     memcpy(&val[i], &tmp[k], 22 - k);
849     val[i + 22 - k] = '\0';
850 }
851
852 CACHE_DECLARE(char *)ap_cache_generate_name(apr_pool_t *p, int dirlevels,
853                                             int dirlength, const char *name)
854 {
855     char hashfile[66];
856     cache_hash(name, hashfile, dirlevels, dirlength);
857     return apr_pstrdup(p, hashfile);
858 }
859
860 /*
861  * Create a new table consisting of those elements from an
862  * headers table that are allowed to be stored in a cache.
863  */
864 CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers(apr_pool_t *pool,
865                                                         apr_table_t *t,
866                                                         server_rec *s)
867 {
868     cache_server_conf *conf;
869     char **header;
870     int i;
871     apr_table_t *headers_out;
872
873     /* Short circuit the common case that there are not
874      * (yet) any headers populated.
875      */
876     if (t == NULL) {
877         return apr_table_make(pool, 10);
878     };
879
880     /* Make a copy of the headers, and remove from
881      * the copy any hop-by-hop headers, as defined in Section
882      * 13.5.1 of RFC 2616
883      */
884     headers_out = apr_table_copy(pool, t);
885
886     apr_table_unset(headers_out, "Connection");
887     apr_table_unset(headers_out, "Keep-Alive");
888     apr_table_unset(headers_out, "Proxy-Authenticate");
889     apr_table_unset(headers_out, "Proxy-Authorization");
890     apr_table_unset(headers_out, "TE");
891     apr_table_unset(headers_out, "Trailers");
892     apr_table_unset(headers_out, "Transfer-Encoding");
893     apr_table_unset(headers_out, "Upgrade");
894
895     conf = (cache_server_conf *)ap_get_module_config(s->module_config,
896                                                      &cache_module);
897
898     /* Remove the user defined headers set with CacheIgnoreHeaders.
899      * This may break RFC 2616 compliance on behalf of the administrator.
900      */
901     header = (char **)conf->ignore_headers->elts;
902     for (i = 0; i < conf->ignore_headers->nelts; i++) {
903         apr_table_unset(headers_out, header[i]);
904     }
905     return headers_out;
906 }
907
908 /*
909  * Legacy call - functionally equivalent to ap_cache_cacheable_headers.
910  * @deprecated @see ap_cache_cacheable_headers
911  */
912 CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *p,
913                                                         apr_table_t *t,
914                                                         server_rec *s)
915 {
916     return ap_cache_cacheable_headers(p,t,s);
917 }
918
919 /*
920  * Create a new table consisting of those elements from an input
921  * headers table that are allowed to be stored in a cache.
922  */
923 CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_in(request_rec *r)
924 {
925     return ap_cache_cacheable_headers(r->pool, r->headers_in, r->server);
926 }
927
928 /*
929  * Create a new table consisting of those elements from an output
930  * headers table that are allowed to be stored in a cache;
931  * ensure there is a content type and capture any errors.
932  */
933 CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_out(request_rec *r)
934 {
935     apr_table_t *headers_out;
936
937     headers_out = apr_table_overlay(r->pool, r->headers_out,
938                                         r->err_headers_out);
939
940     apr_table_clear(r->err_headers_out);
941
942     headers_out = ap_cache_cacheable_headers(r->pool, headers_out,
943                                                   r->server);
944
945     if (!apr_table_get(headers_out, "Content-Type")
946         && r->content_type) {
947         apr_table_setn(headers_out, "Content-Type",
948                        ap_make_content_type(r, r->content_type));
949     }
950
951     if (!apr_table_get(headers_out, "Content-Encoding")
952         && r->content_encoding) {
953         apr_table_setn(headers_out, "Content-Encoding",
954                        r->content_encoding);
955     }
956
957     return headers_out;
958 }