diff options
author | Marcel Hellkamp <marc@gsites.de> | 2019-12-18 11:46:18 +0100 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2019-12-18 11:46:18 +0100 |
commit | 5c52eb0c0b7f21836375b3584896094735171a9f (patch) | |
tree | 72ed031c3ce7114ffa231f996f83ccdbc9ae64a7 | |
parent | d9903c7508294e10b932099afb1e7f108f9a9022 (diff) | |
download | bottle-5c52eb0c0b7f21836375b3584896094735171a9f.tar.gz |
fix #1187: Allow non-ASCII characters in status_line
Uncommon, but valid.
-rwxr-xr-x | bottle.py | 17 | ||||
-rwxr-xr-x | test/test_environ.py | 9 |
2 files changed, 25 insertions, 1 deletions
@@ -730,6 +730,13 @@ class Bottle(object): def start_response(status, headerlist, exc_info=None): if exc_info: _raise(*exc_info) + if py3k: + # Errors here mean that the mounted WSGI app did not + # follow PEP-3333 (which requires latin1) or used a + # pre-encoding other than utf8 :/ + status = status.encode('latin1').decode('utf8') + headerlist = [k, v.encode('latin1').decode('utf8') + for k, v in headerlist] rs.status = status for name, value in headerlist: rs.add_header(name, value) @@ -1108,7 +1115,7 @@ class Bottle(object): or environ['REQUEST_METHOD'] == 'HEAD': if hasattr(out, 'close'): out.close() out = [] - start_response(response._status_line, response.headerlist) + start_response(response._wsgi_status_line(), response.headerlist) return out except (KeyboardInterrupt, SystemExit, MemoryError): raise @@ -1708,6 +1715,8 @@ class BaseResponse(object): if isinstance(status, int): code, status = status, _HTTP_STATUS_LINES.get(status) elif ' ' in status: + if '\n' in status or '\r' in status or '\0' in status: + raise ValueError('Status line must not include control chars.') status = status.strip() code = int(status.split()[0]) else: @@ -1768,6 +1777,12 @@ class BaseResponse(object): allowed with the current response status code. """ return self.headerlist + def _wsgi_status_line(self): + """ WSGI conform status line (latin1-encodeable) """ + if py3k: + return self._status_line.encode('utf8').decode('latin1') + return self._status_line + @property def headerlist(self): """ WSGI conform list of (header, value) tuples. """ diff --git a/test/test_environ.py b/test/test_environ.py index f0b8874..3e6e484 100755 --- a/test/test_environ.py +++ b/test/test_environ.py @@ -556,6 +556,15 @@ class TestResponse(unittest.TestCase): self.assertEqual(rs.status_code, 404) self.assertEqual(rs.status_line, '404 Brain not Found') + # Unicode in status line (thanks RFC7230 :/) + if bottle.py3k: + def test(): rs.status = '400 Non-ASCÜ' + self.assertRaises(ValueError, test) + self.assertEqual(rs.status, rs.status_line) + self.assertEqual(rs.status_code, 400) + wire = rs._wsgi_status_line().encode('latin1') + self.assertEqual(rs.status, wire.decode('utf8')) + def test(): rs.status = '5 Illegal Code' self.assertRaises(ValueError, test) self.assertEqual(rs.status, rs.status_line) # last value |