]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Merge r1879285, r1879322, r1879345, r1879346, r1879465, r1879469, r1879470, r1879488...
authorYann Ylavic <ylavic@apache.org>
Tue, 9 Mar 2021 16:28:23 +0000 (16:28 +0000)
committerYann Ylavic <ylavic@apache.org>
Tue, 9 Mar 2021 16:28:23 +0000 (16:28 +0000)
"[mod_dav_fs etag handling] should really honor the FileETag setting".
- It now does.
- Add "Digest" to FileETag directive, allowing a strong ETag to be
  generated using a file digest.
- Add ap_make_etag_ex() and ap_set_etag_fd() to allow full control over
  ETag generation.
- Add concept of "binary notes" to request_rec, allowing packed bit flags
  to be added to a request.
- First binary note - AP_REQUEST_STRONG_ETAG - allows modules to force
  the ETag to a strong ETag to comply with RFC requirements, such as those
  mandated by various WebDAV extensions.

Be defensive when the request is missing.

Remove tabs.

Use a dedicated constant for the base64 sha1 length.

Be defensive when calculating the digest. Make sure the offset is initialised
to zero before reading the current offset.

Seek to the start of the file when calculating the ETag.

Add log numbers to util_etag.c.

Add MMAP support to ETag generation.

Use a brigade instead of direct reads, allow APR to handle MMAP.

Bump the logno.

Submitted by: minfrin
Reviewed by: minfrin, jim, ylavic

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1887384 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/core.xml
include/ap_mmn.h
include/http_core.h
include/http_protocol.h
include/httpd.h
modules/dav/fs/repos.c
modules/http/http_etag.c
modules/test/mod_dialup.c
server/core.c

diff --git a/CHANGES b/CHANGES
index 76f0cfb7305ff563aa6a426b58ae0cc22545fc2f..fa3d811973a6d5a3d39ade0be65d2c459b89b522 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,19 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.4.47
 
+  *) "[mod_dav_fs etag handling] should really honor the FileETag setting".
+     - It now does.
+     - Add "Digest" to FileETag directive, allowing a strong ETag to be
+       generated using a file digest.
+     - Add ap_make_etag_ex() and ap_set_etag_fd() to allow full control over
+       ETag generation.
+     - Add concept of "binary notes" to request_rec, allowing packed bit flags
+       to be added to a request.
+     - First binary note - AP_REQUEST_STRONG_ETAG - allows modules to force
+       the ETag to a strong ETag to comply with RFC requirements, such as those
+       mandated by various WebDAV extensions.
+     [Graham Leggett]
+
   *) mod_proxy_http: Fix a possibly crash when the origin connection gets
      interrupted before completion.  PR 64234.
      [Barnim Dzwillo <dzwillo strato.de>, Ruediger Pluem]
index 62737f602443b083d1a57bf07cf072b96f2b4b08..6b9f1f03859644645394699fdc12e30de0e7b2bd 100644 (file)
@@ -1862,15 +1862,18 @@ earlier.</compatibility>
          <highlight language="config">
 FileETag INode MTime Size
          </highlight></dd>
+     <dt><strong>Digest</strong></dt>
+     <dd>If a document is file-based, the <code>ETag</code> field will be
+       calculated by taking the digest over the file.</dd>
      <dt><strong>None</strong></dt>
      <dd>If a document is file-based, no <code>ETag</code> field will be
        included in the response</dd>
     </dl>
 
-    <p>The <code>INode</code>, <code>MTime</code>, and <code>Size</code>
-    keywords may be prefixed with either <code>+</code> or <code>-</code>,
-    which allow changes to be made to the default setting inherited
-    from a broader scope. Any keyword appearing without such a prefix
+    <p>The <code>INode</code>, <code>MTime</code>, <code>Size</code> and
+    <code>Digest</code> keywords may be prefixed with either <code>+</code>
+    or <code>-</code>, which allow changes to be made to the default setting
+    inherited from a broader scope. Any keyword appearing without such a prefix
     immediately and completely cancels the inherited setting.</p>
 
     <p>If a directory's configuration includes
