From d5688b382fd477045204fa089f8a84965bb2c8d9 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Tue, 28 May 2013 21:17:53 +0000 Subject: [PATCH] core: Add the ability to do explicit matching on weak and strong ETags as per RFC2616 Section 13.3.3. trunk patch: http://svn.apache.org/r1479528 Submitted by: minfrin Reviewed by: jim, wrowe git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1487123 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 ++ STATUS | 6 --- include/ap_mmn.h | 3 +- include/httpd.h | 18 +++++++++ modules/http/http_protocol.c | 17 ++++----- server/util.c | 72 +++++++++++++++++++++++++++++++++--- 6 files changed, 98 insertions(+), 22 deletions(-) diff --git a/CHANGES b/CHANGES index ffca0d791d..90518d639a 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.4.5 + *) core: Add the ability to do explicit matching on weak and strong ETags + as per RFC2616 Section 13.3.3. [Graham Leggett, Co-Advisor + ] + *) 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/STATUS b/STATUS index 999826c716..1af8eed28d 100644 --- a/STATUS +++ b/STATUS @@ -90,12 +90,6 @@ RELEASE SHOWSTOPPERS: PATCHES ACCEPTED TO BACKPORT FROM TRUNK: [ start all new proposals below, under PATCHES PROPOSED. ] - * core: Add the ability to do explicit matching on weak and strong ETags - as per RFC2616 Section 13.3.3. - trunk patch: http://svn.apache.org/r1479528 - 2.4.x patch: trunk patch works (minus CHANGES, mmn bump) - +1: minfrin, jim, wrowe - * 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 diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 6d3ce53469..36a02e2fbc 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -406,6 +406,7 @@ * 20120211.14 (2.4.5-dev) Add ppinherit and inherit to proxy_server_conf * 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() */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ @@ -413,7 +414,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20120211 #endif -#define MODULE_MAGIC_NUMBER_MINOR 16 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 17 /* 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 24e4c0ddca..9bc8be6e52 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -1509,6 +1509,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 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 c97c77857f..e0ba5c28a3 100644 --- a/server/util.c +++ b/server/util.c @@ -1286,27 +1286,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 */ @@ -1374,7 +1405,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; } @@ -1388,6 +1420,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 -- 2.40.0