You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by co...@apache.org on 2002/01/05 18:13:03 UTC

cvs commit: apache-1.3/src/main http_core.c http_protocol.c

coar        02/01/05 09:13:03

  Modified:    src      CHANGES
               src/include http_core.h
               src/main http_core.c http_protocol.c
  Log:
  	Replace automatic ETag generation with configurable algorithm
  	defined by FileETag directive.  Keywords are All, None,
  	[+|-]INode, [+|-]Size, [+|-]MTime.  Server farms with content
  	fanout may want to use 'FileETag MTime Size' to keep the system-
  	variant inode from being included and busting caches.
  PR:		7010
  Submitted by:	Based on an idea proposed by Phil Dietz
  
  Revision  Changes    Path
  1.1749    +8 -0      apache-1.3/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/CHANGES,v
  retrieving revision 1.1748
  retrieving revision 1.1749
  diff -u -u -r1.1748 -r1.1749
  --- CHANGES	2 Jan 2002 22:56:11 -0000	1.1748
  +++ CHANGES	5 Jan 2002 17:13:02 -0000	1.1749
  @@ -1,5 +1,13 @@
   Changes with Apache 1.3.23
   
  +  *) Add FileETag directive to control fields used when constructing
  +     an ETag for a file-based resource.  Historically the inode,
  +     size, and mtimehave been used, but the inode factor broke
  +     caching for systems with content fan-out across multiple
  +     back-end servers.  Now the fields used in the construction
  +     can be controlled by configuration directives.
  +     [Ken Coar, from a patch by Phil Dietz]
  +
     *) NetWare: Fixed the access forbidden problem when requesting an
        empty directory rather than showing the empty listing.  
        [Charles Goldman, Guenter Knauf <gk...@gknw.de>]
  
  
  
  1.66      +22 -0     apache-1.3/src/include/http_core.h
  
  Index: http_core.h
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/include/http_core.h,v
  retrieving revision 1.65
  retrieving revision 1.66
  diff -u -u -r1.65 -r1.66
  --- http_core.h	28 Dec 2001 05:03:06 -0000	1.65
  +++ http_core.h	5 Jan 2002 17:13:02 -0000	1.66
  @@ -180,6 +180,21 @@
   
   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 struct {
       /* path of the directory/regex/etc.  see also d_is_fnmatch below */
  @@ -308,6 +323,13 @@
       int ebcdicconversion_debug_header; /* whether to add an X-EBCDIC-Debug-{In,Out} header to the response */
   #endif
   #endif /* CHARSET_EBCDIC */
  +
  +    /*
  +     * 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;
   
  
  
  
  1.301     +159 -1    apache-1.3/src/main/http_core.c
  
  Index: http_core.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/main/http_core.c,v
  retrieving revision 1.300
  retrieving revision 1.301
  diff -u -u -r1.300 -r1.301
  --- http_core.c	28 Dec 2001 05:03:07 -0000	1.300
  +++ http_core.c	5 Jan 2002 17:13:02 -0000	1.301
  @@ -170,6 +170,13 @@
   #endif
   #endif /* CHARSET_EBCDIC */
   
  +    /*
  +     * 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;
   }
   
  @@ -194,7 +201,7 @@
       conf->d_is_fnmatch = new->d_is_fnmatch;
       conf->d_components = new->d_components;
       conf->r = new->r;
  -    
  +
       if (new->opts & OPT_UNSET) {
   	/* there was no explicit setting of new->opts, so we merge
   	 * preserve the invariant (opts_add & opts_remove) == 0
  @@ -319,6 +326,26 @@
   #endif
   #endif /* CHARSET_EBCDIC */
   
  +    /*
  +     * 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;
   }
   
  @@ -2985,6 +3012,135 @@
   #endif
   #endif /* CHARSET_EBCDIC */
   
  +/*
  + * Note whether file inodes may be used when forming 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;
  +}
  +
   /* Note --- ErrorDocument will now work from .htaccess files.  
    * The AllowOverride of Fileinfo allows webmasters to turn it off
    */
  @@ -3276,6 +3432,8 @@
   #endif
   #endif /* CHARSET_EBCDIC */
   
  +{ "FileETag", set_etag_bits, NULL, OR_FILEINFO, RAW_ARGS,
  +  "Specify components used to construct a file's ETag"},
   { NULL }
   };
   
  
  
  
  1.303     +52 -6     apache-1.3/src/main/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/main/http_protocol.c,v
  retrieving revision 1.302
  retrieving revision 1.303
  diff -u -u -r1.302 -r1.303
  --- http_protocol.c	28 Dec 2001 05:03:07 -0000	1.302
  +++ http_protocol.c	5 Jan 2002 17:13:03 -0000	1.303
  @@ -649,7 +649,15 @@
   {
       char *etag;
       char *weak;
  +    core_dir_config *cfg;
  +    etag_components_t etag_bits;
   
  +    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 (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
  @@ -666,11 +674,43 @@
       weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/";
   
       if (r->finfo.st_mode != 0) {
  -        etag = ap_psprintf(r->pool,
  -                    "%s\"%lx-%lx-%lx\"", weak,
  -                    (unsigned long) r->finfo.st_ino,
  -                    (unsigned long) r->finfo.st_size,
  -                    (unsigned long) r->mtime);
  +        char **ent;
  +        array_header *components;
  +        int i;
  +
  +        /*
  +         * 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 ap_send_header_field() to ignore.
  +         */
  +        if (etag_bits & ETAG_NONE) {
  +            ap_table_setn(r->notes, "no-etag", "omit");
  +            return "";
  +        }
  +
  +        components = ap_make_array(r->pool, 4, sizeof(char *));
  +        if (etag_bits & ETAG_INODE) {
  +            ent = (char **) ap_push_array(components);
  +            *ent = ap_psprintf(r->pool, "%lx",
  +                               (unsigned long) r->finfo.st_ino);
  +        }
  +        if (etag_bits & ETAG_SIZE) {
  +            ent = (char **) ap_push_array(components);
  +            *ent = ap_psprintf(r->pool, "%lx",
  +                               (unsigned long) r->finfo.st_size);
  +        }
  +        if (etag_bits & ETAG_MTIME) {
  +            ent = (char **) ap_push_array(components);
  +            *ent = ap_psprintf(r->pool, "%lx", (unsigned long) r->mtime);
  +        }
  +        ent = (char **) components->elts;
  +        etag = ap_pstrcat(r->pool, weak, "\"", NULL);
  +        for (i = 0; i < components->nelts; ++i) {
  +            etag = ap_psprintf(r->pool, "%s%s%s", etag,
  +                               (i == 0 ? "" : "-"),
  +                               ent[i]);
  +        }
  +        etag = ap_pstrcat(r->pool, etag, "\"", NULL);
       }
       else {
           etag = ap_psprintf(r->pool, "%s\"%lx\"", weak,
  @@ -1458,8 +1498,14 @@
    * It returns true unless there was a write error of some kind.
    */
   API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r,
  -    const char *fieldname, const char *fieldval)
  +                                            const char *fieldname,
  +                                            const char *fieldval)
   {
  +    if (strcasecmp(fieldname, "ETag") == 0) {
  +        if (ap_table_get(r->notes, "no-etag") != NULL) {
  +            return 1;
  +        }
  +    }
       return (0 < ap_rvputs(r, fieldname, ": ", fieldval, CRLF, NULL));
   }