@@ -1879,18 +1882,10 @@ FileETag INode MTime Size
     the setting for that subdirectory (which will be inherited by
     any sub-subdirectories that don't override it) will be equivalent to
     <code>FileETag&nbsp;MTime&nbsp;Size</code>.</p>
-    <note type="warning"><title>Warning</title>
-    Do not change the default for directories or locations that have WebDAV
-    enabled and use <module>mod_dav_fs</module> as a storage provider.
-    <module>mod_dav_fs</module> uses <code>MTime&nbsp;Size</code>
-    as a fixed format for <code>ETag</code> comparisons on conditional requests.
-    These conditional requests will break if the <code>ETag</code> format is
-    changed via <directive>FileETag</directive>.
-    </note>
     <note><title>Server Side Includes</title>
     An ETag is not generated for responses parsed by <module>mod_include</module>
-    since the response entity can change without a change of the INode, MTime, or Size
-    of the static file with embedded SSI directives.
+    since the response entity can change without a change of the INode, MTime,
+    Size or Digest of the static file with embedded SSI directives.
     </note>
 
 </usage>
index 9a4f376a92bea84bb4c5d27f3e7ac8ef9cccb63c..a204e438bd5c5a59858f0212cb644605f26c6f7c 100644 (file)
  * 20120211.100 (2.4.47-dev) Add ap_proxy_prefetch_input(),
  *                           ap_proxy_spool_input() and
  *                           ap_proxy_read_input().
+ * 20120211.101 (2.4.47-dev) ETAG_DIGEST in http_core.h. struct etag_rec,
+ *                           ap_make_etag_ex() and ap_set_etag_fd() in
+ *                           http_protocol.h. ap_request_bnotes_t,
+ *                           AP_REQUEST_STRONG_ETAG, AP_REQUEST_GET_BNOTE,
+ *                           AP_REQUEST_SET_BNOTE and AP_REQUEST_IS_STRONG_ETAG
+ *                           in httpd.h.
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20120211
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 100                 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 101                 /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index c1612c18a127ca5e3859c4bf07a3dabe6d243842..0d7561b4a9b25738dff7b64a6476c841a8b6ee8c 100644 (file)
@@ -489,12 +489,13 @@ typedef unsigned int overrides_t;
  */
 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_ALL   (ETAG_MTIME | ETAG_INODE | ETAG_SIZE)
+#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_DIGEST (1 << 4)
+#define ETAG_ALL    (ETAG_MTIME | ETAG_INODE | ETAG_SIZE)
 /* This is the default value used */
 #define ETAG_BACKWARD (ETAG_MTIME | ETAG_SIZE)
 
index 48c54ada7afeaad0a49bbece357755d4874669a0..0047f4762b314fcbf3e3a5e58c58700c631d791c 100644 (file)
@@ -145,6 +145,27 @@ AP_DECLARE(const char *) ap_make_content_type(request_rec *r,
  */
 AP_DECLARE(void) ap_setup_make_content_type(apr_pool_t *pool);
 
+/** A structure with the ingredients for a file based etag */
+typedef struct etag_rec etag_rec;
+
+/**
+ * @brief A structure with the ingredients for a file based etag
+ */
+struct etag_rec {
+    /** Optional vary list validator */
+    const char *vlist_validator;
+    /** Time when the request started */
+    apr_time_t request_time;
+    /** finfo.protection (st_mode) set to zero if no such file */
+    apr_finfo_t *finfo;
+    /** File pathname used when generating a digest */
+    const char *pathname;
+    /** File descriptor used when generating a digest */
+    apr_file_t *fd;
+    /** Force a non-digest etag to be weak */
+    int force_weak;
+};
+
 /**
  * Construct an entity tag from the resource information.  If it's a real
  * file, build in some of the file characteristics.
@@ -155,12 +176,27 @@ AP_DECLARE(void) ap_setup_make_content_type(apr_pool_t *pool);
  */
 AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak);
 
+/**
+ * Construct an entity tag from information provided in the etag_rec
+ * structure.
+ * @param r The current request
+ * @param er The etag record, containing ingredients for the etag.
+ */
+AP_DECLARE(char *) ap_make_etag_ex(request_rec *r, etag_rec *er);
+
 /**
  * Set the E-tag outgoing header
  * @param r The current request
  */
 AP_DECLARE(void) ap_set_etag(request_rec *r);
 
+/**
+ * Set the E-tag outgoing header, with the option of forcing a strong ETag.
+ * @param r The current request
+ * @param fd The file descriptor
+ */
+AP_DECLARE(void) ap_set_etag_fd(request_rec *r, apr_file_t *fd);
+
 /**
  * Set the last modified time for the file being sent
  * @param r The current request
@@ -744,7 +780,7 @@ AP_DECLARE_HOOK(const char *,http_scheme,(const request_rec *r))
 AP_DECLARE_HOOK(apr_port_t,default_port,(const request_rec *r))
 
 
-#define AP_PROTOCOL_HTTP1              "http/1.1"
+#define AP_PROTOCOL_HTTP1        "http/1.1"
 
 /**
  * Determine the list of protocols available for a connection/request. This may
@@ -1025,6 +1061,7 @@ AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub_r);
  */
 AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers);
 
