summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--include/ap_mmn.h3
-rw-r--r--include/httpd.h18
-rw-r--r--modules/http/http_protocol.c17
-rw-r--r--server/util.c72
5 files changed, 98 insertions, 16 deletions
diff --git a/CHANGES b/CHANGES
index 9031485875..39027d9769 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.0
+ *) 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>]
+
*) mod_cache: Ensure that updated responses to HEAD requests don't get
mistakenly paired with a previously cached body. Ensure that any existing
body is removed when a HEAD request is cached. [Graham Leggett,
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 6aab61e32d..9d42cf0a9b 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -422,6 +422,7 @@
* 20121222.8 (2.5.0-dev) Add dav_join_error()
* 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()
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
@@ -429,7 +430,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20121222
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 10 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 11 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
diff --git a/include/httpd.h b/include/httpd.h
index 2b8f08ab55..efcb2907c9 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -1520,6 +1520,24 @@ AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field);
AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line, const char *tok);
/**
+ * Do a weak ETag comparison within an HTTP field value list.
+ * @param p The pool to allocate from
+ * @param line The field value list to search
+ * @param tok The token to search for
+ * @return 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line, const char *tok);
+
+/**
+ * Do a strong ETag comparison within an HTTP field value list.
+ * @param p The pool to allocate from
+ * @param line The field value list to search
+ * @param tok The token to search for
+ * @return 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line, const char *tok);
+
+/**
* Retrieve a token, spacing over it and adjusting the pointer to
* the first non-white byte afterwards. Note that these tokens
* are delimited by semis and commas and can also be delimited
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c
index 2f78612303..b25482dbcf 100644
--- a/modules/http/http_protocol.c
+++ b/modules/http/http_protocol.c
@@ -345,8 +345,8 @@ AP_DECLARE(int) ap_meets_conditions(request_rec *r)
*/
if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
if (if_match[0] != '*'
- && (etag == NULL || etag[0] == 'W'
- || !ap_find_list_item(r->pool, if_match, etag))) {
+ && (etag == NULL
+ || !ap_find_etag_strong(r->pool, if_match, etag))) {
return HTTP_PRECONDITION_FAILED;
}
}
@@ -386,19 +386,18 @@ AP_DECLARE(int) ap_meets_conditions(request_rec *r)
}
else if (etag != NULL) {
if (apr_table_get(r->headers_in, "Range")) {
- not_modified = etag[0] != 'W'
- && ap_find_list_item(r->pool,
- if_nonematch, etag);
+ not_modified = ap_find_etag_strong(r->pool, if_nonematch,
+ etag);
}
else {
- not_modified = ap_find_list_item(r->pool,
- if_nonematch, etag);
+ not_modified = ap_find_etag_weak(r->pool, if_nonematch,
+ etag);
}
}
}
else if (if_nonematch[0] == '*'
- || (etag != NULL
- && ap_find_list_item(r->pool, if_nonematch, etag))) {
+ || (etag != NULL
+ && ap_find_etag_strong(r->pool, if_nonematch, etag))) {
return HTTP_PRECONDITION_FAILED;
}
}
diff --git a/server/util.c b/server/util.c
index 676759fe48..6be14736b7 100644
--- a/server/util.c
+++ b/server/util.c
@@ -1287,27 +1287,58 @@ AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
return token;
}
+typedef enum ap_etag_e {
+ AP_ETAG_NONE,
+ AP_ETAG_WEAK,
+ AP_ETAG_STRONG
+} ap_etag_e;
+
/* Find an item in canonical form (lowercase, no extra spaces) within
* an HTTP field value list. Returns 1 if found, 0 if not found.
* This would be much more efficient if we stored header fields as
* an array of list items as they are received instead of a plain string.
*/
-AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
- const char *tok)
+static int find_list_item(apr_pool_t *p, const char *line,
+ const char *tok, ap_etag_e type)
{
const unsigned char *pos;
const unsigned char *ptr = (const unsigned char *)line;
int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
- if (!line || !tok)
+ if (!line || !tok) {
+ return 0;
+ }
+ if (type == AP_ETAG_STRONG && *tok != '\"') {
return 0;
+ }
+ if (type == AP_ETAG_WEAK) {
+ if (*tok == 'W' && (*(tok+1)) == '/' && (*(tok+2)) == '\"') {
+ tok += 2;
+ }
+ else if (*tok != '\"') {
+ return 0;
+ }
+ }
do { /* loop for each item in line's list */
/* Find first non-comma, non-whitespace byte */
-
- while (*ptr == ',' || apr_isspace(*ptr))
+ while (*ptr == ',' || apr_isspace(*ptr)) {
++ptr;
+ }
+
+ /* Account for strong or weak Etags, depending on our search */
+ if (type == AP_ETAG_STRONG && *ptr != '\"') {
+ break;
+ }
+ if (type == AP_ETAG_WEAK) {
+ if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') {
+ ptr += 2;
+ }
+ else if (*ptr != '\"') {
+ break;
+ }
+ }
if (*ptr)
good = 1; /* until proven otherwise for this item */
@@ -1375,7 +1406,8 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
if (in_com || in_qstr)
good = good && (*pos++ == *ptr);
else
- good = good && (*pos++ == apr_tolower(*ptr));
+ good = good
+ && (apr_tolower(*pos++) == apr_tolower(*ptr));
addspace = 0;
break;
}
@@ -1389,6 +1421,34 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
return good;
}
+/* Find an item in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list. Returns 1 if found, 0 if not found.
+ * This would be much more efficient if we stored header fields as
+ * an array of list items as they are received instead of a plain string.
+ */
+AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
+ const char *tok)
+{
+ return find_list_item(p, line, tok, AP_ETAG_NONE);
+}
+
+/* Find a strong Etag in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list. Returns 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line,
+ const char *tok)
+{
+ return find_list_item(p, line, tok, AP_ETAG_STRONG);
+}
+
+/* Find a weak ETag in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list. Returns 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line,
+ const char *tok)
+{
+ return find_list_item(p, line, tok, AP_ETAG_WEAK);
+}
/* Retrieve a token, spacing over it and returning a pointer to
* the first non-white byte afterwards. Note that these tokens