Apache HTTPD
cache_util.c
Go to the documentation of this file.
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 "cache_util.h"
20#include <ap_provider.h>
21
23
24/* -------------------------------------------------------------- */
25
27
28extern module AP_MODULE_DECLARE_DATA cache_module;
29
30/* Determine if "url" matches the hostname, scheme and port and path
31 * in "filter". All but the path comparisons are case-insensitive.
32 */
33static int uri_meets_conditions(const apr_uri_t *filter, const apr_size_t pathlen,
34 const apr_uri_t *url, const char *path)
35{
36 /* Scheme, hostname port and local part. The filter URI and the
37 * URI we test may have the following shapes:
38 * /<path>
39 * <scheme>[:://<hostname>[:<port>][/<path>]]
40 * That is, if there is no scheme then there must be only the path,
41 * and we check only the path; if there is a scheme, we check the
42 * scheme for equality, and then if present we match the hostname,
43 * and then if present match the port, and finally the path if any.
44 *
45 * Note that this means that "/<path>" only matches local paths,
46 * and to match proxied paths one *must* specify the scheme.
47 */
48
49 /* Is the filter is just for a local path or a proxy URI? */
50 if (!filter->scheme) {
51 if (url->scheme || url->hostname) {
52 return 0;
53 }
54 }
55 else {
56 /* The URI scheme must be present and identical except for case. */
57 if (!url->scheme || ap_cstr_casecmp(filter->scheme, url->scheme)) {
58 return 0;
59 }
60
61 /* If the filter hostname is null or empty it matches any hostname,
62 * if it begins with a "*" it matches the _end_ of the URI hostname
63 * excluding the "*", if it begins with a "." it matches the _end_
64 * of the URI * hostname including the ".", otherwise it must match
65 * the URI hostname exactly. */
66
67 if (filter->hostname && filter->hostname[0]) {
68 if (filter->hostname[0] == '.') {
69 const size_t fhostlen = strlen(filter->hostname);
70 const size_t uhostlen = url->hostname ? strlen(url->hostname) : 0;
71
72 if (fhostlen > uhostlen
73 || (url->hostname
74 && strcasecmp(filter->hostname,
75 url->hostname + uhostlen - fhostlen))) {
76 return 0;
77 }
78 }
79 else if (filter->hostname[0] == '*') {
80 const size_t fhostlen = strlen(filter->hostname + 1);
81 const size_t uhostlen = url->hostname ? strlen(url->hostname) : 0;
82
83 if (fhostlen > uhostlen
84 || (url->hostname
85 && strcasecmp(filter->hostname + 1,
86 url->hostname + uhostlen - fhostlen))) {
87 return 0;
88 }
89 }
90 else if (!url->hostname || strcasecmp(filter->hostname, url->hostname)) {
91 return 0;
92 }
93 }
94
95 /* If the filter port is empty it matches any URL port.
96 * If the filter or URL port are missing, or the URL port is
97 * empty, they default to the port for their scheme. */
98
99 if (!(filter->port_str && !filter->port_str[0])) {
100 /* NOTE: ap_port_of_scheme will return 0 if given NULL input */
101 const unsigned fport = filter->port_str ? filter->port
103 const unsigned uport = (url->port_str && url->port_str[0])
104 ? url->port : apr_uri_port_of_scheme(url->scheme);
105
106 if (fport != uport) {
107 return 0;
108 }
109 }
110 }
111
112 /* For HTTP caching purposes, an empty (NULL) path is equivalent to
113 * a single "/" path. RFCs 3986/2396
114 */
115 if (!path) {
116 if (*filter->path == '/' && pathlen == 1) {
117 return 1;
118 }
119 else {
120 return 0;
121 }
122 }
123
124 /* Url has met all of the filter conditions so far, determine
125 * if the paths match.
126 */
127 return !strncmp(filter->path, path, pathlen);
128}
129
131{
132 cache_server_conf *conf;
133
134 if (r->proxyreq == PROXYREQ_PROXY) {
135 return 1;
136 }
137
138 conf = ap_get_module_config(r->server->module_config, &cache_module);
139 if (conf->quick) {
140 return 1;
141 }
142
143 return 0;
144}
145
147 cache_provider_list *providers)
148{
149 /* Fetch from global config and add to the list. */
150 cache_provider *provider;
152 "0");
153 if (!provider) {
154 /* Log an error! */
155 }
156 else {
159 newp->provider_name = ent->type;
160 newp->provider = provider;
161
162 if (!providers) {
163 providers = newp;
164 }
165 else {
166 cache_provider_list *last = providers;
167
168 while (last->next) {
169 if (last->provider == provider) {
170 return providers;
171 }
172 last = last->next;
173 }
174 if (last->provider == provider) {
175 return providers;
176 }
177 last->next = newp;
178 }
179 }
180
181 return providers;
182}
183
185 cache_server_conf *conf)
186{
187 cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_module);
188 cache_provider_list *providers = NULL;
189 const char *path;
190 int i;
191
192 /* per directory cache disable */
193 if (dconf->disable) {
194 return NULL;
195 }
196
198
199 /* global cache disable */
200 for (i = 0; i < conf->cachedisable->nelts; i++) {
201 struct cache_disable *ent =
202 (struct cache_disable *)conf->cachedisable->elts;
204 &r->parsed_uri, path)) {
205 /* Stop searching now. */
206 return NULL;
207 }
208 }
209
210 /* loop through all the per directory cacheenable entries */
211 for (i = 0; i < dconf->cacheenable->nelts; i++) {
212 struct cache_enable *ent =
213 (struct cache_enable *)dconf->cacheenable->elts;
214 providers = get_provider(r, &ent[i], providers);
215 }
216
217 /* loop through all the global cacheenable entries */
218 for (i = 0; i < conf->cacheenable->nelts; i++) {
219 struct cache_enable *ent =
220 (struct cache_enable *)conf->cacheenable->elts;
222 &r->parsed_uri, path)) {
223 providers = get_provider(r, &ent[i], providers);
224 }
225 }
226
227 return providers;
228}
229
230
231/* do a HTTP/1.1 age calculation */
233 const apr_time_t age_value,
235{
239
241
242 /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */
243
244 apparent_age = MAX(0, info->response_time - info->date);
246 response_delay = info->response_time - info->request_time;
248 resident_time = now - info->response_time;
250
251 if (current_age < 0) {
252 current_age = 0;
253 }
254
256}
257
280 request_rec *r)
281{
283 const char *lockname;
284 const char *path;
285 char dir[5];
287 apr_finfo_t finfo;
289 void *dummy;
290
291 finfo.mtime = 0;
292
293 if (!conf || !conf->lock || !conf->lockpath) {
294 /* no locks configured, leave */
295 return APR_SUCCESS;
296 }
297
298 /* lock already obtained earlier? if so, success */
300 if (dummy) {
301 return APR_SUCCESS;
302 }
303
304 /* create the key if it doesn't exist */
305 if (!cache->key) {
307 /*
308 * Try to use the key of a possible open but stale cache
309 * entry if we have one.
310 */
311 if (cache->handle != NULL) {
312 h = cache->handle;
313 }
314 else {
315 h = cache->stale_handle;
316 }
317 if ((h != NULL) &&
318 (h->cache_obj != NULL) &&
319 (h->cache_obj->key != NULL)) {
320 cache->key = apr_pstrdup(r->pool, h->cache_obj->key);
321 }
322 else {
323 cache_generate_key(r, r->pool, &cache->key);
324 }
325 }
326
327 /* create a hashed filename from the key, and save it for later */
329
330 /* lock files represent discrete just-went-stale URLs "in flight", so
331 * we support a simple two level directory structure, more is overkill.
332 */
333 dir[0] = '/';
334 dir[1] = lockname[0];
335 dir[2] = '/';
336 dir[3] = lockname[1];
337 dir[4] = 0;
338
339 /* make the directories */
340 path = apr_pstrcat(r->pool, conf->lockpath, dir, NULL);
344 "Could not create a cache lock directory: %s",
345 path);
346 return status;
347 }
350
351 /* is an existing lock file too old? */
352 status = apr_stat(&finfo, lockname,
356 "Could not stat a cache lock file: %s",
357 lockname);
358 return status;
359 }
360 if ((status == APR_SUCCESS) && (((now - finfo.mtime) > conf->lockmaxage)
361 || (now < finfo.mtime))) {
363 "Cache lock file for '%s' too old, removing: %s",
364 r->uri, lockname);
366 }
367
368 /* try obtain a lock on the file */
371 APR_UREAD | APR_UWRITE, r->pool))) {
373 }
374 return status;
375
376}
377
392{
393 void *dummy;
394 const char *lockname;
395
396 if (!conf || !conf->lock || !conf->lockpath) {
397 /* no locks configured, leave */
398 return APR_SUCCESS;
399 }
400 if (bb) {
401 apr_bucket *e;
402 int eos_found = 0;
403 for (e = APR_BRIGADE_FIRST(bb);
404 e != APR_BRIGADE_SENTINEL(bb);
406 {
407 if (APR_BUCKET_IS_EOS(e)) {
408 eos_found = 1;
409 break;
410 }
411 }
412 if (!eos_found) {
413 /* no eos found in brigade, don't delete anything just yet,
414 * we are not done.
415 */
416 return APR_SUCCESS;
417 }
418 }
420 if (dummy) {
422 }
424 lockname = (const char *)dummy;
425 if (!lockname) {
426 char dir[5];
427
428 /* create the key if it doesn't exist */
429 if (!cache->key) {
430 cache_generate_key(r, r->pool, &cache->key);
431 }
432
433 /* create a hashed filename from the key, and save it for later */
435
436 /* lock files represent discrete just-went-stale URLs "in flight", so
437 * we support a simple two level directory structure, more is overkill.
438 */
439 dir[0] = '/';
440 dir[1] = lockname[0];
441 dir[2] = '/';
442 dir[3] = lockname[1];
443 dir[4] = 0;
444
445 lockname = apr_pstrcat(r->pool, conf->lockpath, dir, "/", lockname, NULL);
446 }
447 return apr_file_remove(lockname, r->pool);
448}
449
451{
452
453 cache_server_conf *conf =
455 &cache_module);
456
457 /*
458 * At this point, we may have data cached, but the request may have
459 * specified that cached data may not be used in a response.
460 *
461 * This is covered under RFC2616 section 14.9.4 (Cache Revalidation and
462 * Reload Controls).
463 *
464 * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache, or Pragma:
465 * no-cache. The server MUST NOT use a cached copy when responding to such
466 * a request.
467 */
468
469 /* This value comes from the client's initial request. */
470 if (!cache->control_in.parsed) {
471 const char *cc_req = cache_table_getm(r->pool, r->headers_in,
472 "Cache-Control");
473 const char *pragma = cache_table_getm(r->pool, r->headers_in, "Pragma");
474 ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in);
475 }
476
477 if (cache->control_in.no_cache) {
478
479 if (!conf->ignorecachecontrol) {
480 return 0;
481 }
482 else {
484 "Incoming request is asking for an uncached version of "
485 "%s, but we have been configured to ignore it and serve "
486 "cached content anyway", r->unparsed_uri);
487 }
488 }
489
490 return 1;
491}
492
494{
495
496 cache_server_conf *conf =
498 &cache_module);
499
500 /*
501 * At this point, we may have data cached, but the request may have
502 * specified that cached data may not be used in a response.
503 *
504 * - RFC2616 14.9.2 What May be Stored by Caches. If Cache-Control:
505 * no-store arrives, do not serve from or store to the cache.
506 */
507
508 /* This value comes from the client's initial request. */
509 if (!cache->control_in.parsed) {
510 const char *cc_req = cache_table_getm(r->pool, r->headers_in,
511 "Cache-Control");
512 const char *pragma = cache_table_getm(r->pool, r->headers_in, "Pragma");
513 ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in);
514 }
515
516 if (cache->control_in.no_store) {
517
518 if (!conf->ignorecachecontrol) {
519 /* We're not allowed to serve a cached copy */
520 return 0;
521 }
522 else {
524 "Incoming request is asking for a no-store version of "
525 "%s, but we have been configured to ignore it and serve "
526 "cached content anyway", r->unparsed_uri);
527 }
528 }
529
530 return 1;
531}
532
534 request_rec *r)
535{
539 const char *cc_req;
540 const char *pragma;
541 const char *agestr = NULL;
542 apr_time_t age_c = 0;
543 cache_info *info = &(h->cache_obj->info);
544 const char *warn_head;
545 cache_server_conf *conf =
547 &cache_module);
548
549 /*
550 * We now want to check if our cached data is still fresh. This depends
551 * on a few things, in this order:
552 *
553 * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache. no-cache
554 * in either the request or the cached response means that we must
555 * perform the request unconditionally, and ignore cached content. We
556 * should never reach here, but if we do, mark the content as stale,
557 * as this is the best we can do.
558 *
559 * - RFC2616 14.32 Pragma: no-cache This is treated the same as
560 * Cache-Control: no-cache.
561 *
562 * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate,
563 * proxy-revalidate if the max-stale request header exists, modify the
564 * stale calculations below so that an object can be at most <max-stale>
565 * seconds stale before we request a revalidation, _UNLESS_ a
566 * must-revalidate or proxy-revalidate cached response header exists to
567 * stop us doing this.
568 *
569 * - RFC2616 14.9.3 Cache-Control: s-maxage the origin server specifies the
570 * maximum age an object can be before it is considered stale. This
571 * directive has the effect of proxy|must revalidate, which in turn means
572 * simple ignore any max-stale setting.
573 *
574 * - RFC2616 14.9.4 Cache-Control: max-age this header can appear in both
575 * requests and responses. If both are specified, the smaller of the two
576 * takes priority.
577 *
578 * - RFC2616 14.21 Expires: if this request header exists in the cached
579 * entity, and it's value is in the past, it has expired.
580 *
581 */
582
583 /* This value comes from the client's initial request. */
584 cc_req = apr_table_get(r->headers_in, "Cache-Control");
585 pragma = apr_table_get(r->headers_in, "Pragma");
586
587 ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in);
588
589 if (cache->control_in.no_cache) {
590
591 if (!conf->ignorecachecontrol) {
592 /* Treat as stale, causing revalidation */
593 return 0;
594 }
595
597 "Incoming request is asking for a uncached version of "
598 "%s, but we have been configured to ignore it and "
599 "serve a cached response anyway",
600 r->unparsed_uri);
601 }
602
603 /* These come from the cached entity. */
604 if (h->cache_obj->info.control.no_cache
605 || h->cache_obj->info.control.invalidated) {
606 /*
607 * The cached entity contained Cache-Control: no-cache, or a
608 * no-cache with a header present, or a private with a header
609 * present, or the cached entity has been invalidated in the
610 * past, so treat as stale causing revalidation.
611 */
612 return 0;
613 }
614
615 if ((agestr = apr_table_get(h->resp_hdrs, "Age"))) {
616 char *endp;
618 if (!apr_strtoff(&offt, agestr, &endp, 10)
619 && endp > agestr && !*endp) {
620 age_c = offt;
621 }
622 }
623
624 /* calculate age of object */
626
627 /* extract s-maxage */
628 smaxage = h->cache_obj->info.control.s_maxage_value;
629
630 /* extract max-age from request */
631 maxage_req = -1;
632 if (!conf->ignorecachecontrol) {
633 maxage_req = cache->control_in.max_age_value;
634 }
635
636 /*
637 * extract max-age from response, if both s-maxage and max-age, s-maxage
638 * takes priority
639 */
640 if (smaxage != -1) {
642 }
643 else {
644 maxage_cresp = h->cache_obj->info.control.max_age_value;
645 }
646
647 /*
648 * if both maxage request and response, the smaller one takes priority
649 */
650 if (maxage_req == -1) {
651 maxage = maxage_cresp;
652 }
653 else if (maxage_cresp == -1) {
654 maxage = maxage_req;
655 }
656 else {
657 maxage = MIN(maxage_req, maxage_cresp);
658 }
659
660 /* extract max-stale */
661 if (cache->control_in.max_stale) {
662 if (cache->control_in.max_stale_value != -1) {
663 maxstale = cache->control_in.max_stale_value;
664 }
665 else {
666 /*
667 * If no value is assigned to max-stale, then the client is willing
668 * to accept a stale response of any age (RFC2616 14.9.3). We will
669 * set it to one year in this case as this situation is somewhat
670 * similar to a "never expires" Expires header (RFC2616 14.21)
671 * which is set to a date one year from the time the response is
672 * sent in this case.
673 */
674 maxstale = APR_INT64_C(86400*365);
675 }
676 }
677 else {
678 maxstale = 0;
679 }
680
681 /* extract min-fresh */
682 if (!conf->ignorecachecontrol && cache->control_in.min_fresh) {
683 minfresh = cache->control_in.min_fresh_value;
684 }
685 else {
686 minfresh = 0;
687 }
688
689 /* override maxstale if must-revalidate, proxy-revalidate or s-maxage */
690 if (maxstale && (h->cache_obj->info.control.must_revalidate
691 || h->cache_obj->info.control.proxy_revalidate || smaxage != -1)) {
692 maxstale = 0;
693 }
694
695 /* handle expiration */
696 if (((maxage != -1) && (age < (maxage + maxstale - minfresh))) ||
697 ((smaxage == -1) && (maxage == -1) &&
698 (info->expire != APR_DATE_BAD) &&
699 (age < (apr_time_sec(info->expire - info->date) + maxstale - minfresh)))) {
700
701 warn_head = apr_table_get(h->resp_hdrs, "Warning");
702
703 /* it's fresh darlings... */
704 /* set age header on response */
705 apr_table_set(h->resp_hdrs, "Age",
706 apr_psprintf(r->pool, "%lu", (unsigned long)age));
707
708 /* add warning if maxstale overrode freshness calculation */
709 if (!(((maxage != -1) && age < maxage) ||
710 (info->expire != APR_DATE_BAD &&
711 (apr_time_sec(info->expire - info->date)) > age))) {
712 /* make sure we don't stomp on a previous warning */
713 if ((warn_head == NULL) ||
714 ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
715 apr_table_mergen(h->resp_hdrs, "Warning",
716 "110 Response is stale");
717 }
718 }
719
720 /*
721 * If none of Expires, Cache-Control: max-age, or Cache-Control:
722 * s-maxage appears in the response, and the response header age
723 * calculated is more than 24 hours add the warning 113
724 */
725 if ((maxage_cresp == -1) && (smaxage == -1) && (apr_table_get(
726 h->resp_hdrs, "Expires") == NULL) && (age > 86400)) {
727
728 /* Make sure we don't stomp on a previous warning, and don't dup
729 * a 113 marning that is already present. Also, make sure to add
730 * the new warning to the correct *headers_out location.
731 */
732 if ((warn_head == NULL) ||
733 ((warn_head != NULL) && (ap_strstr_c(warn_head, "113") == NULL))) {
734 apr_table_mergen(h->resp_hdrs, "Warning",
735 "113 Heuristic expiration");
736 }
737 }
738 return 1; /* Cache object is fresh (enough) */
739 }
740
741 /*
742 * At this point we are stale, but: if we are under load, we may let
743 * a significant number of stale requests through before the first
744 * stale request successfully revalidates itself, causing a sudden
745 * unexpected thundering herd which in turn brings angst and drama.
746 *
747 * So.
748 *
749 * We want the first stale request to go through as normal. But the
750 * second and subsequent request, we must pretend to be fresh until
751 * the first request comes back with either new content or confirmation
752 * that the stale content is still fresh.
753 *
754 * To achieve this, we create a very simple file based lock based on
755 * the key of the cached object. We attempt to open the lock file with
756 * exclusive write access. If we succeed, woohoo! we're first, and we
757 * follow the stale path to the backend server. If we fail, oh well,
758 * we follow the fresh path, and avoid being a thundering herd.
759 *
760 * The lock lives only as long as the stale request that went on ahead.
761 * If the request succeeds, the lock is deleted. If the request fails,
762 * the lock is deleted, and another request gets to make a new lock
763 * and try again.
764 *
765 * At any time, a request marked "no-cache" will force a refresh,
766 * ignoring the lock, ensuring an extended lockout is impossible.
767 *
768 * A lock that exceeds a maximum age will be deleted, and another
769 * request gets to make a new lock and try again.
770 */
771 status = cache_try_lock(conf, cache, r);
772 if (APR_SUCCESS == status) {
773 /* we obtained a lock, follow the stale path */
775 "Cache lock obtained for stale cached URL, "
776 "revalidating entry: %s",
777 r->unparsed_uri);
778 return 0;
779 }
780 else if (APR_STATUS_IS_EEXIST(status)) {
781 /* lock already exists, return stale data anyway, with a warning */
783 "Cache already locked for stale cached URL, "
784 "pretend it is fresh: %s",
785 r->unparsed_uri);
786
787 /* make sure we don't stomp on a previous warning */
788 warn_head = apr_table_get(h->resp_hdrs, "Warning");
789 if ((warn_head == NULL) ||
790 ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
791 apr_table_mergen(h->resp_hdrs, "Warning",
792 "110 Response is stale");
793 }
794
795 return 1;
796 }
797 else {
798 /* some other error occurred, just treat the object as stale */
800 "Attempt to obtain a cache lock for stale "
801 "cached URL failed, revalidating entry anyway: %s",
802 r->unparsed_uri);
803 return 0;
804 }
805
806}
807
808/* return each comma separated token, one at a time */
809CACHE_DECLARE(const char *)ap_cache_tokstr(apr_pool_t *p, const char *list,
810 const char **str)
811{
813 const char *s;
814
815 s = ap_strchr_c(list, ',');
816 if (s != NULL) {
817 i = s - list;
818 do
819 s++;
820 while (apr_isspace(*s))
821 ; /* noop */
822 }
823 else
824 i = strlen(list);
825
826 while (i > 0 && apr_isspace(list[i - 1]))
827 i--;
828
829 *str = s;
830 if (i)
831 return apr_pstrmemdup(p, list, i);
832 else
833 return NULL;
834}
835
836/*
837 * Converts apr_time_t expressed as hex digits to
838 * a true apr_time_t.
839 */
841{
842 int i, ch;
843 apr_time_t j;
844 for (i = 0, j = 0; i < sizeof(j) * 2; i++) {
845 ch = x[i];
846 j <<= 4;
847 if (apr_isdigit(ch))
848 j |= ch - '0';
849 else if (apr_isupper(ch))
850 j |= ch - ('A' - 10);
851 else
852 j |= ch - ('a' - 10);
853 }
854 return j;
855}
856
857/*
858 * Converts apr_time_t to apr_time_t expressed as hex digits.
859 */
861{
862 int i, ch;
863
864 for (i = (sizeof(j) * 2)-1; i >= 0; i--) {
865 ch = (int)(j & 0xF);
866 j >>= 4;
867 if (ch >= 10)
868 y[i] = ch + ('A' - 10);
869 else
870 y[i] = ch + '0';
871 }
872 y[sizeof(j) * 2] = '\0';
873}
874
875static void cache_hash(const char *it, char *val, int ndepth, int nlength)
876{
878 unsigned char digest[16];
879 char tmp[22];
880 int i, k, d;
881 unsigned int x;
882 static const char enc_table[64] =
883 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
884
886 apr_md5_update(&context, (const unsigned char *) it, strlen(it));
888
889 /* encode 128 bits as 22 characters, using a modified uuencoding
890 * the encoding is 3 bytes -> 4 characters* i.e. 128 bits is
891 * 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
892 */
893 for (i = 0, k = 0; i < 15; i += 3) {
894 x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
895 tmp[k++] = enc_table[x >> 18];
896 tmp[k++] = enc_table[(x >> 12) & 0x3f];
897 tmp[k++] = enc_table[(x >> 6) & 0x3f];
898 tmp[k++] = enc_table[x & 0x3f];
899 }
900
901 /* one byte left */
902 x = digest[15];
903 tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */
904 tmp[k++] = enc_table[(x << 4) & 0x3f];
905
906 /* now split into directory levels */
907 for (i = k = d = 0; d < ndepth; ++d) {
908 memcpy(&val[i], &tmp[k], nlength);
909 k += nlength;
910 val[i + nlength] = '/';
911 i += nlength + 1;
912 }
913 memcpy(&val[i], &tmp[k], 22 - k);
914 val[i + 22 - k] = '\0';
915}
916
918 int dirlength, const char *name)
919{
920 char hashfile[66];
921 cache_hash(name, hashfile, dirlevels, dirlength);
922 return apr_pstrdup(p, hashfile);
923}
924
929char *cache_strqtok(char *str, const char *sep, char **last)
930{
931 char *token;
932 int quoted = 0;
933
934 if (!str) { /* subsequent call */
935 str = *last; /* start where we left off */
936 }
937
938 if (!str) { /* no more tokens */
939 return NULL;
940 }
941
942 /* skip characters in sep (will terminate at '\0') */
943 while (*str && ap_strchr_c(sep, *str)) {
944 ++str;
945 }
946
947 if (!*str) { /* no more tokens */
948 return NULL;
949 }
950
951 token = str;
952
953 /* skip valid token characters to terminate token and
954 * prepare for the next call (will terminate at '\0)
955 * on the way, ignore all quoted strings, and within
956 * quoted strings, escaped characters.
957 */
958 *last = token;
959 while (**last) {
960 if (!quoted) {
961 if (**last == '\"' && !ap_strchr_c(sep, '\"')) {
962 quoted = 1;
963 ++*last;
964 }
965 else if (!ap_strchr_c(sep, **last)) {
966 ++*last;
967 }
968 else {
969 break;
970 }
971 }
972 else {
973 if (**last == '\"') {
974 quoted = 0;
975 ++*last;
976 }
977 else if (**last == '\\') {
978 ++*last;
979 if (**last) {
980 ++*last;
981 }
982 }
983 else {
984 ++*last;
985 }
986 }
987 }
988
989 if (**last) {
990 **last = '\0';
991 ++*last;
992 }
993
994 return token;
995}
996
1003 const char *cc_header, const char *pragma_header, apr_table_t *headers)
1004{
1005 char *last;
1006
1007 if (cc->parsed) {
1008 return cc->cache_control || cc->pragma;
1009 }
1010
1011 cc->parsed = 1;
1012 cc->max_age_value = -1;
1013 cc->max_stale_value = -1;
1014 cc->min_fresh_value = -1;
1015 cc->s_maxage_value = -1;
1016
1017 if (pragma_header) {
1018 char *header = apr_pstrdup(r->pool, pragma_header);
1019 const char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
1020 while (token) {
1021 if (!ap_cstr_casecmp(token, "no-cache")) {
1022 cc->no_cache = 1;
1023 }
1025 }
1026 cc->pragma = 1;
1027 }
1028
1029 if (cc_header) {
1030 char *endp;
1032 char *header = apr_pstrdup(r->pool, cc_header);
1033 const char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
1034 while (token) {
1035 switch (token[0]) {
1036 case 'n':
1037 case 'N': {
1038 if (!ap_cstr_casecmpn(token, "no-cache", 8)) {
1039 if (token[8] == '=') {
1040 cc->no_cache_header = 1;
1041 }
1042 else if (!token[8]) {
1043 cc->no_cache = 1;
1044 }
1045 }
1046 else if (!ap_cstr_casecmp(token, "no-store")) {
1047 cc->no_store = 1;
1048 }
1049 else if (!ap_cstr_casecmp(token, "no-transform")) {
1050 cc->no_transform = 1;
1051 }
1052 break;
1053 }
1054 case 'm':
1055 case 'M': {
1056 if (!ap_cstr_casecmpn(token, "max-age", 7)) {
1057 if (token[7] == '='
1058 && !apr_strtoff(&offt, token + 8, &endp, 10)
1059 && endp > token + 8 && !*endp) {
1060 cc->max_age = 1;
1061 cc->max_age_value = offt;
1062 }
1063 }
1064 else if (!ap_cstr_casecmp(token, "must-revalidate")) {
1065 cc->must_revalidate = 1;
1066 }
1067 else if (!ap_cstr_casecmpn(token, "max-stale", 9)) {
1068 if (token[9] == '='
1069 && !apr_strtoff(&offt, token + 10, &endp, 10)
1070 && endp > token + 10 && !*endp) {
1071 cc->max_stale = 1;
1072 cc->max_stale_value = offt;
1073 }
1074 else if (!token[9]) {
1075 cc->max_stale = 1;
1076 cc->max_stale_value = -1;
1077 }
1078 }
1079 else if (!ap_cstr_casecmpn(token, "min-fresh", 9)) {
1080 if (token[9] == '='
1081 && !apr_strtoff(&offt, token + 10, &endp, 10)
1082 && endp > token + 10 && !*endp) {
1083 cc->min_fresh = 1;
1084 cc->min_fresh_value = offt;
1085 }
1086 }
1087 break;
1088 }
1089 case 'o':
1090 case 'O': {
1091 if (!ap_cstr_casecmp(token, "only-if-cached")) {
1092 cc->only_if_cached = 1;
1093 }
1094 break;
1095 }
1096 case 'p':
1097 case 'P': {
1098 if (!ap_cstr_casecmp(token, "public")) {
1099 cc->public = 1;
1100 }
1101 else if (!ap_cstr_casecmpn(token, "private", 7)) {
1102 if (token[7] == '=') {
1103 cc->private_header = 1;
1104 }
1105 else if (!token[7]) {
1106 cc->private = 1;
1107 }
1108 }
1109 else if (!ap_cstr_casecmp(token, "proxy-revalidate")) {
1110 cc->proxy_revalidate = 1;
1111 }
1112 break;
1113 }
1114 case 's':
1115 case 'S': {
1116 if (!ap_cstr_casecmpn(token, "s-maxage", 8)) {
1117 if (token[8] == '='
1118 && !apr_strtoff(&offt, token + 9, &endp, 10)
1119 && endp > token + 9 && !*endp) {
1120 cc->s_maxage = 1;
1121 cc->s_maxage_value = offt;
1122 }
1123 }
1124 break;
1125 }
1126 }
1128 }
1129 cc->cache_control = 1;
1130 }
1131
1132 return (cc_header != NULL || pragma_header != NULL);
1133}
1134
1140 apr_table_t *headers)
1141{
1142 char *last, *slast;
1143 int found = 0;
1144
1145 if (cc_header) {
1146 char *header = apr_pstrdup(r->pool, cc_header);
1147 char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
1148 while (token) {
1149 switch (token[0]) {
1150 case 'n':
1151 case 'N': {
1152 if (!ap_cstr_casecmpn(token, "no-cache", 8)) {
1153 if (token[8] == '=') {
1154 const char *header = cache_strqtok(token + 9,
1155 CACHE_SEPARATOR "\"", &slast);
1156 while (header) {
1157 apr_table_unset(headers, header);
1158 header = cache_strqtok(NULL, CACHE_SEPARATOR "\"",
1159 &slast);
1160 }
1161 found = 1;
1162 }
1163 }
1164 break;
1165 }
1166 case 'p':
1167 case 'P': {
1168 if (!ap_cstr_casecmpn(token, "private", 7)) {
1169 if (token[7] == '=') {
1170 const char *header = cache_strqtok(token + 8,
1171 CACHE_SEPARATOR "\"", &slast);
1172 while (header) {
1173 apr_table_unset(headers, header);
1174 header = cache_strqtok(NULL, CACHE_SEPARATOR "\"",
1175 &slast);
1176 }
1177 found = 1;
1178 }
1179 }
1180 break;
1181 }
1182 }
1184 }
1185 }
1186
1187 return found;
1188}
1189
1190/*
1191 * Create a new table consisting of those elements from an
1192 * headers table that are allowed to be stored in a cache.
1193 */
1195 apr_table_t *t,
1196 server_rec *s)
1197{
1198 cache_server_conf *conf;
1199 char **header;
1200 int i;
1201 apr_table_t *headers_out;
1202
1203 /* Short circuit the common case that there are not
1204 * (yet) any headers populated.
1205 */
1206 if (t == NULL) {
1207 return apr_table_make(pool, 10);
1208 };
1209
1210 /* Make a copy of the headers, and remove from
1211 * the copy any hop-by-hop headers, as defined in Section
1212 * 13.5.1 of RFC 2616
1213 */
1214 headers_out = apr_table_copy(pool, t);
1215
1216 apr_table_unset(headers_out, "Connection");
1217 apr_table_unset(headers_out, "Keep-Alive");
1218 apr_table_unset(headers_out, "Proxy-Authenticate");
1219 apr_table_unset(headers_out, "Proxy-Authorization");
1220 apr_table_unset(headers_out, "TE");
1221 apr_table_unset(headers_out, "Trailers");
1222 apr_table_unset(headers_out, "Transfer-Encoding");
1223 apr_table_unset(headers_out, "Upgrade");
1224
1225 conf = (cache_server_conf *)ap_get_module_config(s->module_config,
1226 &cache_module);
1227
1228 /* Remove the user defined headers set with CacheIgnoreHeaders.
1229 * This may break RFC 2616 compliance on behalf of the administrator.
1230 */
1231 header = (char **)conf->ignore_headers->elts;
1232 for (i = 0; i < conf->ignore_headers->nelts; i++) {
1233 apr_table_unset(headers_out, header[i]);
1234 }
1235 return headers_out;
1236}
1237
1238/*
1239 * Create a new table consisting of those elements from an input
1240 * headers table that are allowed to be stored in a cache.
1241 */
1246
1247/*
1248 * Create a new table consisting of those elements from an output
1249 * headers table that are allowed to be stored in a cache;
1250 * ensure there is a content type and capture any errors.
1251 */
1253{
1254 apr_table_t *headers_out;
1255
1256 headers_out = ap_cache_cacheable_headers(r->pool,
1258 r->server);
1259
1261 cache_table_getm(r->pool, headers_out, "Cache-Control"),
1262 headers_out);
1263
1264 return headers_out;
1265}
1266
1268{
1269 apr_table_t *headers_out;
1270
1271 headers_out = apr_table_overlay(r->pool, r->headers_out,
1273
1274 if (r->content_type
1275 && !apr_table_get(headers_out, "Content-Type")) {
1276 const char *ctype = ap_make_content_type(r, r->content_type);
1277 if (ctype) {
1278 apr_table_setn(headers_out, "Content-Type", ctype);
1279 }
1280 }
1281
1282 if (r->content_encoding
1283 && !apr_table_get(headers_out, "Content-Encoding")) {
1284 apr_table_setn(headers_out, "Content-Encoding",
1286 }
1287
1288 return headers_out;
1289}
1290
1297
1298static int cache_table_getm_do(void *v, const char *key, const char *val)
1299{
1301
1302 if (!state->first) {
1309 state->first = val;
1310 }
1311 else {
1312 const char **elt;
1313 if (!state->merged) {
1314 state->merged = apr_array_make(state->p, 10, sizeof(const char *));
1315 elt = apr_array_push(state->merged);
1316 *elt = state->first;
1317 }
1318 elt = apr_array_push(state->merged);
1319 *elt = val;
1320 }
1321 return 1;
1322}
1323
1325 const char *key)
1326{
1327 cache_table_getm_t state;
1328
1329 state.p = p;
1330 state.first = NULL;
1331 state.merged = NULL;
1332
1334
1335 if (!state.first) {
1336 return NULL;
1337 }
1338 else if (!state.merged) {
1339 return state.first;
1340 }
1341 else {
1342 return apr_array_pstrcat(p, state.merged, ',');
1343 }
1344}
Apache Provider API.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
static int cache_table_getm_do(void *v, const char *key, const char *val)
static int cache_control_remove(request_rec *r, const char *cc_header, apr_table_t *headers)
static void cache_hash(const char *it, char *val, int ndepth, int nlength)
Definition cache_util.c:875
apr_OFN_ap_cache_generate_key_t * cache_generate_key
Definition mod_cache.c:23
static int uri_meets_conditions(const apr_uri_t *filter, const apr_size_t pathlen, const apr_uri_t *url, const char *path)
Definition cache_util.c:33
static cache_provider_list * get_provider(request_rec *r, struct cache_enable *ent, cache_provider_list *providers)
Definition cache_util.c:146
Cache Storage Functions.
return found
Definition core.c:2840
apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
#define APLOG_USE_MODULE(foo)
#define ap_get_module_config(v, m)
request_rec int int apr_table_t const char * path
request_rec * r
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_DEBUG
Definition http_log.h:71
apr_md5_ctx_t * context
Definition util_md5.h:58
const char * ap_make_content_type(request_rec *r, const char *type)
Definition protocol.c:110
void * ap_lookup_provider(const char *provider_group, const char *provider_name, const char *provider_version)
Definition provider.c:99
void * dummy
Definition http_vhost.h:62
#define APR_STATUS_IS_EEXIST(s)
Definition apr_errno.h:1233
#define APR_STATUS_IS_ENOENT(s)
Definition apr_errno.h:1246
#define APR_BUCKET_NEXT(e)
apr_bucket * e
#define APR_BRIGADE_SENTINEL(b)
#define APR_BUCKET_IS_EOS(e)
#define APR_BRIGADE_FIRST(b)
#define APR_DATE_BAD
Definition apr_date.h:43
const char * url
Definition apr_escape.h:120
#define APR_OPTIONAL_FN_TYPE(name)
cache_provider_list * cache_get_providers(request_rec *r, cache_server_conf *conf)
Definition cache_util.c:184
char * cache_strqtok(char *str, const char *sep, char **last)
Definition cache_util.c:929
#define CACHE_LOCKFILE_KEY
Definition cache_util.h:100
#define CACHE_SEPARATOR
Definition cache_util.h:102
#define MIN(a, b)
Definition cache_util.h:82
apr_table_t * cache_merge_headers_out(request_rec *r)
int ap_cache_check_no_cache(cache_request_rec *cache, request_rec *r)
Definition cache_util.c:450
const char * cache_table_getm(apr_pool_t *p, const apr_table_t *t, const char *key)
apr_status_t cache_remove_lock(cache_server_conf *conf, cache_request_rec *cache, request_rec *r, apr_bucket_brigade *bb)
Definition cache_util.c:390
int cache_check_freshness(cache_handle_t *h, cache_request_rec *cache, request_rec *r)
Definition cache_util.c:533
#define CACHE_LOCKNAME_KEY
Definition cache_util.h:99
int ap_cache_check_no_store(cache_request_rec *cache, request_rec *r)
Definition cache_util.c:493
int cache_use_early_url(request_rec *r)
Definition cache_util.c:130
#define MAX(a, b)
Definition cache_util.h:79
apr_status_t cache_try_lock(cache_server_conf *conf, cache_request_rec *cache, request_rec *r)
Definition cache_util.c:279
int ap_cache_control(request_rec *r, cache_control_t *cc, const char *cc_header, const char *pragma_header, apr_table_t *headers)
const char * ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str)
Definition cache_util.c:809
#define CACHE_PROVIDER_GROUP
Definition mod_cache.h:97
char * ap_cache_generate_name(apr_pool_t *p, int dirlevels, int dirlength, const char *name)
Definition cache_util.c:917
apr_table_t * ap_cache_cacheable_headers(apr_pool_t *pool, apr_table_t *t, server_rec *s)
apr_time_t ap_cache_hex2usec(const char *x)
Definition cache_util.c:840
#define CACHE_DECLARE(type)
Definition mod_cache.h:40
void ap_cache_usec2hex(apr_time_t j, char *y)
Definition cache_util.c:860
apr_int64_t ap_cache_current_age(cache_info *info, const apr_time_t age_value, apr_time_t now)
Definition cache_util.c:232
apr_table_t * ap_cache_cacheable_headers_in(request_rec *r)
apr_table_t * ap_cache_cacheable_headers_out(request_rec *r)
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
#define ap_strstr_c(s, c)
Definition httpd.h:2361
#define ap_strchr_c(s, c)
Definition httpd.h:2353
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define PROXYREQ_PROXY
Definition httpd.h:1134
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
const apr_array_header_t * list
Definition apr_cstr.h:105
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isupper(c)
Definition apr_lib.h:227
#define apr_isdigit(c)
Definition apr_lib.h:209
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
#define APR_EXCL
Definition apr_file_io.h:99
#define APR_WRITE
Definition apr_file_io.h:94
#define APR_CREATE
Definition apr_file_io.h:95
#define APR_DELONCLOSE
#define APR_UEXECUTE
#define APR_UWRITE
#define APR_UREAD
#define APR_FINFO_MTIME
#define APR_FINFO_NLINK
int strcasecmp(const char *a, const char *b)
const apr_hash_t * h
Definition apr_hash.h:97
apr_interval_time_t t
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_dir_t * dir
const char * sep
const char char ** last
const char * s
Definition apr_strings.h:95
int int status
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_sec(time)
Definition apr_time.h:63
#define apr_time_from_sec(sec)
Definition apr_time.h:78
apr_pool_t * p
Definition md_event.c:32
Main include file for the Apache Transparent Cache.
static apr_status_t age(proxy_balancer *balancer, server_rec *s)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
apr_time_t mtime
char * scheme
Definition apr_uri.h:87
char * path
Definition apr_uri.h:99
apr_port_t port
Definition apr_uri.h:109
char * hostname
Definition apr_uri.h:95
char * port_str
Definition apr_uri.h:97
unsigned int parsed
unsigned int no_cache_header
unsigned int pragma
unsigned int no_cache
unsigned int max_stale
unsigned int max_age
unsigned int no_store
unsigned int no_transform
unsigned int min_fresh
unsigned int cache_control
unsigned int only_if_cached
apr_array_header_t * cacheenable
Definition cache_util.h:169
unsigned int disable
Definition cache_util.h:171
apr_size_t pathlen
Definition cache_util.h:116
apr_size_t pathlen
Definition cache_util.h:111
cache_provider_list * next
Definition cache_util.h:206
unsigned int ignorecachecontrol
Definition cache_util.h:131
apr_time_t lockmaxage
Definition cache_util.h:128
apr_array_header_t * ignore_headers
Definition cache_util.h:124
apr_array_header_t * cachedisable
Definition cache_util.h:122
unsigned int quick
Definition cache_util.h:135
apr_array_header_t * cacheenable
Definition cache_util.h:121
unsigned int lock
Definition cache_util.h:137
const char * lockpath
Definition cache_util.h:127
const char * first
apr_array_header_t * merged
A structure that represents the current request.
Definition httpd.h:845
char * uri
Definition httpd.h:1016
const char * content_type
Definition httpd.h:992
apr_pool_t * pool
Definition httpd.h:847
apr_time_t request_time
Definition httpd.h:886
apr_uri_t parsed_uri
Definition httpd.h:1092
char * unparsed_uri
Definition httpd.h:1014
int proxyreq
Definition httpd.h:873
apr_table_t * err_headers_out
Definition httpd.h:981
apr_table_t * headers_in
Definition httpd.h:976
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * content_encoding
Definition httpd.h:997
apr_table_t * headers_out
Definition httpd.h:978
A structure to store information for each virtual server.
Definition httpd.h:1322
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
const char * digest
Definition testmd5.c:30
static apr_time_t now
Definition testtime.c:33
#define str
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray
INT info