+
 #ifdef __cplusplus
 }
 #endif
index 22fa8c405884830df7bd552ff44823288f9cf1ff..14f15b5c9b77692c02590273362775d35c6f14ba 100644 (file)
@@ -645,6 +645,49 @@ struct ap_method_list_t {
     /** the array used for extension methods */
     apr_array_header_t *method_list;
 };
+/** @} */
+
+/**
+ * @defgroup bnotes Binary notes recognized by the server
+ * @ingroup APACHE_CORE_DAEMON
+ * @{
+ *
+ * @brief Binary notes recognized by the server.
+ */
+
+/**
+ * The type used for request binary notes.
+ */
+typedef apr_uint64_t ap_request_bnotes_t;
+
+/**
+ * These constants represent bitmasks for notes associated with this
+ * request. There are space for 64 bits in the apr_uint64_t.
+ *
+ */
+#define AP_REQUEST_STRONG_ETAG 1 >> 0
+
+/**
+ * This is a convenience macro to ease with getting specific request
+ * binary notes.
+ */
+#define AP_REQUEST_GET_BNOTE(r, mask) \
+    ((mask) & ((r)->bnotes))
+
+/**
+ * This is a convenience macro to ease with setting specific request
+ * binary notes.
+ */
+#define AP_REQUEST_SET_BNOTE(r, mask, val) \
+    (r)->bnotes = (((r)->bnotes & ~(mask)) | (val))
+
+/**
+ * Returns true if the strong etag flag is set for this request.
+ */
+#define AP_REQUEST_IS_STRONG_ETAG(r) \
+        AP_REQUEST_GET_BNOTE((r), AP_REQUEST_STRONG_ETAG)
+/** @} */
+
 
 /**
  * @defgroup module_magic Module Magic mime types
@@ -1062,6 +1105,11 @@ struct request_rec {
      *  1 yes/success
      */
     int double_reverse;
