summaryrefslogtreecommitdiff
path: root/django/utils
diff options
context:
space:
mode:
Diffstat (limited to 'django/utils')
-rw-r--r--django/utils/http.py45
1 files changed, 25 insertions, 20 deletions
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<day>\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 \<CHAR>.
+ 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):