You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by mi...@apache.org on 2013/05/28 23:22:01 UTC

svn commit: r1487127 - in /httpd/httpd/branches/2.4.x: ./ CHANGES STATUS include/ap_mmn.h include/http_protocol.h modules/cache/cache_storage.c modules/cache/mod_cache.c modules/http/byterange_filter.c modules/http/http_protocol.c

Author: minfrin
Date: Tue May 28 21:22:00 2013
New Revision: 1487127

URL: http://svn.apache.org/r1487127
Log:
core, mod_cache: Ensure RFC2616 compliance in ap_meets_conditions()
with weak validation combined with If-Range and Range headers. Break
out explicit conditional header checks to be useable elsewhere in the
server. Ensure weak validation RFC compliance in the byteranges filter.
Ensure RFC validation compliance when serving cached entities. PR 16142

trunk patch: http://svn.apache.org/r1479905
2.4.x patch: http://people.apache.org/~minfrin/httpd-if-range-fix2.4.patch

Submitted by: minfrin
Reviewed by: jim, wrowe

Modified:
    httpd/httpd/branches/2.4.x/   (props changed)
    httpd/httpd/branches/2.4.x/CHANGES
    httpd/httpd/branches/2.4.x/STATUS
    httpd/httpd/branches/2.4.x/include/ap_mmn.h
    httpd/httpd/branches/2.4.x/include/http_protocol.h
    httpd/httpd/branches/2.4.x/modules/cache/cache_storage.c
    httpd/httpd/branches/2.4.x/modules/cache/mod_cache.c
    httpd/httpd/branches/2.4.x/modules/http/byterange_filter.c
    httpd/httpd/branches/2.4.x/modules/http/http_protocol.c

Propchange: httpd/httpd/branches/2.4.x/
------------------------------------------------------------------------------
  Merged /httpd/httpd/trunk:r1479905

Modified: httpd/httpd/branches/2.4.x/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/CHANGES?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/CHANGES [utf-8] (original)
+++ httpd/httpd/branches/2.4.x/CHANGES [utf-8] Tue May 28 21:22:00 2013
@@ -2,6 +2,13 @@
 
 Changes with Apache 2.4.5
 
+  *) core, mod_cache: Ensure RFC2616 compliance in ap_meets_conditions()
+     with weak validation combined with If-Range and Range headers. Break
+     out explicit conditional header checks to be useable elsewhere in the
+     server. Ensure weak validation RFC compliance in the byteranges filter.
+     Ensure RFC validation compliance when serving cached entities. PR 16142
+     [Graham Leggett, Co-Advisor <coad measurement-factory.com>]
+
   *) core: Add the ability to do explicit matching on weak and strong ETags
      as per RFC2616 Section 13.3.3. [Graham Leggett, Co-Advisor
      <coad measurement-factory.com>]

Modified: httpd/httpd/branches/2.4.x/STATUS
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/STATUS?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/STATUS (original)
+++ httpd/httpd/branches/2.4.x/STATUS Tue May 28 21:22:00 2013
@@ -90,16 +90,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
  
-    * core, mod_cache: Ensure RFC2616 compliance in ap_meets_conditions()
-      with weak validation combined with If-Range and Range headers. Break
-      out explicit conditional header checks to be useable elsewhere in the
-      server. Ensure weak validation RFC compliance in the byteranges filter.
-      Ensure RFC validation compliance when serving cached entities. PR 16142
-      trunk patch: http://svn.apache.org/r1479905
-      2.4.x patch: http://people.apache.org/~minfrin/httpd-if-range-fix2.4.patch
-                   (depends on r1479528 above)
-      +1: minfrin, jim, wrowe
-
     * mod_cache: Ensure that we don't attempt to replace a cached response
       with an older response as per RFC2616 13.12.
       trunk patch: http://svn.apache.org/r1479966