+    /** Request flags associated with this request. Use
+     * AP_REQUEST_GET_FLAGS() and AP_REQUEST_SET_FLAGS() to access
+     * the elements of this field.
+     */
+    ap_request_bnotes_t bnotes;
 };
 
 /**
index 58d410ba9cce19381db3dffde7a6d2588cf3f5c3..8dfa28a34ffc1512cf5908f5e26c8dedf832c183 100644 (file)
@@ -1853,27 +1853,26 @@ static dav_error * dav_fs_walk(const dav_walk_params *params, int depth,
     return dav_fs_internal_walk(params, depth, 0, NULL, response);
 }
 
-/* dav_fs_etag:  Stolen from ap_make_etag.  Creates a strong etag
- *    for file path.
- * ### do we need to return weak tags sometimes?
+/* dav_fs_etag: Creates an etag for the file path.
  */
 static const char *dav_fs_getetag(const dav_resource *resource)
 {
-    dav_resource_private *ctx = resource->info;
-    /* XXX: This should really honor the FileETag setting */
+    etag_rec er;
 
-    if (!resource->exists)
-        return apr_pstrdup(ctx->pool, "");
+    dav_resource_private *ctx = resource->info;
 
-    if (ctx->finfo.filetype != APR_NOFILE) {
-        return apr_psprintf(ctx->pool, "\"%" APR_UINT64_T_HEX_FMT "-%"
-                            APR_UINT64_T_HEX_FMT "\"",
-                            (apr_uint64_t) ctx->finfo.size,
-                            (apr_uint64_t) ctx->finfo.mtime);
+    if (!resource->exists || !ctx->r) {
+        return "";
     }
 
-    return apr_psprintf(ctx->pool, "\"%" APR_UINT64_T_HEX_FMT "\"",
-                       (apr_uint64_t) ctx->finfo.mtime);
+    er.vlist_validator = NULL;
+    er.request_time = ctx->r->request_time;
+    er.finfo = &ctx->finfo;
+    er.pathname = ctx->pathname;
+    er.fd = NULL;
+    er.force_weak = 0;
+
+    return ap_make_etag_ex(ctx->r, &er);
 }
 
 static const dav_hooks_repository dav_hooks_repository_fs =
index 7f3c6d935905ad5f4e7915a848fbf49a5427d59b..59582e11d51d02b3e51b10d6368cfaa549fb83bb 100644 (file)
@@ -16,6 +16,9 @@
 
 #include "apr_strings.h"
 #include "apr_thread_proc.h"    /* for RLIMIT stuff */
+#include "apr_sha1.h"
+#include "apr_base64.h"
+#include "apr_buckets.h"
 
 #define APR_WANT_STRFUNC
 #include "apr_want.h"
 #include "http_config.h"
 #include "http_connection.h"
 #include "http_core.h"
+#include "http_log.h"
 #include "http_protocol.h"   /* For index_of_response().  Grump. */
 #include "http_request.h"
 
+#if APR_HAS_MMAP
+#include "apr_mmap.h"
+#endif /* APR_HAS_MMAP */
+
+#define SHA1_DIGEST_BASE64_LEN 4*(APR_SHA1_DIGESTSIZE/3)
+
 /* Generate the human-readable hex representation of an apr_uint64_t
  * (basically a faster version of 'sprintf("%llx")')
  */
@@ -53,19 +63,159 @@ static char *etag_uint64_to_hex(char *next, apr_uint64_t u)
 
 #define ETAG_WEAK "W/"
 #define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2)
+
+static void etag_start(char *etag, const char *weak, char **next)
+{
+    if (weak) {
+        while (*weak) {
+            *etag++ = *weak++;
+        }
+    }
+    *etag++ = '"';
+
+    *next = etag;
+}
+
+static void etag_end(char *next, const char *vlv, apr_size_t vlv_len)
+{
+    if (vlv) {
+        *next++ = ';';
+        apr_cpystrn(next, vlv, vlv_len);
+    }
+    else {
+        *next++ = '"';
+        *next = '\0';
+    }
+}
+
+/*
+ * Construct a strong ETag by creating a SHA1 hash across the file content.
+ */
+static char *make_digest_etag(request_rec *r, etag_rec *er, char *vlv,
+        apr_size_t vlv_len, char *weak, apr_size_t weak_len)
+{
+    apr_sha1_ctx_t context;
+    unsigned char digest[APR_SHA1_DIGESTSIZE];
+    apr_file_t *fd = NULL;
+    core_dir_config *cfg;
+    char *etag, *next;
+    apr_bucket_brigade *bb;
+    apr_bucket *e;
+
+    apr_size_t nbytes;
+    apr_off_t offset = 0, zero = 0, len = 0;
+    apr_status_t status;
+
+    cfg = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
+
+    if (er->fd) {
+        fd = er->fd;
+    }
+    else if (er->pathname) {
+        if ((status = apr_file_open(&fd, er->pathname, APR_READ | APR_BINARY,
+                0, r->pool)) != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10251)
+                          "Make etag: could not open %s", er->pathname);
+            return "";
+        }
+    }
+    if (!fd) {
+        return "";
+    }
+
+    if ((status = apr_file_seek(fd, APR_CUR, &offset)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10252)
+                      "Make etag: could not seek");
+        if (er->pathname) {
+            apr_file_close(fd);
+        }
+        return "";
+    }
+
+    if ((status = apr_file_seek(fd, APR_END, &len)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO()
+                      "Make etag: could not seek");
+        if (er->pathname) {
+            apr_file_close(fd);
+        }
+        return "";
+    }
+
+    if ((status = apr_file_seek(fd, APR_SET, &zero)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10253)
+                      "Make etag: could not seek");
+        if (er->pathname) {
+            apr_file_close(fd);
+        }
+        return "";
+    }
+
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+    e = apr_brigade_insert_file(bb, fd, 0, len, r->pool);
+
+#if APR_HAS_MMAP
+    if (cfg->enable_mmap == ENABLE_MMAP_OFF) {
+        (void)apr_bucket_file_enable_mmap(e, 0);
+    }
+#endif
+
+    apr_sha1_init(&context);
+    while (!APR_BRIGADE_EMPTY(bb))
+    {
+        const char *str;
+
+        e = APR_BRIGADE_FIRST(bb);
+
+        if ((status = apr_bucket_read(e, &str, &nbytes, APR_BLOCK_READ)) != APR_SUCCESS) {
+               apr_brigade_destroy(bb);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10254)
+                          "Make etag: could not read");
+            if (er->pathname) {
+                apr_file_close(fd);
+            }
+            return "";
+        }
+
+        apr_sha1_update(&context, str, nbytes);
+        apr_bucket_delete(e);
+    }
+
+    if ((status = apr_file_seek(fd, APR_SET, &offset)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10255)
+                      "Make etag: could not seek");
+        if (er->pathname) {
+            apr_file_close(fd);
+        }
+        return "";
+    }
+    apr_sha1_final(digest, &context);
+
+    etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
+            SHA1_DIGEST_BASE64_LEN + vlv_len + 4);
+
+    etag_start(etag, weak, &next);
+    next += apr_base64_encode_binary(next, digest, APR_SHA1_DIGESTSIZE) - 1;
+    etag_end(next, vlv, vlv_len);
+
+    if (er->pathname) {
+        apr_file_close(fd);
+    }
+
+    return etag;
+}
+
 /*
  * Construct an entity tag (ETag) from resource information.  If it's a real
  * file, build in some of the file characteristics.  If the modification time
  * is newer than (request-time minus 1 second), mark the ETag as weak - it
- * could be modified again in as short an interval.  We rationalize the
- * modification time we're given to keep it from being in the future.
+ * could be modified again in as short an interval.
  */
-AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
+AP_DECLARE(char *) ap_make_etag_ex(request_rec *r, etag_rec *er)
 {
-    char *weak;
-    apr_size_t weak_len;
-    char *etag;
-    char *next;
+    char *weak = NULL;
+    apr_size_t weak_len = 0, vlv_len = 0;
+    char *etag, *next, *vlv;
     core_dir_config *cfg;
     etag_components_t etag_bits;
     etag_components_t bits_added;
@@ -73,13 +223,62 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
     cfg = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
     etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
 
+    if (er->force_weak) {
+        weak = ETAG_WEAK;
+        weak_len = sizeof(ETAG_WEAK);
+    }
+
+    if (r->vlist_validator) {
+
+        /* If we have a variant list validator (vlv) due to the
+         * response being negotiated, then we create a structured
+         * entity tag which merges the variant etag with the variant
+         * list validator (vlv).  This merging makes revalidation
+         * somewhat safer, ensures that caches which can deal with
+         * Vary will (eventually) be updated if the set of variants is
+         * changed, and is also a protocol requirement for transparent
+         * content negotiation.
+         */
+
+        /* if the variant list validator is weak, we make the whole
+         * structured etag weak.  If we would not, then clients could
+         * have problems merging range responses if we have different
+         * variants with the same non-globally-unique strong etag.
+         */
+
+        vlv = r->vlist_validator;
+        if (vlv[0] == 'W') {
+            vlv += 3;
+            weak = ETAG_WEAK;
+            weak_len = sizeof(ETAG_WEAK);
+        }
+        else {
+            vlv++;
+        }
+        vlv_len = strlen(vlv);
+
+    }
+    else {
+        vlv = NULL;
+        vlv_len = 0;
+    }
+
+    /*
+     * Did a module flag the need for a strong etag, or did the
+     * configuration tell us to generate a digest?
+     */
+    if (er->finfo->filetype == APR_REG &&
+            (AP_REQUEST_IS_STRONG_ETAG(r) || (etag_bits & ETAG_DIGEST))) {
+
+        return make_digest_etag(r, er, vlv, vlv_len, weak, weak_len);
+    }
+
     /*
      * 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 "";
     }
 
@@ -98,123 +297,117 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
      * be modified again later in the second, and the validation
      * would be incorrect.
      */
-    if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
-        !force_weak) {
-        weak = NULL;
-        weak_len = 0;
-    }
-    else {
+    if ((er->request_time - er->finfo->mtime < (1 * APR_USEC_PER_SEC))) {
         weak = ETAG_WEAK;
         weak_len = sizeof(ETAG_WEAK);
     }
 
-    if (r->finfo.filetype != APR_NOFILE) {
+    if (er->finfo->filetype != APR_NOFILE) {
         /*
          * ETag gets set to [W/]"inode-size-mtime", modulo any
          * FileETag keywords.
          */
         etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
-                          3 * CHARS_PER_UINT64 + 1);
-        next = etag;
-        if (weak) {
-            while (*weak) {
-                *next++ = *weak++;
-            }
-        }
-        *next++ = '"';
+                          3 * CHARS_PER_UINT64 + vlv_len + 2);
+
+        etag_start(etag, weak, &next);
+
         bits_added = 0;
         if (etag_bits & ETAG_INODE) {
-            next = etag_uint64_to_hex(next, r->finfo.inode);
+            next = etag_uint64_to_hex(next, er->finfo->inode);
             bits_added |= ETAG_INODE;
         }
         if (etag_bits & ETAG_SIZE) {
             if (bits_added != 0) {
                 *next++ = '-';
             }
-            next = etag_uint64_to_hex(next, r->finfo.size);
+            next = etag_uint64_to_hex(next, er->finfo->size);
             bits_added |= ETAG_SIZE;
         }
         if (etag_bits & ETAG_MTIME) {
             if (bits_added != 0) {
                 *next++ = '-';
             }
-            next = etag_uint64_to_hex(next, r->mtime);
+            next = etag_uint64_to_hex(next, er->finfo->mtime);
         }
-        *next++ = '"';
-        *next = '\0';
+
+        etag_end(next, vlv, vlv_len);
+
     }
     else {
         /*
          * Not a file document, so just use the mtime: [W/]"mtime"
          */
         etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
-                          CHARS_PER_UINT64 + 1);
-        next = etag;
-        if (weak) {
-            while (*weak) {
-                *next++ = *weak++;
-            }
-        }
-        *next++ = '"';
-        next = etag_uint64_to_hex(next, r->mtime);
-        *next++ = '"';
-        *next = '\0';
+                          CHARS_PER_UINT64 + vlv_len + 2);
+
+        etag_start(etag, weak, &next);
+        next = etag_uint64_to_hex(next, er->finfo->mtime);
+        etag_end(next, vlv, vlv_len);
+
     }
 
     return etag;
 }
 
+AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
+{
+    etag_rec er;
+
+    er.vlist_validator = NULL;
+    er.request_time = r->request_time;
+    er.finfo = &r->finfo;
+    er.pathname = r->filename;
+    er.fd = NULL;
+    er.force_weak = force_weak;
+
+    return ap_make_etag_ex(r, &er);
+}
+
 AP_DECLARE(void) ap_set_etag(request_rec *r)
 {
     char *etag;
-    char *variant_etag, *vlv;
-    int vlv_weak;
 
-    if (!r->vlist_validator) {
-        etag = ap_make_etag(r, 0);
+    etag_rec er;
 
-        /* If we get a blank etag back, don't set the header. */
-        if (!etag[0]) {
-            return;
-        }
+    er.vlist_validator = r->vlist_validator;
+    er.request_time = r->request_time;
+    er.finfo = &r->finfo;
+    er.pathname = r->filename;
+    er.fd = NULL;
+    er.force_weak = 0;
+
+    etag = ap_make_etag_ex(r, &er);
+
+    if (etag && etag[0]) {
+        apr_table_setn(r->headers_out, "ETag", etag);
     }
     else {
-        /* If we have a variant list validator (vlv) due to the
-         * response being negotiated, then we create a structured
-         * entity tag which merges the variant etag with the variant
-         * list validator (vlv).  This merging makes revalidation
-         * somewhat safer, ensures that caches which can deal with
-         * Vary will (eventually) be updated if the set of variants is
-         * changed, and is also a protocol requirement for transparent
-         * content negotiation.
-         */
+        apr_table_setn(r->notes, "no-etag", "omit");
+    }
 
-        /* if the variant list validator is weak, we make the whole
-         * structured etag weak.  If we would not, then clients could
-         * have problems merging range responses if we have different
-         * variants with the same non-globally-unique strong etag.
-         */
+}
 
-        vlv = r->vlist_validator;
-        vlv_weak = (vlv[0] == 'W');
+AP_DECLARE(void) ap_set_etag_fd(request_rec *r, apr_file_t *fd)
+{
+    char *etag;
 
-        variant_etag = ap_make_etag(r, vlv_weak);
+    etag_rec er;
 
-        /* If we get a blank etag back, don't append vlv and stop now. */
-        if (!variant_etag[0]) {
-            return;
-        }
+    er.vlist_validator = r->vlist_validator;
+    er.request_time = r->request_time;
+    er.finfo = &r->finfo;
+    er.pathname = NULL;
+    er.fd = fd;
+    er.force_weak = 0;
 
-        /* merge variant_etag and vlv into a structured etag */
-        variant_etag[strlen(variant_etag) - 1] = '\0';
-        if (vlv_weak) {
-            vlv += 3;
-        }
-        else {
-            vlv++;
-        }
-        etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
+    etag = ap_make_etag_ex(r, &er);
+
+    if (etag && etag[0]) {
+        apr_table_setn(r->headers_out, "ETag", etag);
+    }
+    else {
+        apr_table_setn(r->notes, "no-etag", "omit");
     }
 
-    apr_table_setn(r->headers_out, "ETag", etag);
 }
index bd9ae46f034f78239335117343786b4dd599f7ab..d018d9abaf0cb63fcbfb46f53ae7aebb540c2455 100644 (file)
@@ -171,7 +171,7 @@ dialup_handler(request_rec *r)
     /* copied from default handler: */
     ap_update_mtime(r, r->finfo.mtime);
     ap_set_last_modified(r);
-    ap_set_etag(r);
+    ap_set_etag_fd(r, fd);
     ap_set_accept_ranges(r);
     ap_set_content_length(r, r->finfo.size);
 
index 567ad4a702c6af7a5366c515842ba75f359c6f70..fe3a16f7fd869fce23fe23aa2cfc5673e3102fa1 100644 (file)
@@ -2165,6 +2165,9 @@ static const char *set_etag_bits(cmd_parms *cmd, void *mconfig,
         else if (ap_cstr_casecmp(token, "INode") == 0) {
             bit = ETAG_INODE;
         }
+        else if (ap_cstr_casecmp(token, "Digest") == 0) {
+            bit = ETAG_DIGEST;
+        }
         else {
             return apr_pstrcat(cmd->pool, "Unknown keyword '",
                                token, "' for ", cmd->cmd->name,
@@ -4915,7 +4918,7 @@ static int default_handler(request_rec *r)
 
         ap_update_mtime(r, r->finfo.mtime);
         ap_set_last_modified(r);
-        ap_set_etag(r);
+        ap_set_etag_fd(r, fd);
         ap_set_accept_ranges(r);
         ap_set_content_length(r, r->finfo.size);
         if (bld_content_md5) {