summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hellkamp <marc@gsites.de>2016-02-01 20:26:12 +0100
committerMarcel Hellkamp <marc@gsites.de>2016-02-01 20:26:12 +0100
commit3763f68e932df6419a31cb925a4e99fd0ed19b1c (patch)
tree4cc25545901f3fecdfc1d2fb389b99f2f0c916ff
parent4c80902c0eef3c405393ab7778bfdcf9ae1b0978 (diff)
downloadbottle-3763f68e932df6419a31cb925a4e99fd0ed19b1c.tar.gz
Added function to parse complex HTTP headers.
The function is not used yet but can be used to impement Request.accept, request.lang or similar.
-rw-r--r--bottle.py36
-rw-r--r--test/test_html_helper.py23
2 files changed, 59 insertions, 0 deletions
diff --git a/bottle.py b/bottle.py
index 1f42d6a..1c58c89 100644
--- a/bottle.py
+++ b/bottle.py
@@ -2779,6 +2779,42 @@ def parse_range_header(header, maxlen=0):
pass
+#: Header tokenizer used by _parse_http_header()
+_hsplit = re.compile('(?:(?:"((?:[^"\\\\]+|\\\\.)*)")|([^;,=]+))([;,=]?)').findall
+
+def _parse_http_header(h):
+ """ Parses a typical multi-valued and parametrised HTTP header (e.g. Accept headers) and returns a list of values
+ and parameters. For non-standard or broken input, this implementation may return partial results.
+ :param h: A header string (e.g. ``text/html,text/plain;q=0.9,*/*;q=0.8``)
+ :return: List of (value, params) tuples. The second element is a (possibly empty) dict.
+ """
+ values = []
+ if '"' not in h: # INFO: Fast path without regexp (~2x faster)
+ for value in h.split(','):
+ parts = value.split(';')
+ values.append((parts[0].strip(), {}))
+ for attr in parts[1:]:
+ name, value = attr.split('=', 1)
+ values[-1][1][name.strip()] = value.strip()
+ else:
+ lop, key, attrs = ',', None, {}
+ for quoted, plain, tok in _hsplit(h):
+ value = plain.strip() if plain else quoted.replace('\\"', '"')
+ if lop == ',':
+ attrs = {}
+ values.append((value, attrs))
+ elif lop == ';':
+ if tok == '=':
+ key = value
+ else:
+ attrs[value] = ''
+ elif lop == '=' and key:
+ attrs[key] = value
+ key = None
+ lop = tok
+ return values
+
+
def _parse_qsl(qs):
r = []
for pair in qs.replace(';', '&').split('&'):
diff --git a/test/test_html_helper.py b/test/test_html_helper.py
new file mode 100644
index 0000000..e4a6df5
--- /dev/null
+++ b/test/test_html_helper.py
@@ -0,0 +1,23 @@
+import unittest
+
+from bottle import _parse_http_header
+
+
+class TestHttpUtils(unittest.TestCase):
+
+ # TODO: Move more of the low level http stuff here.
+
+ def test_accept_header(self):
+ self.assertEquals(_parse_http_header(
+ 'text/xml, text/whitespace ,'
+ 'application/params;param=value; ws = lots ;"quote"="mid\\"quote",'
+ '"more\\"quotes\\"",'
+ 'I\'m in space!!!'),
+
+ [('text/xml', {}),
+ ('text/whitespace', {}),
+ ('application/params', {'param': 'value', 'ws': 'lots', 'quote': 'mid"quote'}),
+ ('more"quotes"', {}),
+ ('I\'m in space!!!', {})]
+ )
+