Merge pull request #159 from FlaPer87/master
fix: Encode body and update resp.body
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -7,6 +7,7 @@ by date of contribution:
|
||||
* Chad Lung (chadlung)
|
||||
* Josh Brand (joshbrand)
|
||||
* Jamie Painter (painterjd)
|
||||
* Flavio Percoco (flaper87)
|
||||
* Randall Burt (rs-randallburt)
|
||||
* Zhihao Yuan (lichray)
|
||||
* Ashutosh Das (pyprism)
|
||||
|
||||
@@ -19,8 +19,6 @@ limitations under the License.
|
||||
import re
|
||||
from functools import wraps
|
||||
|
||||
import six
|
||||
|
||||
from falcon import responders, HTTP_METHODS
|
||||
import falcon.status_codes as status
|
||||
|
||||
@@ -77,10 +75,10 @@ def set_content_length(resp):
|
||||
|
||||
content_length = 0
|
||||
|
||||
if resp.body is not None:
|
||||
if resp.body_encoded is not None:
|
||||
# Since body is assumed to be a byte string (str in Python 2, bytes in
|
||||
# Python 3), figure out the length using standard functions.
|
||||
content_length = len(resp.body)
|
||||
content_length = len(resp.body_encoded)
|
||||
elif resp.data is not None:
|
||||
content_length = len(resp.data)
|
||||
elif resp.stream is not None:
|
||||
@@ -111,13 +109,10 @@ def get_body(resp):
|
||||
|
||||
"""
|
||||
|
||||
body = resp.body
|
||||
body = resp.body_encoded
|
||||
|
||||
if body is not None:
|
||||
if isinstance(body, six.text_type):
|
||||
return [body.encode('utf-8')]
|
||||
else:
|
||||
return [body]
|
||||
return [body]
|
||||
|
||||
elif resp.data is not None:
|
||||
return [resp.data]
|
||||
|
||||
@@ -16,6 +16,8 @@ limitations under the License.
|
||||
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
from falcon.response_helpers import header_property, format_range
|
||||
from falcon.util import dt_to_http, percent_escape
|
||||
|
||||
@@ -36,7 +38,8 @@ class Response(object):
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'body', # Stuff
|
||||
'_body', # Stuff
|
||||
'_body_encoded', # Stuff
|
||||
'data',
|
||||
'_headers',
|
||||
'status',
|
||||
@@ -55,11 +58,50 @@ class Response(object):
|
||||
self.status = '200 OK'
|
||||
self._headers = {}
|
||||
|
||||
self.body = None
|
||||
self._body = None
|
||||
self._body_encoded = None
|
||||
self.data = None
|
||||
self.stream = None
|
||||
self.stream_len = None
|
||||
|
||||
def _get_body(self):
|
||||
"""Returns the body as-is."""
|
||||
return self._body
|
||||
|
||||
def _set_body(self, value):
|
||||
"""Sets the body and clears the encoded cache."""
|
||||
self._body = value
|
||||
self._body_encoded = None
|
||||
|
||||
# NOTE(flaper87): Lets use a property
|
||||
# for the body in case its content was
|
||||
# encoded and then modified.
|
||||
body = property(_get_body, _set_body)
|
||||
|
||||
@property
|
||||
def body_encoded(self):
|
||||
"""Encode the body and return it
|
||||
|
||||
This property will encode `_body` and
|
||||
cache the result in the `_body_encoded`
|
||||
attribute.
|
||||
"""
|
||||
# NOTE(flaper87): Notice this property
|
||||
# is not thread-safe. If body is modified
|
||||
# before this property returns, we might
|
||||
# end up returning None.
|
||||
body = self._body
|
||||
if body and self._body_encoded is None:
|
||||
|
||||
# NOTE(flaper87): Assume it is an
|
||||
# encoded str, then check and encode
|
||||
# if it isn't.
|
||||
self._body_encoded = body
|
||||
if isinstance(body, six.text_type):
|
||||
self._body_encoded = body.encode('utf-8')
|
||||
|
||||
return self._body_encoded
|
||||
|
||||
def set_header(self, name, value):
|
||||
"""Set a header for this response to a given value.
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class TestHooks(testing.TestBase):
|
||||
self.api.add_route(self.test_route, zoo_resource)
|
||||
|
||||
self.simulate_request(self.test_route)
|
||||
self.assertEqual('fluffy', zoo_resource.resp.body)
|
||||
self.assertEqual(b'fluffy', zoo_resource.resp.body_encoded)
|
||||
|
||||
def test_multiple_global_hook(self):
|
||||
self.api = falcon.API(after=[fluffiness, cuteness])
|
||||
@@ -94,29 +94,28 @@ class TestHooks(testing.TestBase):
|
||||
self.api.add_route(self.test_route, zoo_resource)
|
||||
|
||||
self.simulate_request(self.test_route)
|
||||
self.assertEqual('fluffy and cute', zoo_resource.resp.body)
|
||||
self.assertEqual(b'fluffy and cute', zoo_resource.resp.body_encoded)
|
||||
|
||||
def test_output_validator(self):
|
||||
self.simulate_request(self.test_route)
|
||||
self.assertEqual(falcon.HTTP_723, self.srmock.status)
|
||||
self.assertEqual(None, self.resource.resp.body)
|
||||
self.assertEqual(None, self.resource.resp.body_encoded)
|
||||
|
||||
def test_serializer(self):
|
||||
self.simulate_request(self.test_route, method='PUT')
|
||||
|
||||
actual_body = self.resource.resp.body
|
||||
self.assertEqual('{"animal": "falcon"}', actual_body)
|
||||
actual_body = self.resource.resp.body_encoded
|
||||
self.assertEqual(b'{"animal": "falcon"}', actual_body)
|
||||
|
||||
def test_wrapped_resource(self):
|
||||
expected = 'fluffy and cute'
|
||||
expected = b'fluffy and cute'
|
||||
|
||||
self.simulate_request('/wrapped')
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
self.assertEqual(expected, self.wrapped_resource.resp.body)
|
||||
self.assertEqual(expected, self.wrapped_resource.resp.body_encoded)
|
||||
|
||||
self.simulate_request('/wrapped', method='HEAD')
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
self.assertEqual(expected, self.wrapped_resource.resp.body)
|
||||
|
||||
self.simulate_request('/wrapped', method='POST')
|
||||
self.assertEqual(falcon.HTTP_405, self.srmock.status)
|
||||
|
||||
@@ -97,11 +97,11 @@ class TestHelloWorld(testing.TestBase):
|
||||
resp = self.resource.resp
|
||||
|
||||
content_length = int(self.srmock.headers_dict['Content-Length'])
|
||||
self.assertEquals(content_length, len(self.resource.sample_unicode))
|
||||
self.assertEquals(content_length, len(self.resource.sample_utf8))
|
||||
|
||||
self.assertEquals(self.srmock.status, self.resource.sample_status)
|
||||
self.assertEquals(resp.status, self.resource.sample_status)
|
||||
self.assertEquals(resp.body, self.resource.sample_unicode)
|
||||
self.assertEquals(resp.body_encoded, self.resource.sample_utf8)
|
||||
self.assertEquals(body, [self.resource.sample_utf8])
|
||||
|
||||
def test_body_bytes(self):
|
||||
@@ -113,7 +113,7 @@ class TestHelloWorld(testing.TestBase):
|
||||
|
||||
self.assertEquals(self.srmock.status, self.resource.sample_status)
|
||||
self.assertEquals(resp.status, self.resource.sample_status)
|
||||
self.assertEquals(resp.body, self.resource.sample_utf8)
|
||||
self.assertEquals(resp.body_encoded, self.resource.sample_utf8)
|
||||
self.assertEquals(body, [self.resource.sample_utf8])
|
||||
|
||||
def test_data(self):
|
||||
|
||||
17
falcon/tests/test_response_body.py
Normal file
17
falcon/tests/test_response_body.py
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
import falcon
|
||||
import falcon.testing as testing
|
||||
|
||||
|
||||
class TestResponseBody(testing.TestBase):
|
||||
|
||||
def test_append_body(self):
|
||||
text = "Hello beautiful world! "
|
||||
resp = falcon.Response()
|
||||
resp.body = ""
|
||||
|
||||
for token in text.split():
|
||||
resp.body += token
|
||||
resp.body += " "
|
||||
|
||||
self.assertEquals(resp.body, text)
|
||||
Reference in New Issue
Block a user