diff --git a/CHANGES b/CHANGES index 1147bb3589..5e10606d7b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_cache: Make sure Vary processing handles multivalued Vary headers and + multivalued headers referred to via Vary. [Graham Leggett] + *) mod_cache: When serving from cache, only the last header of a multivalued header was taken into account. Fixed. [Graham Leggett] diff --git a/modules/cache/cache_storage.c b/modules/cache/cache_storage.c index 99b61fde38..b96ded4d13 100644 --- a/modules/cache/cache_storage.c +++ b/modules/cache/cache_storage.c @@ -220,6 +220,7 @@ int cache_select(cache_request_rec *cache, request_rec *r) case OK: { char *vary = NULL; int fresh, mismatch = 0; + char *last = NULL; if (list->provider->recall_headers(h, r) != APR_SUCCESS) { /* try again with next cache type */ @@ -245,25 +246,19 @@ int cache_select(cache_request_rec *cache, request_rec *r) * * RFC2616 13.6 and 14.44 describe the Vary mechanism. */ - vary = apr_pstrdup(r->pool, apr_table_get(h->resp_hdrs, "Vary")); - while (vary && *vary) { - char *name = vary; + vary = cache_strqtok( + apr_pstrdup(r->pool, + cache_table_getm(r->pool, h->resp_hdrs, "Vary")), + CACHE_SEPARATOR, &last); + while (vary) { const char *h1, *h2; - /* isolate header name */ - while (*vary && !apr_isspace(*vary) && (*vary != ',')) - ++vary; - while (apr_isspace(*vary) || (*vary == ',')) { - *vary = '\0'; - ++vary; - } - /* * is this header in the request and the header in the cached * request identical? If not, we give up and do a straight get */ - h1 = apr_table_get(r->headers_in, name); - h2 = apr_table_get(h->req_hdrs, name); + h1 = cache_table_getm(r->pool, r->headers_in, vary); + h2 = cache_table_getm(r->pool, h->req_hdrs, vary); if (h1 == h2) { /* both headers NULL, so a match - do nothing */ } @@ -277,6 +272,7 @@ int cache_select(cache_request_rec *cache, request_rec *r) mismatch = 1; break; } + vary = cache_strqtok(NULL, CACHE_SEPARATOR, &last); } /* no vary match, try next provider */ diff --git a/modules/cache/cache_util.c b/modules/cache/cache_util.c index f82dbdcb21..f85b125423 100644 --- a/modules/cache/cache_util.c +++ b/modules/cache/cache_util.c @@ -27,8 +27,6 @@ extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key; extern module AP_MODULE_DECLARE_DATA cache_module; -#define CACHE_SEPARATOR ", " - /* Determine if "url" matches the hostname, scheme and port and path * in "filter". All but the path comparisons are case-insensitive. */ @@ -862,7 +860,7 @@ CACHE_DECLARE(char *)ap_cache_generate_name(apr_pool_t *p, int dirlevels, * String tokenizer that ignores separator characters within quoted strings * and escaped characters, as per RFC2616 section 2.2. */ -static char *cache_strqtok(char *str, const char *sep, char **last) +char *cache_strqtok(char *str, const char *sep, char **last) { char *token; int quoted = 0; @@ -871,6 +869,10 @@ static char *cache_strqtok(char *str, const char *sep, char **last) str = *last; /* start where we left off */ } + if (!str) { /* no more tokens */ + return NULL; + } + /* skip characters in sep (will terminate at '\0') */ while (*str && ap_strchr_c(sep, *str)) { ++str; diff --git a/modules/cache/cache_util.h b/modules/cache/cache_util.h index 1879fa607d..c75f20812a 100644 --- a/modules/cache/cache_util.h +++ b/modules/cache/cache_util.h @@ -99,6 +99,7 @@ extern "C" { #define CACHE_LOCKNAME_KEY "mod_cache-lockname" #define CACHE_LOCKFILE_KEY "mod_cache-lockfile" #define CACHE_CTX_KEY "mod_cache-ctx" +#define CACHE_SEPARATOR ", " /** * cache_util.c @@ -305,6 +306,12 @@ cache_provider_list *cache_get_providers(request_rec *r, const char *cache_table_getm(apr_pool_t *p, const apr_table_t *t, const char *key); +/** + * String tokenizer that ignores separator characters within quoted strings + * and escaped characters, as per RFC2616 section 2.2. + */ +char *cache_strqtok(char *str, const char *sep, char **last); + #ifdef __cplusplus } #endif