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_storage.h" 20#include "cache_util.h" 21 22APLOG_USE_MODULE(cache); 23 24extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key; 25 26extern module AP_MODULE_DECLARE_DATA cache_module; 27 28/* -------------------------------------------------------------- */ 29 30/* 31 * delete all URL entities from the cache 32 * 33 */ 34int cache_remove_url(cache_request_rec *cache, request_rec *r) 35{ 36 cache_provider_list *list; 37 cache_handle_t *h; 38 39 list = cache->providers; 40 41 /* Remove the stale cache entry if present. If not, we're 42 * being called from outside of a request; remove the 43 * non-stale handle. 44 */ 45 h = cache->stale_handle ? cache->stale_handle : cache->handle; 46 if (!h) { 47 return OK; 48 } 49 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00691) 50 "cache: Removing url %s from the cache", h->cache_obj->key); 51 52 /* for each specified cache type, delete the URL */ 53 while(list) { 54 list->provider->remove_url(h, r); 55 list = list->next; 56 } 57 return OK; 58} 59 60 61/* 62 * create a new URL entity in the cache 63 * 64 * It is possible to store more than once entity per URL. This 65 * function will always create a new entity, regardless of whether 66 * other entities already exist for the same URL. 67 * 68 * The size of the entity is provided so that a cache module can 69 * decide whether or not it wants to cache this particular entity. 70 * If the size is unknown, a size of -1 should be set. 71 */ 72int cache_create_entity(cache_request_rec *cache, request_rec *r, 73 apr_off_t size, apr_bucket_brigade *in) 74{ 75 cache_provider_list *list; 76 cache_handle_t *h = apr_pcalloc(r->pool, sizeof(cache_handle_t)); 77 apr_status_t rv; 78 79 if (!cache) { 80 /* This should never happen */ 81 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00692) 82 "cache: No cache request information available for key" 83 " generation"); 84 return APR_EGENERAL; 85 } 86 87 if (!cache->key) { 88 rv = cache_generate_key(r, r->pool, &cache->key); 89 if (rv != APR_SUCCESS) { 90 return rv; 91 } 92 } 93 94 list = cache->providers; 95 /* for each specified cache type, delete the URL */ 96 while (list) { 97 switch (rv = list->provider->create_entity(h, r, cache->key, size, in)) { 98 case OK: { 99 cache->handle = h; 100 cache->provider = list->provider; 101 cache->provider_name = list->provider_name; 102 return OK; 103 } 104 case DECLINED: { 105 list = list->next; 106 continue; 107 } 108 default: { 109 return rv; 110 } 111 } 112 } 113 return DECLINED; 114} 115 116static int filter_header_do(void *v, const char *key, const char *val) 117{ 118 if ((*key == 'W' || *key == 'w') && !strcasecmp(key, "Warning") 119 && *val == '1') { 120 /* any stored Warning headers with warn-code 1xx (see section 121 * 14.46) MUST be deleted from the cache entry and the forwarded 122 * response. 123 */ 124 } 125 else { 126 apr_table_addn(v, key, val); 127 } 128 return 1; 129} 130static int remove_header_do(void *v, const char *key, const char *val) 131{ 132 if ((*key == 'W' || *key == 'w') && !strcasecmp(key, "Warning")) { 133 /* any stored Warning headers with warn-code 2xx MUST be retained 134 * in the cache entry and the forwarded response. 135 */ 136 } 137 else { 138 apr_table_unset(v, key); 139 } 140 return 1; 141} 142static int add_header_do(void *v, const char *key, const char *val) 143{ 144 apr_table_addn(v, key, val); 145 return 1; 146} 147 148/** 149 * Take two sets of headers, sandwich them together, and apply the result to 150 * r->headers_out. 151 * 152 * To complicate this, a header may be duplicated in either table. Should a 153 * header exist in the top table, all matching headers will be removed from 154 * the bottom table before the headers are combined. The Warning headers are 155 * handled specially. Warnings are added rather than being replaced, while 156 * in the case of revalidation 1xx Warnings are stripped. 157 * 158 * The Content-Type and Last-Modified headers are then re-parsed and inserted 159 * into the request. 160 */ 161void cache_accept_headers(cache_handle_t *h, request_rec *r, apr_table_t *top, 162 apr_table_t *bottom, int revalidation) 163{ 164 const char *v; 165 166 if (revalidation) { 167 r->headers_out = apr_table_make(r->pool, 10); 168 apr_table_do(filter_header_do, r->headers_out, bottom, NULL); 169 } 170 else if (r->headers_out != bottom) { 171 r->headers_out = apr_table_copy(r->pool, bottom); 172 } 173 apr_table_do(remove_header_do, r->headers_out, top, NULL); 174 apr_table_do(add_header_do, r->headers_out, top, NULL); 175 176 v = apr_table_get(r->headers_out, "Content-Type"); 177 if (v) { 178 ap_set_content_type(r, v); 179 /* 180 * Also unset possible Content-Type headers in r->headers_out and 181 * r->err_headers_out as they may be different to what we have received 182 * from the cache. 183 * Actually they are not needed as r->content_type set by 184 * ap_set_content_type above will be used in the store_headers functions 185 * of the storage providers as a fallback and the HTTP_HEADER filter 186 * does overwrite the Content-Type header with r->content_type anyway. 187 */ 188 apr_table_unset(r->headers_out, "Content-Type"); 189 apr_table_unset(r->err_headers_out, "Content-Type"); 190 } 191 192 /* If the cache gave us a Last-Modified header, we can't just 193 * pass it on blindly because of restrictions on future values. 194 */ 195 v = apr_table_get(r->headers_out, "Last-Modified"); 196 if (v) { 197 ap_update_mtime(r, apr_date_parse_http(v)); 198 ap_set_last_modified(r); 199 } 200 201} 202 203/* 204 * select a specific URL entity in the cache 205 * 206 * It is possible to store more than one entity per URL. Content 207 * negotiation is used to select an entity. Once an entity is 208 * selected, details of it are stored in the per request 209 * config to save time when serving the request later. 210 * 211 * This function returns OK if successful, DECLINED if no 212 * cached entity fits the bill. 213 */ 214int cache_select(cache_request_rec *cache, request_rec *r) 215{ 216 cache_provider_list *list; 217 apr_status_t rv; 218 cache_handle_t *h; 219 220 if (!cache) { 221 /* This should never happen */ 222 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00693) 223 "cache: No cache request information available for key" 224 " generation"); 225 return DECLINED; 226 } 227 228 /* if no-cache, we can't serve from the cache, but we may store to the 229 * cache. 230 */ 231 if (!ap_cache_check_no_cache(cache, r)) { 232 return DECLINED; 233 } 234 235 if (!cache->key) { 236 rv = cache_generate_key(r, r->pool, &cache->key); 237 if (rv != APR_SUCCESS) { 238 return DECLINED; 239 } 240 } 241 242 /* go through the cache types till we get a match */ 243 h = apr_palloc(r->pool, sizeof(cache_handle_t)); 244 245 list = cache->providers; 246 247 while (list) { 248 switch ((rv = list->provider->open_entity(h, r, cache->key))) { 249 case OK: { 250 char *vary = NULL; 251 int mismatch = 0; 252 char *last = NULL; 253 254 if (list->provider->recall_headers(h, r) != APR_SUCCESS) { 255 /* try again with next cache type */ 256 list = list->next; 257 continue; 258 } 259 260 /* 261 * Check Content-Negotiation - Vary 262 * 263 * At this point we need to make sure that the object we found in 264 * the cache is the same object that would be delivered to the 265 * client, when the effects of content negotiation are taken into 266 * effect. 267 * 268 * In plain english, we want to make sure that a language-negotiated 269 * document in one language is not given to a client asking for a 270 * language negotiated document in a different language by mistake. 271 * 272 * This code makes the assumption that the storage manager will 273 * cache the req_hdrs if the response contains a Vary 274 * header. 275 * 276 * RFC2616 13.6 and 14.44 describe the Vary mechanism. 277 */ 278 vary = cache_strqtok( 279 apr_pstrdup(r->pool, 280 cache_table_getm(r->pool, h->resp_hdrs, "Vary")), 281 CACHE_SEPARATOR, &last); 282 while (vary) { 283 const char *h1, *h2; 284 285 /* 286 * is this header in the request and the header in the cached 287 * request identical? If not, we give up and do a straight get 288 */ 289 h1 = cache_table_getm(r->pool, r->headers_in, vary); 290 h2 = cache_table_getm(r->pool, h->req_hdrs, vary); 291 if (h1 == h2) { 292 /* both headers NULL, so a match - do nothing */ 293 } 294 else if (h1 && h2 && !strcmp(h1, h2)) { 295 /* both headers exist and are equal - do nothing */ 296 } 297 else { 298 /* headers do not match, so Vary failed */ 299 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, 300 r, APLOGNO(00694) "cache_select(): Vary header mismatch."); 301 mismatch = 1; 302 break; 303 } 304 vary = cache_strqtok(NULL, CACHE_SEPARATOR, &last); 305 } 306 307 /* no vary match, try next provider */ 308 if (mismatch) { 309 /* try again with next cache type */ 310 list = list->next; 311 continue; 312 } 313 314 cache->provider = list->provider; 315 cache->provider_name = list->provider_name; 316 317 /* 318 * RFC2616 13.3.4 Rules for When to Use Entity Tags and Last-Modified 319 * Dates: An HTTP/1.1 caching proxy, upon receiving a conditional request 320 * that includes both a Last-Modified date and one or more entity tags as 321 * cache validators, MUST NOT return a locally cached response to the 322 * client unless that cached response is consistent with all of the 323 * conditional header fields in the request. 324 */ 325 if (ap_condition_if_match(r, h->resp_hdrs) == AP_CONDITION_NOMATCH 326 || ap_condition_if_unmodified_since(r, h->resp_hdrs) 327 == AP_CONDITION_NOMATCH 328 || ap_condition_if_none_match(r, h->resp_hdrs) 329 == AP_CONDITION_NOMATCH 330 || ap_condition_if_modified_since(r, h->resp_hdrs) 331 == AP_CONDITION_NOMATCH 332 || ap_condition_if_range(r, h->resp_hdrs) == AP_CONDITION_NOMATCH) { 333 mismatch = 1; 334 } 335 336 /* Is our cached response fresh enough? */ 337 if (mismatch || !cache_check_freshness(h, cache, r)) { 338 const char *etag, *lastmod; 339 340 /* Cache-Control: only-if-cached and revalidation required, try 341 * the next provider 342 */ 343 if (cache->control_in.only_if_cached) { 344 /* try again with next cache type */ 345 list = list->next; 346 continue; 347 } 348 349 /* set aside the stale entry for accessing later */ 350 cache->stale_headers = apr_table_copy(r->pool, 351 r->headers_in); 352 cache->stale_handle = h; 353 354 /* if no existing conditionals, use conditionals of our own */ 355 if (!mismatch) { 356 357 ap_log_rerror( 358 APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00695) "Cached response for %s isn't fresh. Adding " 359 "conditional request headers.", r->uri); 360 361 /* Remove existing conditionals that might conflict with ours */ 362 apr_table_unset(r->headers_in, "If-Match"); 363 apr_table_unset(r->headers_in, "If-Modified-Since"); 364 apr_table_unset(r->headers_in, "If-None-Match"); 365 apr_table_unset(r->headers_in, "If-Range"); 366 apr_table_unset(r->headers_in, "If-Unmodified-Since"); 367 368 etag = apr_table_get(h->resp_hdrs, "ETag"); 369 lastmod = apr_table_get(h->resp_hdrs, "Last-Modified"); 370 371 if (etag || lastmod) { 372 /* If we have a cached etag and/or Last-Modified add in 373 * our own conditionals. 374 */ 375 376 if (etag) { 377 apr_table_set(r->headers_in, "If-None-Match", etag); 378 } 379 380 if (lastmod) { 381 apr_table_set(r->headers_in, "If-Modified-Since", 382 lastmod); 383 } 384 385 /* 386 * Do not do Range requests with our own conditionals: If 387 * we get 304 the Range does not matter and otherwise the 388 * entity changed and we want to have the complete entity 389 */ 390 apr_table_unset(r->headers_in, "Range"); 391 392 } 393 394 } 395 396 /* ready to revalidate, pretend we were never here */ 397 return DECLINED; 398 } 399 400 /* Okay, this response looks okay. Merge in our stuff and go. */ 401 cache_accept_headers(h, r, h->resp_hdrs, r->headers_out, 0); 402 403 cache->handle = h; 404 return OK; 405 } 406 case DECLINED: { 407 /* try again with next cache type */ 408 list = list->next; 409 continue; 410 } 411 default: { 412 /* oo-er! an error */ 413 return rv; 414 } 415 } 416 } 417 418 /* if Cache-Control: only-if-cached, and not cached, return 504 */ 419 if (cache->control_in.only_if_cached) { 420 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00696) 421 "cache: 'only-if-cached' requested and no cached entity, " 422 "returning 504 Gateway Timeout for: %s", r->uri); 423 return HTTP_GATEWAY_TIME_OUT; 424 } 425 426 return DECLINED; 427} 428 429static apr_status_t cache_canonicalise_key(request_rec *r, apr_pool_t* p, 430 const char *uri, apr_uri_t *parsed_uri, const char **key) 431{ 432 cache_server_conf *conf; 433 char *port_str, *hn, *lcs; 434 const char *hostname, *scheme; 435 int i; 436 const char *path; 437 char *querystring; 438 439 if (*key) { 440 /* 441 * We have been here before during the processing of this request. 442 */ 443 return APR_SUCCESS; 444 } 445 446 /* 447 * Get the module configuration. We need this for the CacheIgnoreQueryString 448 * option below. 449 */ 450 conf = (cache_server_conf *) ap_get_module_config(r->server->module_config, 451 &cache_module); 452 453 /* 454 * Use the canonical name to improve cache hit rate, but only if this is 455 * not a proxy request or if this is a reverse proxy request. 456 * We need to handle both cases in the same manner as for the reverse proxy 457 * case we have the following situation: 458 * 459 * If a cached entry is looked up by mod_cache's quick handler r->proxyreq 460 * is still unset in the reverse proxy case as it only gets set in the 461 * translate name hook (either by ProxyPass or mod_rewrite) which is run 462 * after the quick handler hook. This is different to the forward proxy 463 * case where it gets set before the quick handler is run (in the 464 * post_read_request hook). 465 * If a cache entry is created by the CACHE_SAVE filter we always have 466 * r->proxyreq set correctly. 467 * So we must ensure that in the reverse proxy case we use the same code 468 * path and using the canonical name seems to be the right thing to do 469 * in the reverse proxy case. 470 */ 471 if (!r->proxyreq || (r->proxyreq == PROXYREQ_REVERSE)) { 472 if (conf->base_uri && conf->base_uri->hostname) { 473 hostname = conf->base_uri->hostname; 474 } 475 else { 476 /* Use _default_ as the hostname if none present, as in mod_vhost */ 477 hostname = ap_get_server_name(r); 478 if (!hostname) { 479 hostname = "_default_"; 480 } 481 } 482 } 483 else if (parsed_uri->hostname) { 484 /* Copy the parsed uri hostname */ 485 hn = apr_pstrdup(p, parsed_uri->hostname); 486 ap_str_tolower(hn); 487 /* const work-around */ 488 hostname = hn; 489 } 490 else { 491 /* We are a proxied request, with no hostname. Unlikely 492 * to get very far - but just in case */ 493 hostname = "_default_"; 494 } 495 496 /* 497 * Copy the scheme, ensuring that it is lower case. If the parsed uri 498 * contains no string or if this is not a proxy request get the http 499 * scheme for this request. As r->parsed_uri.scheme is not set if this 500 * is a reverse proxy request, it is ensured that the cases 501 * "no proxy request" and "reverse proxy request" are handled in the same 502 * manner (see above why this is needed). 503 */ 504 if (r->proxyreq && parsed_uri->scheme) { 505 /* Copy the scheme and lower-case it */ 506 lcs = apr_pstrdup(p, parsed_uri->scheme); 507 ap_str_tolower(lcs); 508 /* const work-around */ 509 scheme = lcs; 510 } 511 else { 512 if (conf->base_uri && conf->base_uri->scheme) { 513 scheme = conf->base_uri->scheme; 514 } 515 else { 516 scheme = ap_http_scheme(r); 517 } 518 } 519 520 /* 521 * If this is a proxy request, but not a reverse proxy request (see comment 522 * above why these cases must be handled in the same manner), copy the 523 * URI's port-string (which may be a service name). If the URI contains 524 * no port-string, use apr-util's notion of the default port for that 525 * scheme - if available. Otherwise use the port-number of the current 526 * server. 527 */ 528 if (r->proxyreq && (r->proxyreq != PROXYREQ_REVERSE)) { 529 if (parsed_uri->port_str) { 530 port_str = apr_pcalloc(p, strlen(parsed_uri->port_str) + 2); 531 port_str[0] = ':'; 532 for (i = 0; parsed_uri->port_str[i]; i++) { 533 port_str[i + 1] = apr_tolower(parsed_uri->port_str[i]); 534 } 535 } 536 else if (apr_uri_port_of_scheme(scheme)) { 537 port_str = apr_psprintf(p, ":%u", apr_uri_port_of_scheme(scheme)); 538 } 539 else { 540 /* No port string given in the AbsoluteUri, and we have no 541 * idea what the default port for the scheme is. Leave it 542 * blank and live with the inefficiency of some extra cached 543 * entities. 544 */ 545 port_str = ""; 546 } 547 } 548 else { 549 if (conf->base_uri && conf->base_uri->port_str) { 550 port_str = conf->base_uri->port_str; 551 } 552 else if (conf->base_uri && conf->base_uri->hostname) { 553 port_str = ""; 554 } 555 else { 556 /* Use the server port */ 557 port_str = apr_psprintf(p, ":%u", ap_get_server_port(r)); 558 } 559 } 560 561 /* 562 * Check if we need to ignore session identifiers in the URL and do so 563 * if needed. 564 */ 565 path = uri; 566 querystring = parsed_uri->query; 567 if (conf->ignore_session_id->nelts) { 568 int i; 569 char **identifier; 570 571 identifier = (char **) conf->ignore_session_id->elts; 572 for (i = 0; i < conf->ignore_session_id->nelts; i++, identifier++) { 573 int len; 574 const char *param; 575 576 len = strlen(*identifier); 577 /* 578 * Check that we have a parameter separator in the last segment 579 * of the path and that the parameter matches our identifier 580 */ 581 if ((param = ap_strrchr_c(path, ';')) 582 && !strncmp(param + 1, *identifier, len) 583 && (*(param + len + 1) == '=') 584 && !ap_strchr_c(param + len + 2, '/')) { 585 path = apr_pstrmemdup(p, path, param - path); 586 continue; 587 } 588 /* 589 * Check if the identifier is in the querystring and cut it out. 590 */ 591 if (querystring) { 592 /* 593 * First check if the identifier is at the beginning of the 594 * querystring and followed by a '=' 595 */ 596 if (!strncmp(querystring, *identifier, len) 597 && (*(querystring + len) == '=')) { 598 param = querystring; 599 } 600 else { 601 char *complete; 602 603 /* 604 * In order to avoid subkey matching (PR 48401) prepend 605 * identifier with a '&' and append a '=' 606 */ 607 complete = apr_pstrcat(p, "&", *identifier, "=", NULL); 608 param = strstr(querystring, complete); 609 /* If we found something we are sitting on the '&' */ 610 if (param) { 611 param++; 612 } 613 } 614 if (param) { 615 const char *amp; 616 617 if (querystring != param) { 618 querystring = apr_pstrndup(p, querystring, 619 param - querystring); 620 } 621 else { 622 querystring = ""; 623 } 624 625 if ((amp = ap_strchr_c(param + len + 1, '&'))) { 626 querystring = apr_pstrcat(p, querystring, amp + 1, 627 NULL); 628 } 629 else { 630 /* 631 * If querystring is not "", then we have the case 632 * that the identifier parameter we removed was the 633 * last one in the original querystring. Hence we have 634 * a trailing '&' which needs to be removed. 635 */ 636 if (*querystring) { 637 querystring[strlen(querystring) - 1] = '\0'; 638 } 639 } 640 } 641 } 642 } 643 } 644 645 /* Key format is a URI, optionally without the query-string */ 646 if (conf->ignorequerystring) { 647 *key = apr_pstrcat(p, scheme, "://", hostname, port_str, path, "?", 648 NULL); 649 } 650 else { 651 *key = apr_pstrcat(p, scheme, "://", hostname, port_str, path, "?", 652 querystring, NULL); 653 } 654 655 /* 656 * Store the key in the request_config for the cache as r->parsed_uri 657 * might have changed in the time from our first visit here triggered by the 658 * quick handler and our possible second visit triggered by the CACHE_SAVE 659 * filter (e.g. r->parsed_uri got unescaped). In this case we would save the 660 * resource in the cache under a key where it is never found by the quick 661 * handler during following requests. 662 */ 663 ap_log_rerror( 664 APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00698) "cache: Key for entity %s?%s is %s", uri, parsed_uri->query, *key); 665 666 return APR_SUCCESS; 667} 668 669apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p, 670 const char **key) 671{ 672 return cache_canonicalise_key(r, p, r->uri, &r->parsed_uri, key); 673} 674 675/* 676 * Invalidate a specific URL entity in all caches 677 * 678 * All cached entities for this URL are removed, usually in 679 * response to a POST/PUT or DELETE. 680 * 681 * This function returns OK if at least one entity was found and 682 * removed, and DECLINED if no cached entities were removed. 683 */ 684int cache_invalidate(cache_request_rec *cache, request_rec *r) 685{ 686 cache_provider_list *list; 687 apr_status_t rv, status = DECLINED; 688 cache_handle_t *h; 689 apr_uri_t location_uri; 690 apr_uri_t content_location_uri; 691 692 const char *location, *location_key = NULL; 693 const char *content_location, *content_location_key = NULL; 694 695 if (!cache) { 696 /* This should never happen */ 697 ap_log_rerror( 698 APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00697) "cache: No cache request information available for key" 699 " generation"); 700 return DECLINED; 701 } 702 703 if (!cache->key) { 704 rv = cache_generate_key(r, r->pool, &cache->key); 705 if (rv != APR_SUCCESS) { 706 return DECLINED; 707 } 708 } 709 710 location = apr_table_get(r->headers_out, "Location"); 711 if (location) { 712 if (APR_SUCCESS != apr_uri_parse(r->pool, location, &location_uri) 713 || APR_SUCCESS 714 != cache_canonicalise_key(r, r->pool, location, 715 &location_uri, &location_key) 716 || !(r->parsed_uri.hostname && location_uri.hostname 717 && !strcmp(r->parsed_uri.hostname, 718 location_uri.hostname))) { 719 location_key = NULL; 720 } 721 } 722 723 content_location = apr_table_get(r->headers_out, "Content-Location"); 724 if (content_location) { 725 if (APR_SUCCESS 726 != apr_uri_parse(r->pool, content_location, 727 &content_location_uri) 728 || APR_SUCCESS 729 != cache_canonicalise_key(r, r->pool, content_location, 730 &content_location_uri, &content_location_key) 731 || !(r->parsed_uri.hostname && content_location_uri.hostname 732 && !strcmp(r->parsed_uri.hostname, 733 content_location_uri.hostname))) { 734 content_location_key = NULL; 735 } 736 } 737 738 /* go through the cache types */ 739 h = apr_palloc(r->pool, sizeof(cache_handle_t)); 740 741 list = cache->providers; 742 743 while (list) { 744 745 /* invalidate the request uri */ 746 rv = list->provider->open_entity(h, r, cache->key); 747 if (OK == rv) { 748 rv = list->provider->invalidate_entity(h, r); 749 status = OK; 750 } 751 ap_log_rerror( 752 APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02468) "cache: Attempted to invalidate cached entity with key: %s", cache->key); 753 754 /* invalidate the Location */ 755 if (location_key) { 756 rv = list->provider->open_entity(h, r, location_key); 757 if (OK == rv) { 758 rv = list->provider->invalidate_entity(h, r); 759 status = OK; 760 } 761 ap_log_rerror( 762 APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02469) "cache: Attempted to invalidate cached entity with key: %s", location_key); 763 } 764 765 /* invalidate the Content-Location */ 766 if (content_location_key) { 767 rv = list->provider->open_entity(h, r, content_location_key); 768 if (OK == rv) { 769 rv = list->provider->invalidate_entity(h, r); 770 status = OK; 771 } 772 ap_log_rerror( 773 APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02470) "cache: Attempted to invalidate cached entity with key: %s", content_location_key); 774 } 775 776 list = list->next; 777 } 778 779 return status; 780} 781