]> granicus.if.org Git - apache/commitdiff
Bring forward the FileETag directive enhancement from 1.3.23-dev.
authorKen Coar <coar@apache.org>
Fri, 11 Jan 2002 18:55:27 +0000 (18:55 +0000)
committerKen Coar <coar@apache.org>
Fri, 11 Jan 2002 18:55:27 +0000 (18:55 +0000)
(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
include/ap_mmn.h
include/http_core.h
modules/http/http_protocol.c
server/core.c

diff --git a/CHANGES b/CHANGES
index 5b3f8e3ba0c2baf8a2ab54124999c0061bf4b807..bb228c504004484a6093ccfd0390ce7bbde38d77 100644 (file)
--- 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]
 
index 7d5e47e0bb6efad9f6a301e33d3e1b3b6aa2e7e6..3db6e3d1e4996637c31b4d5655212b0851c7ca6b 100644 (file)
  * 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 */
index 402d51152cb0b5c8d9aeaa0d109cb4d8877721c6..9ba793bac87b2af478f1bfff8f941bac8959fa6f 100644 (file)
@@ -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 */
index 8aa5fb01625a6de2c5cf0d0b09c14c3bb024f451..500d190800a72f49bdb10fcdde6df3ea7edb2e3a 100644 (file)
@@ -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;
index a823de6352b2eb05ab6c2b40d51b1deab7569807..5ea35adf98705c8990788a84141ebf227fdc7108 100644 (file)
@@ -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 */