diff options
-rw-r--r-- | include/ap_mmn.h | 3 | ||||
-rw-r--r-- | modules/cache/cache_storage.c | 2 | ||||
-rw-r--r-- | modules/cache/cache_util.c | 174 | ||||
-rw-r--r-- | modules/cache/cache_util.h | 43 | ||||
-rw-r--r-- | modules/cache/mod_cache.h | 9 |
5 files changed, 216 insertions, 15 deletions
diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 0ee469b232..5f433c3e3c 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -274,12 +274,13 @@ * Make root parameter of ap_expr_eval() const. * 20100923.3 (2.3.9-dev) Add "last" member to ap_directive_t * 20101012.0 (2.3.9-dev) Add header to cache_status hook. + * 20101016.0 (2.3.9-dev) Remove ap_cache_check_allowed(). */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ #ifndef MODULE_MAGIC_NUMBER_MAJOR -#define MODULE_MAGIC_NUMBER_MAJOR 20101012 +#define MODULE_MAGIC_NUMBER_MAJOR 20101016 #endif #define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */ diff --git a/modules/cache/cache_storage.c b/modules/cache/cache_storage.c index c0bea51262..457f63c70a 100644 --- a/modules/cache/cache_storage.c +++ b/modules/cache/cache_storage.c @@ -216,7 +216,7 @@ int cache_select(cache_request_rec *cache, request_rec *r) } } - if (!ap_cache_check_allowed(r)) { + if (!ap_cache_check_allowed(cache, r)) { return DECLINED; } diff --git a/modules/cache/cache_util.c b/modules/cache/cache_util.c index 9982c20f63..5547575864 100644 --- a/modules/cache/cache_util.c +++ b/modules/cache/cache_util.c @@ -383,7 +383,7 @@ apr_status_t cache_remove_lock(cache_server_conf *conf, return apr_file_remove(lockname, r->pool); } -CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r) { +CACHE_DECLARE(int) ap_cache_check_allowed(cache_request_rec *cache, request_rec *r) { const char *cc_req; const char *pragma; cache_server_conf *conf = @@ -409,8 +409,9 @@ CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r) { cc_req = apr_table_get(r->headers_in, "Cache-Control"); pragma = apr_table_get(r->headers_in, "Pragma"); - if (ap_cache_liststr(NULL, pragma, "no-cache", NULL) - || ap_cache_liststr(NULL, cc_req, "no-cache", NULL)) { + ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in); + + if (cache->control_in.no_cache) { if (!conf->ignorecachecontrol) { return 0; @@ -423,7 +424,7 @@ CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r) { } } - if (ap_cache_liststr(NULL, cc_req, "no-store", NULL)) { + if (cache->control_in.no_store) { if (!conf->ignorecachecontrol) { /* We're not allowed to serve a cached copy */ @@ -1012,3 +1013,168 @@ CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_out(request_rec *r) return headers_out; } + +/** + * Parse the Cache-Control and Pragma headers in one go, marking + * which tokens appear within the header. Populate the structure + * passed in. + */ +int ap_cache_control(request_rec *r, cache_control_t *cc, + const char *cc_header, const char *pragma_header, apr_table_t *headers) +{ + char *last; + + if (cc->parsed) { + return cc->cache_control || cc->pragma; + } + + cc->parsed = 1; + cc->max_age_value = -1; + cc->max_stale_value = -1; + cc->min_fresh_value = -1; + cc->s_maxage_value = -1; + + if (pragma_header) { + char *header = apr_pstrdup(r->pool, pragma_header); + const char *token = apr_strtok(header, ", ", &last); + while (token) { + /* handle most common quickest case... */ + if (!strcmp(token, "no-cache")) { + cc->no_cache = 1; + } + /* ...then try slowest case */ + else if (!strcasecmp(token, "no-cache")) { + cc->no_cache = 1; + } + token = apr_strtok(NULL, ", ", &last); + } + cc->pragma = 1; + } + + if (cc_header) { + char *header = apr_pstrdup(r->pool, cc_header); + const char *token = apr_strtok(header, ", ", &last); + while (token) { + switch (token[0]) { + case 'n': + case 'N': { + /* handle most common quickest cases... */ + if (!strcmp(token, "no-cache")) { + cc->no_cache = 1; + } + else if (!strcmp(token, "no-store")) { + cc->no_store = 1; + } + /* ...then try slowest cases */ + else if (!strncasecmp(token, "no-cache", 8)) { + if (token[8] == '=') { + if (apr_table_get(headers, token + 9)) { + cc->no_cache_header = 1; + } + } + else if (!token[8]) { + cc->no_cache = 1; + } + break; + } + else if (!strcasecmp(token, "no-store")) { + cc->no_store = 1; + } + else if (!strcasecmp(token, "no-transform")) { + cc->no_transform = 1; + } + break; + } + case 'm': + case 'M': { + /* handle most common quickest cases... */ + if (!strcmp(token, "max-age=0")) { + cc->max_age = 1; + cc->max_age_value = 0; + } + else if (!strcmp(token, "must-revalidate")) { + cc->must_revalidate = 1; + } + /* ...then try slowest cases */ + else if (!strncasecmp(token, "max-age", 7)) { + if (token[7] == '=') { + cc->max_age = 1; + cc->max_age_value = atoi(token + 8); + } + break; + } + else if (!strncasecmp(token, "max-stale", 9)) { + if (token[9] == '=') { + cc->max_stale = 1; + cc->max_stale_value = atoi(token + 10); + } + else if (!token[10]) { + cc->max_stale = 1; + cc->max_stale_value = -1; + } + break; + } + else if (!strncasecmp(token, "min-fresh", 9)) { + if (token[9] == '=') { + cc->min_fresh = 1; + cc->min_fresh_value = atoi(token + 10); + } + break; + } + else if (!strcasecmp(token, "must-revalidate")) { + cc->must_revalidate = 1; + } + break; + } + case 'o': + case 'O': { + if (!strcasecmp(token, "only-if-cached")) { + cc->only_if_cached = 1; + } + break; + } + case 'p': + case 'P': { + /* handle most common quickest cases... */ + if (!strcmp(token, "private")) { + cc->private = 1; + } + /* ...then try slowest cases */ + else if (!strcasecmp(token, "public")) { + cc->public = 1; + } + else if (!strncasecmp(token, "private", 7)) { + if (token[7] == '=') { + if (apr_table_get(headers, token + 8)) { + cc->private_header = 1; + } + } + else if (!token[7]) { + cc->private = 1; + } + break; + } + else if (!strcasecmp(token, "proxy-revalidate")) { + cc->proxy_revalidate = 1; + } + break; + } + case 's': + case 'S': { + if (!strncasecmp(token, "s-maxage", 8)) { + if (token[8] == '=') { + cc->s_maxage = 1; + cc->s_maxage_value = atoi(token + 9); + } + break; + } + break; + } + } + token = apr_strtok(NULL, ", ", &last); + } + cc->cache_control = 1; + } + + return (cc_header != NULL || pragma_header != NULL); +} diff --git a/modules/cache/cache_util.h b/modules/cache/cache_util.h index 1189694298..0aa20fe0b0 100644 --- a/modules/cache/cache_util.h +++ b/modules/cache/cache_util.h @@ -190,6 +190,31 @@ typedef struct { int stale_on_error_set; } cache_dir_conf; +/* a cache control header breakdown */ +typedef struct { + unsigned int parsed:1; + unsigned int cache_control:1; + unsigned int pragma:1; + unsigned int no_cache:1; + unsigned int no_cache_header:1; /* no cache by header match */ + unsigned int no_store:1; + unsigned int max_age:1; + unsigned int max_stale:1; + unsigned int min_fresh:1; + unsigned int no_transform:1; + unsigned int only_if_cached:1; + unsigned int public:1; + unsigned int private:1; + unsigned int private_header:1; /* private by header match */ + unsigned int must_revalidate:1; + unsigned int proxy_revalidate:1; + unsigned int s_maxage:1; + int max_age_value; /* if positive, then set */ + int max_stale_value; /* if positive, then set */ + int min_fresh_value; /* if positive, then set */ + int s_maxage_value; /* if positive, then set */ +} cache_control_t; + /* A linked-list of authn providers. */ typedef struct cache_provider_list cache_provider_list; @@ -222,9 +247,27 @@ typedef struct { */ apr_off_t size; /* the content length from the headers, or -1 */ apr_bucket_brigade *out; /* brigade to reuse for upstream responses */ + cache_control_t control_in; /* cache control incoming */ } cache_request_rec; /** + * Parse the Cache-Control and Pragma headers in one go, marking + * which tokens appear within the header. Populate the structure + * passed in. + */ +int ap_cache_control(request_rec *r, cache_control_t *cc, const char *cc_header, + const char *pragma_header, apr_table_t *headers); + +/** + * Check the whether the request allows a cached object to be served as per RFC2616 + * section 14.9.4 (Cache Revalidation and Reload Controls) + * @param h cache_handle_t + * @param r request_rec + * @return 0 ==> cache object may not be served, 1 ==> cache object may be served + */ +CACHE_DECLARE(int) ap_cache_check_allowed(cache_request_rec *cache, request_rec *r); + +/** * Check the freshness of the cache object per RFC2616 section 13.2 (Expiration Model) * @param h cache_handle_t * @param r request_rec diff --git a/modules/cache/mod_cache.h b/modules/cache/mod_cache.h index 3944dfd7e1..3a06080192 100644 --- a/modules/cache/mod_cache.h +++ b/modules/cache/mod_cache.h @@ -124,15 +124,6 @@ typedef enum { CACHE_DECLARE(apr_time_t) ap_cache_current_age(cache_info *info, const apr_time_t age_value, apr_time_t now); -/** - * Check the whether the request allows a cached object to be served as per RFC2616 - * section 14.9.4 (Cache Revalidation and Reload Controls) - * @param h cache_handle_t - * @param r request_rec - * @return 0 ==> cache object may not be served, 1 ==> cache object may be served - */ -CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r); - CACHE_DECLARE(apr_time_t) ap_cache_hex2usec(const char *x); CACHE_DECLARE(void) ap_cache_usec2hex(apr_time_t j, char *y); CACHE_DECLARE(char *) ap_cache_generate_name(apr_pool_t *p, int dirlevels, |