diff options
author | Marcel Hellkamp <marc@gsites.de> | 2016-02-01 20:26:12 +0100 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2016-02-01 20:26:12 +0100 |
commit | 3763f68e932df6419a31cb925a4e99fd0ed19b1c (patch) | |
tree | 4cc25545901f3fecdfc1d2fb389b99f2f0c916ff | |
parent | 4c80902c0eef3c405393ab7778bfdcf9ae1b0978 (diff) | |
download | bottle-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.py | 36 | ||||
-rw-r--r-- | test/test_html_helper.py | 23 |
2 files changed, 59 insertions, 0 deletions
@@ -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!!!', {})] + ) + |