From 0d1a46028ee5cd09e5ecd589910cda86ff609a29 Mon Sep 17 00:00:00 2001 From: Ken Coar Date: Fri, 11 Jan 2002 18:55:27 +0000 Subject: [PATCH] Bring forward the FileETag directive enhancement from 1.3.23-dev. (Passes all 61 of the apache/etags.t test.) Bump MMN due to change to core_dir_config structure (new fields at end). git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@92830 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 5 ++ include/ap_mmn.h | 3 +- include/http_core.h | 23 +++++ modules/http/http_protocol.c | 64 ++++++++++++-- server/core.c | 158 +++++++++++++++++++++++++++++++++++ 5 files changed, 243 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 5b3f8e3ba0..bb228c5040 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ Changes with Apache 2.0.31-dev + *) Add FileETag directive to allow configurable control of what + data are used to form ETag values for file-based URIs. MMN + bumped to 20020111 because of fields added to the end of + the core_dir_config structure. [Ken Coar] + *) Fix a segfault in mod_rewrite's logging code caused by passing the wrong config to ap_get_remote_host(). [Jeff Trawick] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 7d5e47e0bb..3db6e3d1e4 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -89,12 +89,13 @@ * 20020102 (2.0.30-dev) bump for changed type of limit_req_body in * core_dir_config * 20020109 (2.0.31-dev) bump for changed shm and scoreboard declarations + * 20020111 (2.0.31-dev) bump for ETag fields added at end of cor_dir_config */ #define MODULE_MAGIC_COOKIE 0x41503230UL /* "AP20" */ #ifndef MODULE_MAGIC_NUMBER_MAJOR -#define MODULE_MAGIC_NUMBER_MAJOR 20020109 +#define MODULE_MAGIC_NUMBER_MAJOR 20020111 #endif #define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */ #define MODULE_MAGIC_NUMBER MODULE_MAGIC_NUMBER_MAJOR /* backward compat */ diff --git a/include/http_core.h b/include/http_core.h index 402d51152c..9ba793bac8 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -382,6 +382,22 @@ AP_DECLARE(void **) ap_get_request_note(request_rec *r, apr_size_t note_num); typedef unsigned char allow_options_t; typedef unsigned char overrides_t; +/* + * Bits of info that go into making an ETag for a file + * document. Why a long? Because char historically + * proved too short for Options, and int can be different + * sizes on different platforms. + */ +typedef unsigned long etag_components_t; + +#define ETAG_UNSET 0 +#define ETAG_NONE (1 << 0) +#define ETAG_MTIME (1 << 1) +#define ETAG_INODE (1 << 2) +#define ETAG_SIZE (1 << 3) +#define ETAG_BACKWARD (ETAG_MTIME | ETAG_INODE | ETAG_SIZE) +#define ETAG_ALL (ETAG_MTIME | ETAG_INODE | ETAG_SIZE) + typedef enum { srv_sig_unset, srv_sig_off, @@ -489,6 +505,13 @@ typedef struct { const char *output_filters; /* forced with SetOutputFilters */ const char *input_filters; /* forced with SetInputFilters */ int accept_path_info; /* forced with AcceptPathInfo */ + + /* + * What attributes/data should be included in ETag generation? + */ + etag_components_t etag_bits; + etag_components_t etag_add; + etag_components_t etag_remove; } core_dir_config; /* Per-server core configuration */ diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 8aa5fb0162..500d190800 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -1189,6 +1189,14 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, fixup_vary(r); } + /* + * Now remove any ETag response header field if earlier processing + * says so (such as a 'FileETag None' directive). + */ + if (apr_table_get(r->notes, "no-etag") != NULL) { + apr_table_unset(r->headers_out, "ETag"); + } + /* determine the protocol and whether we should use keepalives. */ basic_http_header_check(r, &protocol); ap_set_keepalive(r); @@ -2205,7 +2213,27 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) apr_size_t weak_len; char *etag; char *next; + core_dir_config *cfg; + etag_components_t etag_bits; + etag_components_t bits_added; + + cfg = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add; + + /* + * If it's a file (or we wouldn't be here) and no ETags + * should be set for files, return an empty string and + * note it for the header-sender to ignore. + */ + if (etag_bits & ETAG_NONE) { + apr_table_setn(r->notes, "no-etag", "omit"); + return ""; + } + if (etag_bits == ETAG_UNSET) { + etag_bits = ETAG_BACKWARD; + } /* * Make an ETag header out of various pieces of information. We use * the last-modified date and, if we have a real file, the @@ -2228,7 +2256,10 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) } if (r->finfo.filetype != 0) { - /* [W/]"inode-size-mtime" */ + /* + * ETag gets set to [W/]"inode-size-mtime", modulo any + * FileETag keywords. + */ etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") + 3 * CHARS_PER_UNSIGNED_LONG + 1); next = etag; @@ -2238,16 +2269,31 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) } } *next++ = '"'; - next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode); - *next++ = '-'; - next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size); - *next++ = '-'; - next = etag_ulong_to_hex(next, (unsigned long)r->mtime); + bits_added = 0; + if (etag_bits & ETAG_INODE) { + next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode); + bits_added |= ETAG_INODE; + } + if (etag_bits & ETAG_SIZE) { + if (bits_added != 0) { + *next++ = '-'; + } + next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size); + bits_added |= ETAG_SIZE; + } + if (etag_bits & ETAG_MTIME) { + if (bits_added != 0) { + *next++ = '-'; + } + next = etag_ulong_to_hex(next, (unsigned long)r->mtime); + } *next++ = '"'; - *next = 0; + *next = '\0'; } else { - /* [W/]"mtime" */ + /* + * Not a file document, so just use the mtime: [W/]"mtime" + */ etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") + CHARS_PER_UNSIGNED_LONG + 1); next = etag; @@ -2259,7 +2305,7 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) *next++ = '"'; next = etag_ulong_to_hex(next, (unsigned long)r->mtime); *next++ = '"'; - *next = 0; + *next = '\0'; } return etag; diff --git a/server/core.c b/server/core.c index a823de6352..5ea35adf98 100644 --- a/server/core.c +++ b/server/core.c @@ -166,6 +166,13 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir) conf->output_filters = NULL; conf->input_filters = NULL; + /* + * Flag for use of inodes in ETags. + */ + conf->etag_bits = ETAG_UNSET; + conf->etag_add = ETAG_UNSET; + conf->etag_remove = ETAG_UNSET; + return (void *)conf; } @@ -330,6 +337,26 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv) conf->input_filters = new->input_filters; } + /* + * Now merge the setting of the FileETag directive. + */ + if (new->etag_bits == ETAG_UNSET) { + conf->etag_add = + (conf->etag_add & (~ new->etag_remove)) | new->etag_add; + conf->etag_remove = + (conf->opts_remove & (~ new->etag_add)) | new->etag_remove; + conf->etag_bits = + (conf->etag_bits & (~ conf->etag_remove)) | conf->etag_add; + } + else { + conf->etag_bits = new->etag_bits; + conf->etag_add = new->etag_add; + conf->etag_remove = new->etag_remove; + } + if (conf->etag_bits != ETAG_NONE) { + conf->etag_bits &= (~ ETAG_NONE); + } + return (void*)conf; } @@ -1131,6 +1158,135 @@ static const char *set_options(cmd_parms *cmd, void *d_, const char *l) return NULL; } +/* + * Note what data should be used when forming file ETag values. + * It would be nicer to do this as an ITERATE, but then we couldn't + * remember the +/- state properly. + */ +static const char *set_etag_bits(cmd_parms *cmd, void *mconfig, + const char *args_p) +{ + core_dir_config *cfg; + etag_components_t bit; + char action; + char *token; + const char *args; + int valid; + int first; + int explicit; + + cfg = (core_dir_config *) mconfig; + + args = args_p; + first = 1; + explicit = 0; + while (args[0] != '\0') { + action = '*'; + bit = ETAG_UNSET; + valid = 1; + token = ap_getword_conf(cmd->pool, &args); + if ((*token == '+') || (*token == '-')) { + action = *token; + token++; + } + else { + /* + * The occurrence of an absolute setting wipes + * out any previous relative ones. The first such + * occurrence forgets any inherited ones, too. + */ + if (first) { + cfg->etag_bits = ETAG_UNSET; + cfg->etag_add = ETAG_UNSET; + cfg->etag_remove = ETAG_UNSET; + first = 0; + } + } + + if (strcasecmp(token, "None") == 0) { + if (action != '*') { + valid = 0; + } + else { + cfg->etag_bits = bit = ETAG_NONE; + explicit = 1; + } + } + else if (strcasecmp(token, "All") == 0) { + if (action != '*') { + valid = 0; + } + else { + explicit = 1; + cfg->etag_bits = bit = ETAG_ALL; + } + } + else if (strcasecmp(token, "Size") == 0) { + bit = ETAG_SIZE; + } + else if ((strcasecmp(token, "LMTime") == 0) + || (strcasecmp(token, "MTime") == 0) + || (strcasecmp(token, "LastModified") == 0)) { + bit = ETAG_MTIME; + } + else if (strcasecmp(token, "INode") == 0) { + bit = ETAG_INODE; + } + else { + return ap_pstrcat(cmd->pool, "Unknown keyword '", + token, "' for ", cmd->cmd->name, + " directive", NULL); + } + + if (! valid) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, " keyword '", + token, "' cannot be used with '+' or '-'", + NULL); + } + + if (action == '+') { + /* + * Make sure it's in the 'add' list and absent from the + * 'subtract' list. + */ + cfg->etag_add |= bit; + cfg->etag_remove &= (~ bit); + } + else if (action == '-') { + cfg->etag_remove |= bit; + cfg->etag_add &= (~ bit); + } + else { + /* + * Non-relative values wipe out any + or - values + * accumulated so far. + */ + cfg->etag_bits |= bit; + cfg->etag_add = ETAG_UNSET; + cfg->etag_remove = ETAG_UNSET; + explicit = 1; + } + } + + /* + * Any setting at all will clear the 'None' and 'Unset' bits. + */ + + if (cfg->etag_add != ETAG_UNSET) { + cfg->etag_add &= (~ ETAG_UNSET); + } + if (cfg->etag_remove != ETAG_UNSET) { + cfg->etag_remove &= (~ ETAG_UNSET); + } + if (explicit) { + cfg->etag_bits &= (~ ETAG_UNSET); + if ((cfg->etag_bits & ETAG_NONE) != ETAG_NONE) { + cfg->etag_bits &= (~ ETAG_NONE); + } + } + return NULL; +} + static const char *satisfy(cmd_parms *cmd, void *c_, const char *arg) { core_dir_config *c=c_; @@ -2459,6 +2615,8 @@ AP_INIT_RAW_ARGS("Options", set_options, NULL, OR_OPTIONS, AP_INIT_TAKE1("DefaultType", ap_set_string_slot, (void*)APR_XtOffsetOf (core_dir_config, ap_default_type), OR_FILEINFO, "the default MIME type for untypable files"), +AP_INIT_RAW_ARGS("FileETag", set_etag_bits, NULL, OR_FILEINFO, + "Specify components used to construct a file's ETag"), /* Old server config file commands */ -- 2.50.1