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:
committed by
Kurt Griffiths
parent
a2a117735e
commit
0c67a3e7ee
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user