summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2014-07-08 14:41:50 -0400
committerRyan Petrello <lists@ryanpetrello.com>2014-07-09 11:32:12 -0400
commit1a2933e23f13a18de5c004327282c85334a1b135 (patch)
treef4b6a0f00d3ac8a203c87faf5dc0a3d15d8dfa04
parent2260909302e5fc5633100887b372677e8dab3ebc (diff)
downloadpecan-1a2933e23f13a18de5c004327282c85334a1b135.tar.gz
Don't (mistakenly) set HTTP 204 on controllers which set `response.body_file`.
Fixes bug 1339121 Change-Id: I70785315837b3907b63bb10565f3ccdf07559e8d
-rw-r--r--pecan/core.py24
-rw-r--r--pecan/tests/test_base.py83
2 files changed, 105 insertions, 2 deletions
diff --git a/pecan/core.py b/pecan/core.py
index fae1502..cb4cf2c 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -2,11 +2,12 @@ try:
from simplejson import dumps, loads
except ImportError: # pragma: no cover
from json import dumps, loads # noqa
-from itertools import chain
+from itertools import chain, tee
from mimetypes import guess_type, add_type
from os.path import splitext
import logging
import operator
+import types
import six
@@ -566,7 +567,26 @@ class PecanBase(object):
elif result:
resp.body = result
elif response.status_int == 200:
- resp.status = 204
+ # If the response is a generator...
+ if isinstance(response.app_iter, types.GeneratorType):
+ # Split the generator into two so we can peek at one of them
+ # and determine if there is any response body content
+ a, b = tee(response.app_iter)
+ try:
+ next(a)
+ except StopIteration:
+ # If we hit StopIteration, the body is empty
+ resp.status = 204
+ finally:
+ resp.app_iter = b
+ else:
+ text = None
+ if response.charset:
+ # `response.text` cannot be accessed without a charset
+ # (because we don't know which encoding to use)
+ text = response.text
+ if not any((response.body, text)):
+ resp.status = 204
if resp.status_int in (204, 304):
resp.content_type = None
diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py
index 44a6861..35f48ab 100644
--- a/pecan/tests/test_base.py
+++ b/pecan/tests/test_base.py
@@ -44,6 +44,30 @@ class TestEmptyContent(PecanTestCase):
def index(self):
pass
+ @expose()
+ def explicit_body(self):
+ response.body = b_('Hello, World!')
+
+ @expose()
+ def empty_body(self):
+ response.body = b_('')
+
+ @expose()
+ def explicit_text(self):
+ response.text = six.text_type('Hello, World!')
+
+ @expose()
+ def empty_text(self):
+ response.text = six.text_type('')
+
+ @expose()
+ def explicit_json(self):
+ response.json = {'foo': 'bar'}
+
+ @expose()
+ def explicit_json_body(self):
+ response.json_body = {'foo': 'bar'}
+
return TestApp(Pecan(RootController()))
def test_empty_index(self):
@@ -53,6 +77,65 @@ class TestEmptyContent(PecanTestCase):
self.assertEqual(r.headers['Content-Length'], '0')
self.assertEqual(len(r.body), 0)
+ def test_explicit_body(self):
+ r = self.app_.get('/explicit_body/')
+ self.assertEqual(r.status_int, 200)
+ self.assertEqual(r.body, b_('Hello, World!'))
+
+ def test_empty_body(self):
+ r = self.app_.get('/empty_body/')
+ self.assertEqual(r.status_int, 204)
+ self.assertEqual(r.body, b_(''))
+
+ def test_explicit_text(self):
+ r = self.app_.get('/explicit_text/')
+ self.assertEqual(r.status_int, 200)
+ self.assertEqual(r.body, b_('Hello, World!'))
+
+ def test_empty_text(self):
+ r = self.app_.get('/empty_text/')
+ self.assertEqual(r.status_int, 204)
+ self.assertEqual(r.body, b_(''))
+
+ def test_explicit_json(self):
+ r = self.app_.get('/explicit_json/')
+ self.assertEqual(r.status_int, 200)
+ json_resp = json.loads(r.body.decode())
+ assert json_resp == {'foo': 'bar'}
+
+ def test_explicit_json_body(self):
+ r = self.app_.get('/explicit_json_body/')
+ self.assertEqual(r.status_int, 200)
+ json_resp = json.loads(r.body.decode())
+ assert json_resp == {'foo': 'bar'}
+
+
+class TestAppIterFile(PecanTestCase):
+ @property
+ def app_(self):
+ class RootController(object):
+ @expose()
+ def index(self):
+ body = six.BytesIO(b_('Hello, World!'))
+ response.body_file = body
+
+ @expose()
+ def empty(self):
+ body = six.BytesIO(b_(''))
+ response.body_file = body
+
+ return TestApp(Pecan(RootController()))
+
+ def test_body_generator(self):
+ r = self.app_.get('/')
+ self.assertEqual(r.status_int, 200)
+ assert r.body == b_('Hello, World!')
+
+ def test_empty_body_generator(self):
+ r = self.app_.get('/empty')
+ self.assertEqual(r.status_int, 204)
+ assert len(r.body) == 0
+
class TestIndexRouting(PecanTestCase):