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