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));
}