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]
* 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 */
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,
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 */
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);
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
}
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;
}
}
*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;
*next++ = '"';
next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
*next++ = '"';
- *next = 0;
+ *next = '\0';
}
return etag;
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;
}
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;
}
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_;
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 */