Modified: httpd/httpd/branches/2.4.x/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/include/ap_mmn.h?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/include/ap_mmn.h (original)
+++ httpd/httpd/branches/2.4.x/include/ap_mmn.h Tue May 28 21:22:00 2013
@@ -407,6 +407,11 @@
  * 20120211.15 (2.4.5-dev) Add dav_join_error()
  * 20120211.16 (2.4.5-dev) Add cache_control_t.invalidated
  * 20120211.17 (2.5.0-dev) Add ap_find_etag_weak(), ap_find_etag_strong()
+ * 20120211.18 (2.5.0-dev) Add ap_condition_e, ap_condition_if_match(),
+ *                         ap_condition_if_unmodified_since(),
+ *                         ap_condition_if_none_match(),
+ *                         ap_condition_if_modified_since(),
+ *                         ap_condition_if_range()
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
@@ -414,7 +419,7 @@
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20120211
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 17                   /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 18                   /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

Modified: httpd/httpd/branches/2.4.x/include/http_protocol.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/include/http_protocol.h?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/include/http_protocol.h (original)
+++ httpd/httpd/branches/2.4.x/include/http_protocol.h Tue May 28 21:22:00 2013
@@ -167,6 +167,72 @@ AP_DECLARE(void) ap_set_etag(request_rec
  */
 AP_DECLARE(void) ap_set_last_modified(request_rec *r);
 
+typedef enum {
+    AP_CONDITION_NONE,
+    AP_CONDITION_NOMATCH,
+    AP_CONDITION_WEAK,
+    AP_CONDITION_STRONG
+} ap_condition_e;
+
+/**
+ * Tests conditional request rules for the If-Match header.
+ * @param r The current request
+ * @param headers The response headers to check against
+ * @return AP_CONDITION_NONE if the header is missing, AP_CONDITION_NOMATCH
+ *         if the header does not match, AP_CONDITION_STRONG for a strong
+ *         match. Weak matches are not permitted for the If-Match header.
+ */
+AP_DECLARE(ap_condition_e) ap_condition_if_match(request_rec *r,
+        apr_table_t *headers);
+
+/**
+ * Tests conditional request rules for the If-Unmodified-Since header.
+ * @param r The current request
+ * @param headers The response headers to check against
+ * @return AP_CONDITION_NONE if the header is missing, AP_CONDITION_NOMATCH
+ *         if the header does not match, AP_CONDITION_WEAK if a weak match
+ *         was present and allowed by RFC2616, AP_CONDITION_STRONG for a
+ *         strong match.
+ */
+AP_DECLARE(ap_condition_e) ap_condition_if_unmodified_since(request_rec *r,
+        apr_table_t *headers);
+
+/**
+ * Tests conditional request rules for the If-None-Match header.
+ * @param r The current request
+ * @param headers The response headers to check against
+ * @return AP_CONDITION_NONE if the header is missing, AP_CONDITION_NOMATCH
+ *         if the header does not match, AP_CONDITION_WEAK if a weak match
+ *         was present and allowed by RFC2616, AP_CONDITION_STRONG for a
+ *         strong match.
+ */
+AP_DECLARE(ap_condition_e) ap_condition_if_none_match(request_rec *r,
+        apr_table_t *headers);
+
+/**
+ * Tests conditional request rules for the If-Modified-Since header.
+ * @param r The current request
+ * @param headers The response headers to check against
+ * @return AP_CONDITION_NONE if the header is missing, AP_CONDITION_NOMATCH
+ *         if the header does not match, AP_CONDITION_WEAK if a weak match
+ *         was present and allowed by RFC2616, AP_CONDITION_STRONG for a
+ *         strong match.
+ */
+AP_DECLARE(ap_condition_e) ap_condition_if_modified_since(request_rec *r,
+        apr_table_t *headers);
+
+/**
+ * Tests conditional request rules for the If-Range header.
+ * @param r The current request
+ * @param headers The response headers to check against
+ * @return AP_CONDITION_NONE if either the If-Range or Range header is
+ *         missing, AP_CONDITION_NOMATCH if the header does not match,
+ *         AP_CONDITION_STRONG for a strong match. Weak matches are not
+ *         permitted for the If-Range header.
+ */
+AP_DECLARE(ap_condition_e) ap_condition_if_range(request_rec *r,
+        apr_table_t *headers);
+
 /**
  * Implements condition GET rules for HTTP/1.1 specification.  This function
  * inspects the client headers and determines if the response fulfills

Modified: httpd/httpd/branches/2.4.x/modules/cache/cache_storage.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/cache/cache_storage.c?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/cache/cache_storage.c (original)
+++ httpd/httpd/branches/2.4.x/modules/cache/cache_storage.c Tue May 28 21:22:00 2013
@@ -248,7 +248,7 @@ int cache_select(cache_request_rec *cach
         switch ((rv = list->provider->open_entity(h, r, cache->key))) {
         case OK: {
             char *vary = NULL;
-            int fresh, mismatch = 0;
+            int mismatch = 0;
             char *last = NULL;
 
             if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
@@ -314,9 +314,27 @@ int cache_select(cache_request_rec *cach
             cache->provider = list->provider;
             cache->provider_name = list->provider_name;
 
+            /*
+             * RFC2616 13.3.4 Rules for When to Use Entity Tags and Last-Modified
+             * Dates: An HTTP/1.1 caching proxy, upon receiving a conditional request
+             * that includes both a Last-Modified date and one or more entity tags as
+             * cache validators, MUST NOT return a locally cached response to the
+             * client unless that cached response is consistent with all of the
+             * conditional header fields in the request.
+             */
+            if (ap_condition_if_match(r, h->resp_hdrs) == AP_CONDITION_NOMATCH
+                    || ap_condition_if_unmodified_since(r, h->resp_hdrs)
+                            == AP_CONDITION_NOMATCH
+                    || ap_condition_if_none_match(r, h->resp_hdrs)
+                            == AP_CONDITION_NOMATCH
+                    || ap_condition_if_modified_since(r, h->resp_hdrs)
+                            == AP_CONDITION_NOMATCH
+                    || ap_condition_if_range(r, h->resp_hdrs) == AP_CONDITION_NOMATCH) {
+                mismatch = 1;
+            }
+
             /* Is our cached response fresh enough? */
-            fresh = cache_check_freshness(h, cache, r);
-            if (!fresh) {
+            if (mismatch || !cache_check_freshness(h, cache, r)) {
                 const char *etag, *lastmod;
 
                 /* Cache-Control: only-if-cached and revalidation required, try
@@ -333,42 +351,45 @@ int cache_select(cache_request_rec *cach
                         r->headers_in);
                 cache->stale_handle = h;
 
-                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00695)
-                        "Cached response for %s isn't fresh.  Adding/replacing "
-                        "conditional request headers.", r->uri);
+                /* if no existing conditionals, use conditionals of our own */
+                if (!mismatch) {
 
-                /* We can only revalidate with our own conditionals: remove the
-                 * conditions from the original request.
-                 */
-                apr_table_unset(r->headers_in, "If-Match");
-                apr_table_unset(r->headers_in, "If-Modified-Since");
-                apr_table_unset(r->headers_in, "If-None-Match");
-                apr_table_unset(r->headers_in, "If-Range");
-                apr_table_unset(r->headers_in, "If-Unmodified-Since");
-
-                etag = apr_table_get(h->resp_hdrs, "ETag");
-                lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
-
-                if (etag || lastmod) {
-                    /* If we have a cached etag and/or Last-Modified add in
-                     * our own conditionals.
-                     */
+                    ap_log_rerror(
+                            APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00695) "Cached response for %s isn't fresh. Adding "
+                            "conditional request headers.", r->uri);
+
+                    /* Remove existing conditionals that might conflict with ours */
+                    apr_table_unset(r->headers_in, "If-Match");
+                    apr_table_unset(r->headers_in, "If-Modified-Since");
+                    apr_table_unset(r->headers_in, "If-None-Match");
+                    apr_table_unset(r->headers_in, "If-Range");
+                    apr_table_unset(r->headers_in, "If-Unmodified-Since");
+
+                    etag = apr_table_get(h->resp_hdrs, "ETag");
+                    lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
+
+                    if (etag || lastmod) {
+                        /* If we have a cached etag and/or Last-Modified add in
+                         * our own conditionals.
+                         */
 
-                    if (etag) {
-                        apr_table_set(r->headers_in, "If-None-Match", etag);
-                    }
+                        if (etag) {
+                            apr_table_set(r->headers_in, "If-None-Match", etag);
+                        }
 
-                    if (lastmod) {
-                        apr_table_set(r->headers_in, "If-Modified-Since",
-                                lastmod);
-                    }
+                        if (lastmod) {
+                            apr_table_set(r->headers_in, "If-Modified-Since",
+                                    lastmod);
+                        }
 
-                    /*
-                     * Do not do Range requests with our own conditionals: If
-                     * we get 304 the Range does not matter and otherwise the
-                     * entity changed and we want to have the complete entity
-                     */
-                    apr_table_unset(r->headers_in, "Range");
+                        /*
+                         * Do not do Range requests with our own conditionals: If
+                         * we get 304 the Range does not matter and otherwise the
+                         * entity changed and we want to have the complete entity
+                         */
+                        apr_table_unset(r->headers_in, "Range");
+
+                    }
 
                 }
 

Modified: httpd/httpd/branches/2.4.x/modules/cache/mod_cache.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/cache/mod_cache.c?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/cache/mod_cache.c (original)
+++ httpd/httpd/branches/2.4.x/modules/cache/mod_cache.c Tue May 28 21:22:00 2013
@@ -1318,7 +1318,7 @@ static apr_status_t cache_save_filter(ap
     if (rv != OK) {
         /* we've got a cache miss! tell anyone who cares */
         cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
-                "cache miss: create_entity failed");
+                "cache miss: cache unwilling to store response");
 
         /* Caching layer declined the opportunity to cache the response */
         ap_remove_output_filter(f);

Modified: httpd/httpd/branches/2.4.x/modules/http/byterange_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http/byterange_filter.c?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http/byterange_filter.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http/byterange_filter.c Tue May 28 21:22:00 2013
@@ -82,8 +82,6 @@ static int ap_set_byterange(request_rec 
                             int *overlaps, int *reversals)
 {
     const char *range;
-    const char *if_range;
-    const char *match;
     const char *ct;
     char *cur;
     apr_array_header_t *merged;
@@ -135,20 +133,9 @@ static int ap_set_byterange(request_rec 
 
     /*
      * Check the If-Range header for Etag or Date.
-     * Note that this check will return false (as required) if either
-     * of the two etags are weak.
      */
-    if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
-        if (if_range[0] == '"') {
-            if (!(match = apr_table_get(r->headers_out, "Etag"))
-                || (strcmp(if_range, match) != 0)) {
-                return 0;
-            }
-        }
-        else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
-                 || (strcmp(if_range, match) != 0)) {
-            return 0;
-        }
+    if (AP_CONDITION_NOMATCH == ap_condition_if_range(r, r->headers_out)) {
+        return 0;
     }
 
     range += 6;

Modified: httpd/httpd/branches/2.4.x/modules/http/http_protocol.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http/http_protocol.c?rev=1487127&r1=1487126&r2=1487127&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http/http_protocol.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http/http_protocol.c Tue May 28 21:22:00 2013
@@ -306,13 +306,229 @@ AP_DECLARE(int) ap_set_keepalive(request
     return 0;
 }
 
+AP_DECLARE(ap_condition_e) ap_condition_if_match(request_rec *r,
+        apr_table_t *headers)
+{
+    const char *if_match, *etag;
+
+    /* A server MUST use the strong comparison function (see section 13.3.3)
+     * to compare the entity tags in If-Match.
+     */
+    if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
+        if (if_match[0] == '*'
+                || ((etag = apr_table_get(headers, "ETag")) == NULL
+                        && !ap_find_etag_strong(r->pool, if_match, etag))) {
+            return AP_CONDITION_STRONG;
+        }
+        else {
+            return AP_CONDITION_NOMATCH;
+        }
+    }
+
+    return AP_CONDITION_NONE;
+}
+
+AP_DECLARE(ap_condition_e) ap_condition_if_unmodified_since(request_rec *r,
+        apr_table_t *headers)
+{
+    const char *if_unmodified;
+
+    if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
+    if (if_unmodified) {
+        apr_int64_t mtime, reqtime;
+
+        apr_time_t ius = apr_time_sec(apr_date_parse_http(if_unmodified));
+
+        /* All of our comparisons must be in seconds, because that's the
+         * highest time resolution the HTTP specification allows.
+         */
+        mtime = apr_time_sec(apr_date_parse_http(
+                        apr_table_get(headers, "Last-Modified")));
+        if (mtime == APR_DATE_BAD) {
+            mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
+        }
+
+        reqtime = apr_time_sec(apr_date_parse_http(
+                        apr_table_get(headers, "Date")));
+        if (!reqtime) {
+            reqtime = apr_time_sec(r->request_time);
+        }
+
+        if ((ius != APR_DATE_BAD) && (mtime > ius)) {
+            if (reqtime < mtime + 60) {
+                if (apr_table_get(r->headers_in, "Range")) {
+                    /* weak matches not allowed with Range requests */
+                    return AP_CONDITION_NOMATCH;
+                }
+                else {
+                    return AP_CONDITION_WEAK;
+                }
+            }
+            else {
+                return AP_CONDITION_STRONG;
+            }
+        }
+        else {
+            return AP_CONDITION_NOMATCH;
+        }
+    }
+
+    return AP_CONDITION_NONE;
+}
+
+AP_DECLARE(ap_condition_e) ap_condition_if_none_match(request_rec *r,
+        apr_table_t *headers)
+{
+    const char *if_nonematch, *etag;
+
+    if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
+    if (if_nonematch != NULL) {
+
+        if (if_nonematch[0] == '*') {
+            return AP_CONDITION_STRONG;
+        }
+
+        /* See section 13.3.3 for rules on how to determine if two entities tags
+         * match. The weak comparison function can only be used with GET or HEAD
+         * requests.
+         */
+        if (r->method_number == M_GET) {
+            if ((etag = apr_table_get(headers, "ETag")) != NULL) {
+                if (apr_table_get(r->headers_in, "Range")) {
+                    if (ap_find_etag_strong(r->pool, if_nonematch, etag)) {
+                        return AP_CONDITION_STRONG;
+                    }
+                }
+                else {
+                    if (ap_find_etag_weak(r->pool, if_nonematch, etag)) {
+                        return AP_CONDITION_WEAK;
+                    }
+                }
+            }
+        }
+
+        else if ((etag = apr_table_get(headers, "ETag")) != NULL
+                && ap_find_etag_strong(r->pool, if_nonematch, etag)) {
+            return AP_CONDITION_STRONG;
+        }
+        return AP_CONDITION_NOMATCH;
+    }
+
+    return AP_CONDITION_NONE;
+}
+
+AP_DECLARE(ap_condition_e) ap_condition_if_modified_since(request_rec *r,
+        apr_table_t *headers)
+{
+    const char *if_modified_since;
+
+    if ((if_modified_since = apr_table_get(r->headers_in, "If-Modified-Since"))
+            != NULL) {
+        apr_int64_t mtime;
+        apr_int64_t ims, reqtime;
+
+        /* All of our comparisons must be in seconds, because that's the
+         * highest time resolution the HTTP specification allows.
+         */
+
+        mtime = apr_time_sec(apr_date_parse_http(
+                        apr_table_get(headers, "Last-Modified")));
+        if (mtime == APR_DATE_BAD) {
+            mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
+        }
+
+        reqtime = apr_time_sec(apr_date_parse_http(
+                        apr_table_get(headers, "Date")));
+        if (!reqtime) {
+            reqtime = apr_time_sec(r->request_time);
+        }
+
+        ims = apr_time_sec(apr_date_parse_http(if_modified_since));
+
+        if (ims >= mtime && ims <= reqtime) {
+            if (reqtime < mtime + 60) {
+                if (apr_table_get(r->headers_in, "Range")) {
+                    /* weak matches not allowed with Range requests */
+                    return AP_CONDITION_NOMATCH;
+                }
+                else {
+                    return AP_CONDITION_WEAK;
+                }
+            }
+            else {
+                return AP_CONDITION_STRONG;
+            }
+        }
+        else {
+            return AP_CONDITION_NOMATCH;
+        }
+    }
+
+    return AP_CONDITION_NONE;
+}
+
+AP_DECLARE(ap_condition_e) ap_condition_if_range(request_rec *r,
+        apr_table_t *headers)
+{
+    const char *if_range, *etag;
+
+    if ((if_range = apr_table_get(r->headers_in, "If-Range"))
+            && apr_table_get(r->headers_in, "Range")) {
+        if (if_range[0] == '"') {
+
+            if ((etag = apr_table_get(headers, "ETag"))
+                    && !strcmp(if_range, etag)) {
+                return AP_CONDITION_STRONG;
+            }
+            else {
+                return AP_CONDITION_NOMATCH;
+            }
+
+        }
+        else {
+            apr_int64_t mtime;
+            apr_int64_t rtime, reqtime;
+
+            /* All of our comparisons must be in seconds, because that's the
+             * highest time resolution the HTTP specification allows.
+             */
+
+            mtime = apr_time_sec(apr_date_parse_http(
+                            apr_table_get(headers, "Last-Modified")));
+            if (mtime == APR_DATE_BAD) {
+                mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
+            }
+
+            reqtime = apr_time_sec(apr_date_parse_http(
+                            apr_table_get(headers, "Date")));
+            if (!reqtime) {
+                reqtime = apr_time_sec(r->request_time);
+            }
+
+            rtime = apr_time_sec(apr_date_parse_http(if_range));
+
+            if (rtime == mtime) {
+                if (reqtime < mtime + 60) {
+                    /* weak matches not allowed with Range requests */
+                    return AP_CONDITION_NOMATCH;
+                }
+                else {
+                    return AP_CONDITION_STRONG;
+                }
+            }
+            else {
+                return AP_CONDITION_NOMATCH;
+            }
+        }
+    }
+
+    return AP_CONDITION_NONE;
+}
+
 AP_DECLARE(int) ap_meets_conditions(request_rec *r)
 {
-    const char *etag;
-    const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
-    apr_time_t tmp_time;
-    apr_int64_t mtime;
-    int not_modified = 0;
+    int not_modified = -1; /* unset by default */
+    ap_condition_e cond;
 
     /* Check for conditional requests --- note that we only want to do
      * this if we are successful so far and we are not processing a
@@ -329,41 +545,30 @@ AP_DECLARE(int) ap_meets_conditions(requ
         return OK;
     }
 
-    etag = apr_table_get(r->headers_out, "ETag");
-
-    /* All of our comparisons must be in seconds, because that's the
-     * highest time resolution the HTTP specification allows.
-     */
-    /* XXX: we should define a "time unset" constant */
-    tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now());
-    mtime =  apr_time_sec(tmp_time);
-
     /* If an If-Match request-header field was given
      * AND the field value is not "*" (meaning match anything)
      * AND if our strong ETag does not match any entity tag in that field,
      *     respond with a status of 412 (Precondition Failed).
      */
-    if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
-        if (if_match[0] != '*'
-                && (etag == NULL
-                        || !ap_find_etag_strong(r->pool, if_match, etag))) {
-            return HTTP_PRECONDITION_FAILED;
-        }
+    cond = ap_condition_if_match(r, r->headers_out);
+    if (AP_CONDITION_NOMATCH == cond) {
+        not_modified = 0;
+    }
+    else if (cond >= AP_CONDITION_WEAK) {
+        return HTTP_PRECONDITION_FAILED;
     }
-    else {
-        /* Else if a valid If-Unmodified-Since request-header field was given
-         * AND the requested resource has been modified since the time
-         * specified in this field, then the server MUST
-         *     respond with a status of 412 (Precondition Failed).
-         */
-        if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
-        if (if_unmodified != NULL) {
-            apr_time_t ius = apr_date_parse_http(if_unmodified);
 
-            if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) {
-                return HTTP_PRECONDITION_FAILED;
-            }
-        }
+    /* Else if a valid If-Unmodified-Since request-header field was given
+     * AND the requested resource has been modified since the time
+     * specified in this field, then the server MUST
+     *     respond with a status of 412 (Precondition Failed).
+     */
+    cond = ap_condition_if_unmodified_since(r, r->headers_out);
+    if (AP_CONDITION_NOMATCH == cond) {
+        not_modified = 0;
+    }
+    else if (cond >= AP_CONDITION_WEAK) {
+        return HTTP_PRECONDITION_FAILED;
     }
 
     /* If an If-None-Match request-header field was given
@@ -378,26 +583,17 @@ AP_DECLARE(int) ap_meets_conditions(requ
      * GET or HEAD allow weak etag comparison, all other methods require
      * strong comparison.  We can only use weak if it's not a range request.
      */
-    if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
-    if (if_nonematch != NULL) {
+    cond = ap_condition_if_none_match(r, r->headers_out);
+    if (AP_CONDITION_NOMATCH == cond) {
+        not_modified = 0;
+    }
+    else if (cond >= AP_CONDITION_WEAK) {
         if (r->method_number == M_GET) {
-            if (if_nonematch[0] == '*') {
+            if (not_modified) {
                 not_modified = 1;
             }
-            else if (etag != NULL) {
-                if (apr_table_get(r->headers_in, "Range")) {
-                    not_modified = ap_find_etag_strong(r->pool, if_nonematch,
-                            etag);
-                }
-                else {
-                    not_modified = ap_find_etag_weak(r->pool, if_nonematch,
-                            etag);
-                }
-            }
         }
-        else if (if_nonematch[0] == '*'
-                || (etag != NULL
-                        && ap_find_etag_strong(r->pool, if_nonematch, etag))) {
+        else {
             return HTTP_PRECONDITION_FAILED;
         }
     }
@@ -409,22 +605,27 @@ AP_DECLARE(int) ap_meets_conditions(requ
      *    respond with a status of 304 (Not Modified).
      * A date later than the server's current request time is invalid.
      */
-    if (r->method_number == M_GET
-        && (not_modified || !if_nonematch)
-        && (if_modified_since =
-              apr_table_get(r->headers_in,
-                            "If-Modified-Since")) != NULL) {
-        apr_time_t ims_time;
-        apr_int64_t ims, reqtime;
-
-        ims_time = apr_date_parse_http(if_modified_since);
-        ims = apr_time_sec(ims_time);
-        reqtime = apr_time_sec(r->request_time);
+    cond = ap_condition_if_modified_since(r, r->headers_out);
+    if (AP_CONDITION_NOMATCH == cond) {
+        not_modified = 0;
+    }
+    else if (cond >= AP_CONDITION_WEAK) {
+        if (r->method_number == M_GET) {
+            if (not_modified) {
+                not_modified = 1;
+            }
+        }
+    }
 
-        not_modified = ims >= mtime && ims <= reqtime;
+    /* If an If-Range and an Range header is present, we must return
+     * 200 OK. The byterange filter will convert it to a range response.
+     */
+    cond = ap_condition_if_range(r, r->headers_out);
+    if (cond > AP_CONDITION_NONE) {
+        return OK;
     }
 
-    if (not_modified) {
+    if (not_modified == 1) {
         return HTTP_NOT_MODIFIED;
     }