mirror of
https://github.com/apache/httpd.git
synced 2025-07-30 20:03:10 +03:00
mod_cache: Ignore response headers specified by no-cache=header and
private=header as specified by RFC2616 14.9.1 What is Cacheable. Ensure that these headers are still processed when multiple Cache-Control headers are present in the response. PR 54706 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1478382 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
6
CHANGES
6
CHANGES
@ -1,6 +1,12 @@
|
|||||||
-*- coding: utf-8 -*-
|
-*- coding: utf-8 -*-
|
||||||
Changes with Apache 2.5.0
|
Changes with Apache 2.5.0
|
||||||
|
|
||||||
|
*) mod_cache: Ignore response headers specified by no-cache=header and
|
||||||
|
private=header as specified by RFC2616 14.9.1 What is Cacheable. Ensure
|
||||||
|
that these headers are still processed when multiple Cache-Control
|
||||||
|
headers are present in the response. PR 54706 [Graham Leggett,
|
||||||
|
Yann Ylavic <ylavic.dev gmail.com>]
|
||||||
|
|
||||||
*) mod_cache: Invalidate cached entities in response to RFC2616 Section
|
*) mod_cache: Invalidate cached entities in response to RFC2616 Section
|
||||||
13.10 Invalidation After Updates or Deletions. PR 15868 [Graham
|
13.10 Invalidation After Updates or Deletions. PR 15868 [Graham
|
||||||
Leggett]
|
Leggett]
|
||||||
|
307
modules/cache/cache_util.c
vendored
307
modules/cache/cache_util.c
vendored
@ -543,8 +543,6 @@ int cache_check_freshness(cache_handle_t *h, cache_request_rec *cache,
|
|||||||
|
|
||||||
/* These come from the cached entity. */
|
/* These come from the cached entity. */
|
||||||
if (h->cache_obj->info.control.no_cache
|
if (h->cache_obj->info.control.no_cache
|
||||||
|| h->cache_obj->info.control.no_cache_header
|
|
||||||
|| h->cache_obj->info.control.private_header
|
|
||||||
|| h->cache_obj->info.control.invalidated) {
|
|| h->cache_obj->info.control.invalidated) {
|
||||||
/*
|
/*
|
||||||
* The cached entity contained Cache-Control: no-cache, or a
|
* The cached entity contained Cache-Control: no-cache, or a
|
||||||
@ -860,95 +858,6 @@ CACHE_DECLARE(char *)ap_cache_generate_name(apr_pool_t *p, int dirlevels,
|
|||||||
return apr_pstrdup(p, hashfile);
|
return apr_pstrdup(p, hashfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a new table consisting of those elements from an
|
|
||||||
* headers table that are allowed to be stored in a cache.
|
|
||||||
*/
|
|
||||||
CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers(apr_pool_t *pool,
|
|
||||||
apr_table_t *t,
|
|
||||||
server_rec *s)
|
|
||||||
{
|
|
||||||
cache_server_conf *conf;
|
|
||||||
char **header;
|
|
||||||
int i;
|
|
||||||
apr_table_t *headers_out;
|
|
||||||
|
|
||||||
/* Short circuit the common case that there are not
|
|
||||||
* (yet) any headers populated.
|
|
||||||
*/
|
|
||||||
if (t == NULL) {
|
|
||||||
return apr_table_make(pool, 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
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");
|
|
||||||
apr_table_unset(headers_out, "Proxy-Authorization");
|
|
||||||
apr_table_unset(headers_out, "TE");
|
|
||||||
apr_table_unset(headers_out, "Trailers");
|
|
||||||
apr_table_unset(headers_out, "Transfer-Encoding");
|
|
||||||
apr_table_unset(headers_out, "Upgrade");
|
|
||||||
|
|
||||||
conf = (cache_server_conf *)ap_get_module_config(s->module_config,
|
|
||||||
&cache_module);
|
|
||||||
|
|
||||||
/* Remove the user defined headers set with CacheIgnoreHeaders.
|
|
||||||
* This may break RFC 2616 compliance on behalf of the administrator.
|
|
||||||
*/
|
|
||||||
header = (char **)conf->ignore_headers->elts;
|
|
||||||
for (i = 0; i < conf->ignore_headers->nelts; i++) {
|
|
||||||
apr_table_unset(headers_out, header[i]);
|
|
||||||
}
|
|
||||||
return headers_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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_headers_in(request_rec *r)
|
|
||||||
{
|
|
||||||
return ap_cache_cacheable_headers(r->pool, r->headers_in, r->server);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a new table consisting of those elements from an output
|
|
||||||
* headers table that are allowed to be stored in a cache;
|
|
||||||
* ensure there is a content type and capture any errors.
|
|
||||||
*/
|
|
||||||
CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_out(request_rec *r)
|
|
||||||
{
|
|
||||||
apr_table_t *headers_out;
|
|
||||||
|
|
||||||
headers_out = apr_table_overlay(r->pool, r->headers_out,
|
|
||||||
r->err_headers_out);
|
|
||||||
|
|
||||||
apr_table_clear(r->err_headers_out);
|
|
||||||
|
|
||||||
headers_out = ap_cache_cacheable_headers(r->pool, headers_out,
|
|
||||||
r->server);
|
|
||||||
|
|
||||||
if (!apr_table_get(headers_out, "Content-Type")
|
|
||||||
&& r->content_type) {
|
|
||||||
apr_table_setn(headers_out, "Content-Type",
|
|
||||||
ap_make_content_type(r, r->content_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!apr_table_get(headers_out, "Content-Encoding")
|
|
||||||
&& r->content_encoding) {
|
|
||||||
apr_table_setn(headers_out, "Content-Encoding",
|
|
||||||
r->content_encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String tokenizer that ignores separator characters within quoted strings
|
* String tokenizer that ignores separator characters within quoted strings
|
||||||
* and escaped characters, as per RFC2616 section 2.2.
|
* and escaped characters, as per RFC2616 section 2.2.
|
||||||
@ -981,7 +890,7 @@ static char *cache_strqtok(char *str, const char *sep, char **last)
|
|||||||
*last = token;
|
*last = token;
|
||||||
while (**last) {
|
while (**last) {
|
||||||
if (!quoted) {
|
if (!quoted) {
|
||||||
if (**last == '\"') {
|
if (**last == '\"' && !ap_strchr_c(sep, '\"')) {
|
||||||
quoted = 1;
|
quoted = 1;
|
||||||
++*last;
|
++*last;
|
||||||
}
|
}
|
||||||
@ -1071,9 +980,7 @@ int ap_cache_control(request_rec *r, cache_control_t *cc,
|
|||||||
/* ...then try slowest cases */
|
/* ...then try slowest cases */
|
||||||
else if (!strncasecmp(token, "no-cache", 8)) {
|
else if (!strncasecmp(token, "no-cache", 8)) {
|
||||||
if (token[8] == '=') {
|
if (token[8] == '=') {
|
||||||
if (apr_table_get(headers, token + 9)) {
|
cc->no_cache_header = 1;
|
||||||
cc->no_cache_header = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!token[8]) {
|
else if (!token[8]) {
|
||||||
cc->no_cache = 1;
|
cc->no_cache = 1;
|
||||||
@ -1148,9 +1055,7 @@ int ap_cache_control(request_rec *r, cache_control_t *cc,
|
|||||||
}
|
}
|
||||||
else if (!strncasecmp(token, "private", 7)) {
|
else if (!strncasecmp(token, "private", 7)) {
|
||||||
if (token[7] == '=') {
|
if (token[7] == '=') {
|
||||||
if (apr_table_get(headers, token + 8)) {
|
cc->private_header = 1;
|
||||||
cc->private_header = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!token[7]) {
|
else if (!token[7]) {
|
||||||
cc->private = 1;
|
cc->private = 1;
|
||||||
@ -1181,3 +1086,209 @@ int ap_cache_control(request_rec *r, cache_control_t *cc,
|
|||||||
|
|
||||||
return (cc_header != NULL || pragma_header != NULL);
|
return (cc_header != NULL || pragma_header != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the Cache-Control, identifying and removing headers that
|
||||||
|
* exist as tokens after the no-cache and private tokens.
|
||||||
|
*/
|
||||||
|
static int cache_control_remove(request_rec *r, const char *cc_header,
|
||||||
|
apr_table_t *headers)
|
||||||
|
{
|
||||||
|
char *last, *slast;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
if (cc_header) {
|
||||||
|
char *header = apr_pstrdup(r->pool, cc_header);
|
||||||
|
char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
|
||||||
|
while (token) {
|
||||||
|
switch (token[0]) {
|
||||||
|
case 'n':
|
||||||
|
case 'N': {
|
||||||
|
if (!strncmp(token, "no-cache", 8)
|
||||||
|
|| !strncasecmp(token, "no-cache", 8)) {
|
||||||
|
if (token[8] == '=') {
|
||||||
|
const char *header = cache_strqtok(token + 9,
|
||||||
|
CACHE_SEPARATOR "\"", &slast);
|
||||||
|
while (header) {
|
||||||
|
apr_table_unset(headers, header);
|
||||||
|
header = cache_strqtok(NULL, CACHE_SEPARATOR "\"",
|
||||||
|
&slast);
|
||||||
|
}
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p':
|
||||||
|
case 'P': {
|
||||||
|
if (!strncmp(token, "private", 7)
|
||||||
|
|| !strncasecmp(token, "private", 7)) {
|
||||||
|
if (token[7] == '=') {
|
||||||
|
const char *header = cache_strqtok(token + 8,
|
||||||
|
CACHE_SEPARATOR "\"", &slast);
|
||||||
|
while (header) {
|
||||||
|
apr_table_unset(headers, header);
|
||||||
|
header = cache_strqtok(NULL, CACHE_SEPARATOR "\"",
|
||||||
|
&slast);
|
||||||
|
}
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = cache_strqtok(NULL, CACHE_SEPARATOR, &last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new table consisting of those elements from an
|
||||||
|
* headers table that are allowed to be stored in a cache.
|
||||||
|
*/
|
||||||
|
CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers(apr_pool_t *pool,
|
||||||
|
apr_table_t *t,
|
||||||
|
server_rec *s)
|
||||||
|
{
|
||||||
|
cache_server_conf *conf;
|
||||||
|
char **header;
|
||||||
|
int i;
|
||||||
|
apr_table_t *headers_out;
|
||||||
|
|
||||||
|
/* Short circuit the common case that there are not
|
||||||
|
* (yet) any headers populated.
|
||||||
|
*/
|
||||||
|
if (t == NULL) {
|
||||||
|
return apr_table_make(pool, 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
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");
|
||||||
|
apr_table_unset(headers_out, "Proxy-Authorization");
|
||||||
|
apr_table_unset(headers_out, "TE");
|
||||||
|
apr_table_unset(headers_out, "Trailers");
|
||||||
|
apr_table_unset(headers_out, "Transfer-Encoding");
|
||||||
|
apr_table_unset(headers_out, "Upgrade");
|
||||||
|
|
||||||
|
conf = (cache_server_conf *)ap_get_module_config(s->module_config,
|
||||||
|
&cache_module);
|
||||||
|
|
||||||
|
/* Remove the user defined headers set with CacheIgnoreHeaders.
|
||||||
|
* This may break RFC 2616 compliance on behalf of the administrator.
|
||||||
|
*/
|
||||||
|
header = (char **)conf->ignore_headers->elts;
|
||||||
|
for (i = 0; i < conf->ignore_headers->nelts; i++) {
|
||||||
|
apr_table_unset(headers_out, header[i]);
|
||||||
|
}
|
||||||
|
return headers_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_headers_in(request_rec *r)
|
||||||
|
{
|
||||||
|
return ap_cache_cacheable_headers(r->pool, r->headers_in, r->server);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new table consisting of those elements from an output
|
||||||
|
* headers table that are allowed to be stored in a cache;
|
||||||
|
* ensure there is a content type and capture any errors.
|
||||||
|
*/
|
||||||
|
CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_out(request_rec *r)
|
||||||
|
{
|
||||||
|
apr_table_t *headers_out;
|
||||||
|
|
||||||
|
headers_out = apr_table_overlay(r->pool, r->headers_out,
|
||||||
|
r->err_headers_out);
|
||||||
|
|
||||||
|
apr_table_clear(r->err_headers_out);
|
||||||
|
|
||||||
|
headers_out = ap_cache_cacheable_headers(r->pool, headers_out,
|
||||||
|
r->server);
|
||||||
|
|
||||||
|
cache_control_remove(r,
|
||||||
|
cache_table_getm(r->pool, headers_out, "Cache-Control"),
|
||||||
|
headers_out);
|
||||||
|
|
||||||
|
if (!apr_table_get(headers_out, "Content-Type")
|
||||||
|
&& r->content_type) {
|
||||||
|
apr_table_setn(headers_out, "Content-Type",
|
||||||
|
ap_make_content_type(r, r->content_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apr_table_get(headers_out, "Content-Encoding")
|
||||||
|
&& r->content_encoding) {
|
||||||
|
apr_table_setn(headers_out, "Content-Encoding",
|
||||||
|
r->content_encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
apr_pool_t *p;
|
||||||
|
const char *first;
|
||||||
|
apr_array_header_t *merged;
|
||||||
|
} cache_table_getm_t;
|
||||||
|
|
||||||
|
static int cache_table_getm_do(void *v, const char *key, const char *val)
|
||||||
|
{
|
||||||
|
cache_table_getm_t *state = (cache_table_getm_t *) v;
|
||||||
|
|
||||||
|
if (!state->first) {
|
||||||
|
/**
|
||||||
|
* The most common case is a single header, and this is covered by
|
||||||
|
* a fast path that doesn't allocate any memory. On the second and
|
||||||
|
* subsequent header, an array is created and the array concatenated
|
||||||
|
* together to form the final value.
|
||||||
|
*/
|
||||||
|
state->first = val;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const char **elt;
|
||||||
|
if (!state->merged) {
|
||||||
|
state->merged = apr_array_make(state->p, 10, sizeof(const char *));
|
||||||
|
elt = apr_array_push(state->merged);
|
||||||
|
*elt = state->first;
|
||||||
|
}
|
||||||
|
elt = apr_array_push(state->merged);
|
||||||
|
*elt = val;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cache_table_getm(apr_pool_t *p, const apr_table_t *t,
|
||||||
|
const char *key)
|
||||||
|
{
|
||||||
|
cache_table_getm_t state;
|
||||||
|
|
||||||
|
state.p = p;
|
||||||
|
state.first = NULL;
|
||||||
|
state.merged = NULL;
|
||||||
|
|
||||||
|
apr_table_do(cache_table_getm_do, &state, t, key, NULL);
|
||||||
|
|
||||||
|
if (!state.first) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (!state.merged) {
|
||||||
|
return state.first;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return apr_array_pstrcat(p, state.merged, ',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
13
modules/cache/cache_util.h
vendored
13
modules/cache/cache_util.h
vendored
@ -292,6 +292,19 @@ apr_status_t cache_remove_lock(cache_server_conf *conf,
|
|||||||
cache_provider_list *cache_get_providers(request_rec *r,
|
cache_provider_list *cache_get_providers(request_rec *r,
|
||||||
cache_server_conf *conf, apr_uri_t uri);
|
cache_server_conf *conf, apr_uri_t uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value from a table, where the table may contain multiple
|
||||||
|
* values for a given key.
|
||||||
|
*
|
||||||
|
* When the table contains a single value, that value is returned
|
||||||
|
* unchanged.
|
||||||
|
*
|
||||||
|
* When the table contains two or more values for a key, all values
|
||||||
|
* for the key are returned, separated by commas.
|
||||||
|
*/
|
||||||
|
const char *cache_table_getm(apr_pool_t *p, const apr_table_t *t,
|
||||||
|
const char *key);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
14
modules/cache/mod_cache.c
vendored
14
modules/cache/mod_cache.c
vendored
@ -918,12 +918,12 @@ static apr_status_t cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
|
|||||||
if (etag == NULL) {
|
if (etag == NULL) {
|
||||||
etag = apr_table_get(r->headers_out, "Etag");
|
etag = apr_table_get(r->headers_out, "Etag");
|
||||||
}
|
}
|
||||||
cc_out = apr_table_get(r->err_headers_out, "Cache-Control");
|
cc_out = cache_table_getm(r->pool, r->err_headers_out, "Cache-Control");
|
||||||
pragma = apr_table_get(r->err_headers_out, "Pragma");
|
pragma = cache_table_getm(r->pool, r->err_headers_out, "Pragma");
|
||||||
headers = r->err_headers_out;
|
headers = r->err_headers_out;
|
||||||
if (!cc_out && !pragma) {
|
if (!cc_out && !pragma) {
|
||||||
cc_out = apr_table_get(r->headers_out, "Cache-Control");
|
cc_out = cache_table_getm(r->pool, r->headers_out, "Cache-Control");
|
||||||
pragma = apr_table_get(r->headers_out, "Pragma");
|
pragma = cache_table_getm(r->pool, r->headers_out, "Pragma");
|
||||||
headers = r->headers_out;
|
headers = r->headers_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -932,8 +932,10 @@ static apr_status_t cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
|
|||||||
*/
|
*/
|
||||||
if (r->status == HTTP_NOT_MODIFIED && cache->stale_handle && !cc_out
|
if (r->status == HTTP_NOT_MODIFIED && cache->stale_handle && !cc_out
|
||||||
&& !pragma) {
|
&& !pragma) {
|
||||||
cc_out = apr_table_get(cache->stale_handle->resp_hdrs, "Cache-Control");
|
cc_out = cache_table_getm(r->pool, cache->stale_handle->resp_hdrs,
|
||||||
pragma = apr_table_get(cache->stale_handle->resp_hdrs, "Pragma");
|
"Cache-Control");
|
||||||
|
pragma = cache_table_getm(r->pool, cache->stale_handle->resp_hdrs,
|
||||||
|
"Pragma");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse the cache control header */
|
/* Parse the cache control header */
|
||||||
|
Reference in New Issue
Block a user