From 09af9b4d0d874a6673379967aa2bf372fd39c866 Mon Sep 17 00:00:00 2001 From: "Paul J. Reder" Date: Thu, 11 Sep 2003 14:56:03 +0000 Subject: [PATCH] Modified the cache code to be header-location agnostic. Also fixed a number of other cache code bugs related to PR 15852. Includes a patch submitted by Sushma Rai . This fixes mod_mem_cache but not mod_disk_cache yet so I'm not closing the PR since that is what they are using. [Paul J. Reder] git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@101218 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 6 +++ modules/experimental/cache_storage.c | 6 ++- modules/experimental/cache_util.c | 58 +++++++++++++++++++---- modules/experimental/mod_cache.c | 66 +++++++++++++++++++++------ modules/experimental/mod_cache.h | 2 +- modules/experimental/mod_disk_cache.c | 2 +- modules/experimental/mod_mem_cache.c | 35 +++++++++++++- 7 files changed, 150 insertions(+), 25 deletions(-) diff --git a/CHANGES b/CHANGES index 42ea800263..8da3d830d9 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,12 @@ Changes with Apache 2.1.0-dev [Remove entries to the current 2.0 section below, when backported] + *) Modified the cache code to be header-location agnostic. Also + fixed a number of other cache code bugs related to PR 15852. + Includes a patch submitted by Sushma Rai . + This fixes mod_mem_cache but not mod_disk_cache yet so I'm not + closing the PR since that is what they are using. [Paul J. Reder] + *) Switch to APR 1.0 API. *) Modify ap_get_client_block() to note if it has seen EOS. diff --git a/modules/experimental/cache_storage.c b/modules/experimental/cache_storage.c index 7a549683cb..4f8b703d50 100644 --- a/modules/experimental/cache_storage.c +++ b/modules/experimental/cache_storage.c @@ -187,6 +187,7 @@ int cache_select_url(request_rec *r, const char *types, char *url) switch ((rv = cache_run_open_entity(h, r, type, key))) { case OK: { char *vary = NULL; + char *varyhdr = NULL; if (cache_read_entity_headers(h, r) != APR_SUCCESS) { /* TODO: Handle this error */ return DECLINED; @@ -209,7 +210,10 @@ int cache_select_url(request_rec *r, const char *types, char *url) * * RFC2616 13.6 and 14.44 describe the Vary mechanism. */ - vary = apr_pstrdup(r->pool, apr_table_get(r->headers_out, "Vary")); + if ((varyhdr = apr_table_get(r->err_headers_out, "Vary")) == NULL) { + varyhdr = apr_table_get(r->headers_out, "Vary"); + } + vary = apr_pstrdup(r->pool, varyhdr); while (vary && *vary) { char *name = vary; const char *h1, *h2; diff --git a/modules/experimental/cache_util.c b/modules/experimental/cache_util.c index 43fb0b6970..412dd40d79 100644 --- a/modules/experimental/cache_util.c +++ b/modules/experimental/cache_util.c @@ -126,12 +126,15 @@ CACHE_DECLARE(apr_int64_t) ap_cache_current_age(cache_info *info, apr_time_t now) { apr_time_t apparent_age, corrected_received_age, response_delay, - corrected_initial_age, resident_time, current_age; + corrected_initial_age, resident_time, current_age, + age_value_usec; + + age_value_usec = apr_time_from_sec(age_value); /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */ apparent_age = MAX(0, info->response_time - info->date); - corrected_received_age = MAX(apparent_age, age_value); + corrected_received_age = MAX(apparent_age, age_value_usec); response_delay = info->response_time - info->request_time; corrected_initial_age = corrected_received_age + response_delay; resident_time = now - info->response_time; @@ -148,6 +151,7 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_request_rec *cache, int age_in_errhdr = 0; const char *cc_cresp, *cc_ceresp, *cc_req; const char *agestr = NULL; + const char *expstr = NULL; char *val; apr_time_t age_c = 0; cache_info *info = &(cache->handle->cache_obj->info); @@ -196,6 +200,10 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_request_rec *cache, age_in_errhdr = 1; } + if (!(expstr = apr_table_get(r->err_headers_out, "Expires"))) { + expstr = apr_table_get(r->headers_out, "Expires"); + } + /* calculate age of object */ age = ap_cache_current_age(info, age_c, r->request_time); @@ -274,12 +282,25 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_request_rec *cache, "proxy-revalidate", NULL)))) { maxstale = 0; } + /* handle expiration */ if (((smaxage != -1) && (age < (smaxage - minfresh))) || ((maxage != -1) && (age < (maxage + maxstale - minfresh))) || ((smaxage == -1) && (maxage == -1) && (info->expire != APR_DATE_BAD) && (age < (apr_time_sec(info->expire - info->date) + maxstale - minfresh)))) { + const char *warn_head; + apr_table_t *head_ptr; + + warn_head = apr_table_get(r->headers_out, "Warning"); + if (warn_head != NULL) { + head_ptr = r->headers_out; + } + else { + warn_head = apr_table_get(r->err_headers_out, "Warning"); + head_ptr = r->err_headers_out; + } + /* it's fresh darlings... */ /* set age header on response */ if (age_in_errhdr) { @@ -297,7 +318,27 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_request_rec *cache, (info->expire != APR_DATE_BAD && (info->expire - info->date) > age))) { /* make sure we don't stomp on a previous warning */ - apr_table_merge(r->headers_out, "Warning", "110 Response is stale"); + if ((warn_head == NULL) || + ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) { + apr_table_merge(head_ptr, "Warning", "110 Response is stale"); + } + } + /* + * If none of Expires, Cache-Control: max-age, or Cache-Control: + * s-maxage appears in the response, and the respose header age + * calculated is more than 24 hours add the warning 113 + */ + if ((maxage_cresp == -1) && (smaxage == -1) && + (expstr == NULL) && (age > 86400)) { + + /* Make sure we don't stomp on a previous warning, and don't dup + * a 113 marning that is already present. Also, make sure to add + * the new warning to the correct *headers_out location. + */ + if ((warn_head == NULL) || + ((warn_head != NULL) && (ap_strstr_c(warn_head, "113") == NULL))) { + apr_table_merge(head_ptr, "Warning", "113 Heuristic expiration"); + } } return 1; /* Cache object is fresh (enough) */ } @@ -497,17 +538,18 @@ CACHE_DECLARE(char *)generate_name(apr_pool_t *p, int dirlevels, return apr_pstrdup(p, hashfile); } -/* Create a new table consisting of those elements from a request_rec's - * headers_out that are allowed to be stored in a cache. +/* Create a new table consisting of those elements from an input + * headers table that are allowed to be stored in a cache. */ -CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(request_rec *r) +CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool, + apr_table_t *t) { - /* Make a copy of the response headers, and remove from + /* Make a copy of the headers, and remove from * the copy any hop-by-hop headers, as defined in Section * 13.5.1 of RFC 2616 */ apr_table_t *headers_out; - headers_out = apr_table_copy(r->pool, r->headers_out); + headers_out = apr_table_copy(pool, t); apr_table_unset(headers_out, "Connection"); apr_table_unset(headers_out, "Keep-Alive"); apr_table_unset(headers_out, "Proxy-Authenticate"); diff --git a/modules/experimental/mod_cache.c b/modules/experimental/mod_cache.c index d93b48d9ab..8828b45e2e 100644 --- a/modules/experimental/mod_cache.c +++ b/modules/experimental/mod_cache.c @@ -250,7 +250,9 @@ static int cache_url_handler(request_rec *r, int lookup) return OK; } else { - r->err_headers_out = apr_table_make(r->pool, 3); + if (!r->err_headers_out) { + r->err_headers_out = apr_table_make(r->pool, 3); + } /* stale data available */ if (lookup) { return DECLINED; @@ -272,6 +274,16 @@ static int cache_url_handler(request_rec *r, int lookup) } /* else if non-conditional request */ else { + /* Temporarily hack this to work the way it had been. Its broken, + * but its broken the way it was before. I'm working on figuring + * out why the filter add in the conditional filter doesn't work. pjr + * + * info = &(cache->handle->cache_obj->info); + * + * Uncomment the above when the code in cache_conditional_filter_handle + * is properly fixed... pjr + */ + /* fudge response into a conditional */ if (info && info->etag) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, @@ -415,6 +427,7 @@ static int cache_conditional_filter(ap_filter_t *f, apr_bucket_brigade *in) static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) { int rv; + int date_in_errhdr = 0; request_rec *r = f->r; cache_request_rec *cache; cache_server_conf *conf; @@ -478,7 +491,10 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) /* read expiry date; if a bad date, then leave it so the client can * read it */ - exps = apr_table_get(r->headers_out, "Expires"); + exps = apr_table_get(r->err_headers_out, "Expires"); + if (exps == NULL) { + exps = apr_table_get(r->headers_out, "Expires"); + } if (exps != NULL) { if (APR_DATE_BAD == (exp = apr_date_parse_http(exps))) { exps = NULL; @@ -489,7 +505,10 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) } /* read the last-modified date; if the date is bad, then delete it */ - lastmods = apr_table_get(r->headers_out, "Last-Modified"); + lastmods = apr_table_get(r->err_headers_out, "Last-Modified"); + if (lastmods ==NULL) { + lastmods = apr_table_get(r->headers_out, "Last-Modified"); + } if (lastmods != NULL) { if (APR_DATE_BAD == (lastmod = apr_date_parse_http(lastmods))) { lastmods = NULL; @@ -501,8 +520,14 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) conf = (cache_server_conf *) ap_get_module_config(r->server->module_config, &cache_module); /* read the etag and cache-control from the entity */ - etag = apr_table_get(r->headers_out, "Etag"); - cc_out = apr_table_get(r->headers_out, "Cache-Control"); + etag = apr_table_get(r->err_headers_out, "Etag"); + if (etag == NULL) { + etag = apr_table_get(r->headers_out, "Etag"); + } + cc_out = apr_table_get(r->err_headers_out, "Cache-Control"); + if (cc_out == NULL) { + cc_out = apr_table_get(r->headers_out, "Cache-Control"); + } /* * what responses should we not cache? @@ -603,7 +628,10 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) /* Set the content length if known. */ - cl = apr_table_get(r->headers_out, "Content-Length"); + cl = apr_table_get(r->err_headers_out, "Content-Length"); + if (cl == NULL) { + cl = apr_table_get(r->headers_out, "Content-Length"); + } if (cl) { size = apr_atoi64(cl); } @@ -652,7 +680,7 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) } /* pre-existing cache handle and 304, make entity fresh */ else if (r->status == HTTP_NOT_MODIFIED) { - /* update headers */ + /* update headers: TODO */ /* remove this filter ??? */ @@ -689,7 +717,13 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) */ /* Read the date. Generate one if one is not supplied */ - dates = apr_table_get(r->headers_out, "Date"); + dates = apr_table_get(r->err_headers_out, "Date"); + if (dates != NULL) { + date_in_errhdr = 1; + } + else { + dates = apr_table_get(r->headers_out, "Date"); + } if (dates != NULL) { info->date = apr_date_parse_http(dates); } @@ -700,10 +734,13 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) now = apr_time_now(); if (info->date == APR_DATE_BAD) { /* No, or bad date */ char *dates; - /* no date header! */ + /* no date header (or bad header)! */ /* add one; N.B. use the time _now_ rather than when we were checking * the cache */ + if (date_in_errhdr == 1) { + apr_table_unset(r->err_headers_out, "Date"); + } date = now; dates = apr_pcalloc(r->pool, MAX_STRING_LEN); apr_rfc822_date(dates, now); @@ -737,9 +774,9 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) /* if no expiry date then * if lastmod - * expiry date = now + min((date - lastmod) * factor, maxexpire) + * expiry date = date + min((date - lastmod) * factor, maxexpire) * else - * expire date = now + defaultexpire + * expire date = date + defaultexpire */ if (exp == APR_DATE_BAD) { /* if lastmod == date then you get 0*conf->factor which results in @@ -748,18 +785,21 @@ static int cache_in_filter(ap_filter_t *f, apr_bucket_brigade *in) */ if ((lastmod != APR_DATE_BAD) && (lastmod < date)) { apr_time_t x = (apr_time_t) ((date - lastmod) * conf->factor); + if (x > conf->maxex) { x = conf->maxex; } - exp = now + x; + exp = date + x; } else { - exp = now + conf->defex; + exp = date + conf->defex; } } info->expire = exp; info->content_type = apr_pstrdup(r->pool, r->content_type); + info->etag = apr_pstrdup(r->pool, etag); + info->lastmod = apr_pstrdup(r->pool, lastmods); info->filename = apr_pstrdup(r->pool, r->filename ); /* diff --git a/modules/experimental/mod_cache.h b/modules/experimental/mod_cache.h index e1be586c88..b68492d337 100644 --- a/modules/experimental/mod_cache.h +++ b/modules/experimental/mod_cache.h @@ -278,7 +278,7 @@ CACHE_DECLARE(const char *)ap_cache_tokstr(apr_pool_t *p, const char *list, cons /* Create a new table consisting of those elements from a request_rec's * headers_out that are allowed to be stored in a cache */ -CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(request_rec *r); +CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool, apr_table_t *t); /** * cache_storage.c diff --git a/modules/experimental/mod_disk_cache.c b/modules/experimental/mod_disk_cache.c index 82c3ad883e..ab2a4ab5ae 100644 --- a/modules/experimental/mod_disk_cache.c +++ b/modules/experimental/mod_disk_cache.c @@ -603,7 +603,7 @@ static apr_status_t write_headers(cache_handle_t *h, request_rec *r, cache_info if (r->headers_out) { int i; - apr_table_t* headers_out = ap_cache_cacheable_hdrs_out(r); + apr_table_t* headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out); apr_table_entry_t *elts = (apr_table_entry_t *) apr_table_elts(headers_out)->elts; for (i = 0; i < apr_table_elts(headers_out)->nelts; ++i) { if (elts[i].key != NULL) { diff --git a/modules/experimental/mod_mem_cache.c b/modules/experimental/mod_mem_cache.c index c14bfe56e5..37831deef7 100644 --- a/modules/experimental/mod_mem_cache.c +++ b/modules/experimental/mod_mem_cache.c @@ -86,10 +86,12 @@ typedef struct { typedef struct mem_cache_object { cache_type_e type; apr_ssize_t num_header_out; + apr_ssize_t num_err_header_out; apr_ssize_t num_subprocess_env; apr_ssize_t num_notes; apr_ssize_t num_req_hdrs; cache_header_tbl_t *header_out; + cache_header_tbl_t *err_header_out; cache_header_tbl_t *subprocess_env; cache_header_tbl_t *notes; cache_header_tbl_t *req_hdrs; /* for Vary negotiation */ @@ -303,6 +305,11 @@ static void cleanup_cache_object(cache_object_t *obj) free(mobj->header_out[0].hdr); free(mobj->header_out); } + if (mobj->err_header_out) { + if (mobj->err_header_out[0].hdr) + free(mobj->err_header_out[0].hdr); + free(mobj->err_header_out); + } if (mobj->subprocess_env) { if (mobj->subprocess_env[0].hdr) free(mobj->subprocess_env[0].hdr); @@ -782,6 +789,7 @@ static apr_status_t read_headers(cache_handle_t *h, request_rec *r) h->req_hdrs = apr_table_make(r->pool, mobj->num_req_hdrs); r->headers_out = apr_table_make(r->pool, mobj->num_header_out); + r->err_headers_out = apr_table_make(r->pool, mobj->num_err_header_out); r->subprocess_env = apr_table_make(r->pool, mobj->num_subprocess_env); r->notes = apr_table_make(r->pool, mobj->num_notes); @@ -791,6 +799,9 @@ static apr_status_t read_headers(cache_handle_t *h, request_rec *r) rc = unserialize_table( mobj->header_out, mobj->num_header_out, r->headers_out); + rc = unserialize_table( mobj->err_header_out, + mobj->num_err_header_out, + r->err_headers_out); rc = unserialize_table( mobj->subprocess_env, mobj->num_subprocess_env, r->subprocess_env); @@ -852,7 +863,13 @@ static apr_status_t write_headers(cache_handle_t *h, request_rec *r, cache_info /* Precompute how much storage we need to hold the headers */ rc = serialize_table(&mobj->header_out, &mobj->num_header_out, - ap_cache_cacheable_hdrs_out(r)); + ap_cache_cacheable_hdrs_out(r->pool, r->headers_out)); + if (rc != APR_SUCCESS) { + return rc; + } + rc = serialize_table(&mobj->err_header_out, + &mobj->num_err_header_out, + ap_cache_cacheable_hdrs_out(r->pool, r->err_headers_out)); if (rc != APR_SUCCESS) { return rc; } @@ -892,6 +909,22 @@ static apr_status_t write_headers(cache_handle_t *h, request_rec *r, cache_info } memcpy(obj->info.content_type, info->content_type, len); } + if (info->etag) { + apr_size_t len = strlen(info->etag) + 1; + obj->info.etag = (char*) malloc(len); + if (!obj->info.etag) { + return APR_ENOMEM; + } + memcpy(obj->info.etag, info->etag, len); + } + if (info->lastmods) { + apr_size_t len = strlen(info->lastmods) + 1; + obj->info.lastmods = (char*) malloc(len); + if (!obj->info.lastmods) { + return APR_ENOMEM; + } + memcpy(obj->info.lastmods, info->lastmods, len); + } if ( info->filename) { apr_size_t len = strlen(info->filename) + 1; obj->info.filename = (char*) malloc(len); -- 2.40.0