From 4ef0e019b7dd3d2bf93b5c705b3b7df9cdb77561 Mon Sep 17 00:00:00 2001 From: Kevin Christopher Henry Date: Thu, 1 Sep 2016 09:32:20 -0400 Subject: Fixed #27083 -- Added support for weak ETags. --- django/utils/http.py | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'django/utils') diff --git a/django/utils/http.py b/django/utils/http.py index c6285e6f40..cba2a08095 100644 --- a/django/utils/http.py +++ b/django/utils/http.py @@ -21,7 +21,15 @@ from django.utils.six.moves.urllib.parse import ( urlparse, ) -ETAG_MATCH = re.compile(r'(?:W/)?"((?:\\.|[^"])*)"') +# based on RFC 7232, Appendix C +ETAG_MATCH = re.compile(r''' + \A( # start of string and capture group + (?:W/)? # optional weak indicator + " # opening quote + [^"]* # any sequence of non-quote characters + " # end quote + )\Z # end of string and capture group +''', re.X) MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split() __D = r'(?P\d{2})' @@ -234,30 +242,27 @@ def urlsafe_base64_decode(s): def parse_etags(etag_str): """ - Parses a string with one or several etags passed in If-None-Match and - If-Match headers by the rules in RFC 2616. Returns a list of etags - without surrounding double quotes (") and unescaped from \. + Parse a string of ETags given in an If-None-Match or If-Match header as + defined by RFC 7232. Return a list of quoted ETags, or ['*'] if all ETags + should be matched. """ - etags = ETAG_MATCH.findall(etag_str) - if not etags: - # etag_str has wrong format, treat it as an opaque string then - return [etag_str] - etags = [e.encode('ascii').decode('unicode_escape') for e in etags] - return etags - - -def quote_etag(etag): - """ - Wraps a string in double quotes escaping contents as necessary. - """ - return '"%s"' % etag.replace('\\', '\\\\').replace('"', '\\"') + if etag_str.strip() == '*': + return ['*'] + else: + # Parse each ETag individually, and return any that are valid. + etag_matches = (ETAG_MATCH.match(etag.strip()) for etag in etag_str.split(',')) + return [match.group(1) for match in etag_matches if match] -def unquote_etag(etag): +def quote_etag(etag_str): """ - Unquote an ETag string; i.e. revert quote_etag(). + If the provided string is already a quoted ETag, return it. Otherwise, wrap + the string in quotes, making it a strong ETag. """ - return etag.strip('"').replace('\\"', '"').replace('\\\\', '\\') if etag else etag + if ETAG_MATCH.match(etag_str): + return etag_str + else: + return '"%s"' % etag_str def is_same_domain(host, pattern): -- cgit v1.3