diff options
author | Ryan Petrello <lists@ryanpetrello.com> | 2015-01-07 17:34:21 -0500 |
---|---|---|
committer | Ryan Petrello <lists@ryanpetrello.com> | 2015-01-08 14:58:21 -0500 |
commit | f491076a0f1b9f080c139a8062c9481e8024d8cb (patch) | |
tree | 408427508ad981aa61803d060bc503677d0f78d0 /pecan | |
parent | fe19eb67a83c4ec1e239586b2d7a6141a5d56418 (diff) | |
download | pecan-f491076a0f1b9f080c139a8062c9481e8024d8cb.tar.gz |
Change pecan to more gracefully handle a few odd request encoding edge cases.
Webob raises UnicodeDecodeErrors during request argument parsing in two
situations:
* HTTP POST requests composed of non-Unicode data (only affects Webob in
Python2)
* URL paths that contain invalid percent-encoded characters, e.g.,
/some/path/%AA
Pecan should detect these types of decoding failures when `webob.Request`
attributes are accessed, log the original exception, and coerce the HTTP 500
into a more accurate HTTP 400.
Fixes bug: 1408102
Fixes bug: 1407749
Change-Id: I734efd36230b6742805bcfd801dc0de2489ef92b
Diffstat (limited to 'pecan')
-rw-r--r-- | pecan/core.py | 8 | ||||
-rw-r--r-- | pecan/tests/test_base.py | 17 | ||||
-rw-r--r-- | pecan/tests/test_rest.py | 26 |
3 files changed, 48 insertions, 3 deletions
diff --git a/pecan/core.py b/pecan/core.py index 173210b..e74a8b1 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -48,7 +48,13 @@ class RoutingState(object): class Request(WebObRequest): - pass + + def __getattribute__(self, name): + try: + return WebObRequest.__getattribute__(self, name) + except UnicodeDecodeError as e: + logger.exception(e) + abort(400) class Response(WebObResponse): diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py index cca494c..1369837 100644 --- a/pecan/tests/test_base.py +++ b/pecan/tests/test_base.py @@ -146,6 +146,23 @@ class TestAppIterFile(PecanTestCase): assert len(r.body) == 0 +class TestInvalidURLEncoding(PecanTestCase): + + @property + def app_(self): + class RootController(object): + + @expose() + def _route(self, args, request): + assert request.path + + return TestApp(Pecan(RootController())) + + def test_rest_with_non_utf_8_body(self): + r = self.app_.get('/%aa/', expect_errors=True) + assert r.status_int == 400 + + class TestIndexRouting(PecanTestCase): @property diff --git a/pecan/tests/test_rest.py b/pecan/tests/test_rest.py index d839084..391b7d0 100644 --- a/pecan/tests/test_rest.py +++ b/pecan/tests/test_rest.py @@ -1,11 +1,12 @@ -from webtest import TestApp +import struct import warnings try: from simplejson import dumps, loads except: from json import dumps, loads # noqa -from six import b as b_ +from six import b as b_, PY3 +from webtest import TestApp from pecan import abort, expose, make_app, response, redirect from pecan.rest import RestController @@ -1359,6 +1360,27 @@ class TestRestController(PecanTestCase): assert r.status_int == 200 assert r.body == b_("DEFAULT missing") + def test_rest_with_non_utf_8_body(self): + if PY3: + # webob+PY3 doesn't suffer from this bug; the POST parsing in PY3 + # seems to more gracefully detect the bytestring + return + + class FooController(RestController): + + @expose() + def post(self): + return "POST" + + class RootController(RestController): + foo = FooController() + + app = TestApp(make_app(RootController())) + + data = struct.pack('255h', *range(0, 255)) + r = app.post('/foo/', data, expect_errors=True) + assert r.status_int == 400 + def test_dynamic_rest_lookup(self): class BarController(RestController): @expose() |