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/07 15:42:31 UTC

svn commit: r1479905 - in /httpd/httpd/trunk: CHANGES 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  7 13:42:31 2013
New Revision: 1479905

URL: http://svn.apache.org/r1479905
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

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/include/ap_mmn.h
    httpd/httpd/trunk/include/http_protocol.h
    httpd/httpd/trunk/modules/cache/cache_storage.c
    httpd/httpd/trunk/modules/cache/mod_cache.c
    httpd/httpd/trunk/modules/http/byterange_filter.c
    httpd/httpd/trunk/modules/http/http_protocol.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Tue May  7 13:42:31 2013
@@ -1,6 +1,13 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) 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/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Tue May  7 13:42:31 2013
@@ -423,6 +423,11 @@
  * 20121222.9 (2.5.0-dev)  Add conn_sense_e
  * 20121222.10 (2.5.0-dev) Add cache_control_t.invalidated
  * 20121222.11 (2.5.0-dev) Add ap_find_etag_weak(), ap_find_etag_strong()
+ * 20121222.12 (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 0x41503235UL /* "AP25" */
@@ -430,7 +435,7 @@
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20121222
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 11                  /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 12                  /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

Modified: httpd/httpd/trunk/include/http_protocol.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/http_protocol.h?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/include/http_protocol.h (original)
+++ httpd/httpd/trunk/include/http_protocol.h Tue May  7 13:42:31 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/trunk/modules/cache/cache_storage.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/cache_storage.c?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/cache/cache_storage.c (original)
+++ httpd/httpd/trunk/modules/cache/cache_storage.c Tue May  7 13:42:31 2013
@@ -222,7 +222,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) {
@@ -288,9 +288,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
@@ -307,42 +325,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/trunk/modules/cache/mod_cache.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_cache.c?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/cache/mod_cache.c (original)
+++ httpd/httpd/trunk/modules/cache/mod_cache.c Tue May  7 13:42:31 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/trunk/modules/http/byterange_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http/byterange_filter.c?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http/byterange_filter.c (original)
+++ httpd/httpd/trunk/modules/http/byterange_filter.c Tue May  7 13:42:31 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;
@@ -120,20 +118,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/trunk/modules/http/http_protocol.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http/http_protocol.c?rev=1479905&r1=1479904&r2=1479905&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http/http_protocol.c (original)
+++ httpd/httpd/trunk/modules/http/http_protocol.c Tue May  7 13:42:31 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;
     }