From 1a2933e23f13a18de5c004327282c85334a1b135 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 8 Jul 2014 14:41:50 -0400 Subject: Don't (mistakenly) set HTTP 204 on controllers which set `response.body_file`. Fixes bug 1339121 Change-Id: I70785315837b3907b63bb10565f3ccdf07559e8d --- pecan/core.py | 24 ++++++++++++-- pecan/tests/test_base.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 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): -- cgit v1.2.1