fix(Response): HTTP headers as strings

Ensure HTTP headers and values are string types per PEP3333. This
fixes WSGI servers running under py2k.

Fixes #413

Co-Authored-By: Kurt Griffiths <mail@kgriffs.com>
This commit is contained in:
Gino Ledesma
2015-04-13 13:47:07 -04:00
committed by Kurt Griffiths
parent a2a117735e
commit 0c67a3e7ee
2 changed files with 56 additions and 17 deletions

View File

@@ -272,15 +272,14 @@ class Response(object):
For setting cookies, see instead :meth:`~.set_cookie`
Args:
name (str): Header name to set (case-insensitive). Must be of
type ``str`` or ``StringType``, and only character values 0x00
through 0xFF may be used on platforms that use wide
characters.
name (str): Header name (case-insensitive). The restrictions
noted below for the header's value also apply here.
value (str): Value for the header. Must be of type ``str`` or
``StringType``, and only character values 0x00 through 0xFF
may be used on platforms that use wide characters.
``StringType`` and contain only ISO-8859-1 characters.
Under Python 2.x, the ``unicode`` type is also accepted,
although such strings are also limited to ISO-8859-1.
"""
name, value = self._encode_header(name, value)
# NOTE(kgriffs): normalize name by lowercasing it
self._headers[name.lower()] = value
@@ -297,15 +296,16 @@ class Response(object):
For setting cookies, see :py:meth:`~.set_cookie`
Args:
name (str): Header name to set (case-insensitive). Must be of
type ``str`` or ``StringType``, and only character values 0x00
through 0xFF may be used on platforms that use wide
characters.
name (str): Header name (case-insensitive). The restrictions
noted below for the header's value also apply here.
value (str): Value for the header. Must be of type ``str`` or
``StringType``, and only character values 0x00 through 0xFF
may be used on platforms that use wide characters.
``StringType`` and contain only ISO-8859-1 characters.
Under Python 2.x, the ``unicode`` type is also accepted,
although such strings are also limited to ISO-8859-1.
"""
name, value = self._encode_header(name, value)
name = name.lower()
if name in self._headers:
value = self._headers[name] + ',' + value
@@ -320,10 +320,11 @@ class Response(object):
Args:
headers (dict or list): A dictionary of header names and values
to set, or ``list`` of (*name*, *value*) tuples. Both *name*
and *value* must be of type ``str`` or ``StringType``, and
only character values 0x00 through 0xFF may be used on
platforms that use wide characters.
to set, or a ``list`` of (*name*, *value*) tuples. Both *name*
and *value* must be of type ``str`` or ``StringType`` and
contain only ISO-8859-1 characters. Under Python 2.x, the
``unicode`` type is also accepted, although such strings are
also limited to ISO-8859-1.
Note:
Falcon can process a list of tuples slightly faster
@@ -341,6 +342,7 @@ class Response(object):
# normalize the header names.
_headers = self._headers
for name, value in headers:
name, value = self._encode_header(name, value)
_headers[name.lower()] = value
def add_link(self, target, rel, title=None, title_star=None,
@@ -541,6 +543,16 @@ class Response(object):
""",
lambda v: ', '.join(v))
def _encode_header(self, name, value, py2=PY2):
if py2: # pragma: no cover
if isinstance(name, unicode):
name = name.encode('ISO-8859-1')
if isinstance(value, unicode):
value = value.encode('ISO-8859-1')
return name, value
def _wsgi_headers(self, media_type=None, py2=PY2):
"""Convert headers into the format expected by WSGI servers.

View File

@@ -117,6 +117,17 @@ class LocationHeaderUnicodeResource:
resp.content_location = self.URL1
class UnicodeHeaderResource:
def on_get(self, req, resp):
resp.set_headers([
(u'X-auTH-toKEN', 'toomanysecrets'),
('Content-TYpE', u'application/json'),
(u'X-symBOl', u'\u0040'),
(u'X-symb\u00F6l', u'\u00FF'),
])
class VaryHeaderResource:
def __init__(self, vary):
@@ -361,6 +372,22 @@ class TestHeaders(testing.TestBase):
content_location = ('content-location', '/%C3%A7runchy/bacon')
self.assertIn(content_location, self.srmock.headers)
def test_unicode_headers(self):
self.api.add_route(self.test_route, UnicodeHeaderResource())
self.simulate_request(self.test_route)
expect = ('x-auth-token', 'toomanysecrets')
self.assertIn(expect, self.srmock.headers)
expect = ('content-type', 'application/json')
self.assertIn(expect, self.srmock.headers)
expect = ('x-symbol', '@')
self.assertIn(expect, self.srmock.headers)
expect = ('x-symb\xF6l', '\xFF')
self.assertIn(expect, self.srmock.headers)
def test_response_set_and_get_header(self):
self.resource = HeaderHelpersResource()
self.api.add_route(self.test_route, self.resource)