summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hellkamp <marc@gsites.de>2010-08-27 19:15:28 +0200
committerMarcel Hellkamp <marc@gsites.de>2010-08-27 19:15:28 +0200
commit5e609047e6bcd5f8cdcb49dca222977be966b506 (patch)
tree8781c28a261bc70a49daa6bd3e5cead3d1e13ff0
parent27f0ecde4d22471c452c07cb2b5f839bb68a192a (diff)
downloadbottle-5e609047e6bcd5f8cdcb49dca222977be966b506.tar.gz
The Request.headers dict is now guaranteed to contain native strings (bytes or unicode) based on
the running python version. If the WSGI environment contains non-native strings, these are de- or encoded using 'utf8' (default) or 'latin1' (fallback). This API will remain stable even on WSGI spec changes, if possible. If you need the unmodified value, call Request.headers.raw(key).
-rwxr-xr-xapidoc/api.rst5
-rwxr-xr-xapidoc/changelog.rst5
-rwxr-xr-xbottle.py59
3 files changed, 58 insertions, 11 deletions
diff --git a/apidoc/api.rst b/apidoc/api.rst
index d3db5c9..74cb092 100755
--- a/apidoc/api.rst
+++ b/apidoc/api.rst
@@ -83,7 +83,10 @@ Data Structures
.. autoclass:: HeaderDict
:members:
-
+
+.. autoclass:: WSGIHeaderDict
+ :members:
+
.. autoclass:: AppStack
:members:
diff --git a/apidoc/changelog.rst b/apidoc/changelog.rst
index 1b2b561..23a35a3 100755
--- a/apidoc/changelog.rst
+++ b/apidoc/changelog.rst
@@ -15,6 +15,11 @@ This changes are not released yet and are only part of the development documenta
* A new hook-API to inject code immediately before or after the execution of handler callbacks.
* The :meth:`Bottle.route` decorator got a lot of new features. See API documentation for details.
+* The :attr:`Request.headers` dict is now guaranteed to contain native strings but still allows access to the raw data provided by the WSGI environment (see :class:`WSGIHeaderDict`).
+
+.. rubric:: API changes
+
+* :attr:`Request.header` is now :attr:`Request.headers`
Bugfix Release 0.8.3
diff --git a/bottle.py b/bottle.py
index c34d1ab..c999b82 100755
--- a/bottle.py
+++ b/bottle.py
@@ -796,16 +796,14 @@ class Request(threading.local, DictMixin):
@property
def header(self):
- ''' :class:`HeaderDict` filled with request headers.
+ depr("The Request.header property was renamed to Request.headers")
+ return self.headers
- HeaderDict keys are case insensitive str.title()d
- '''
+ @property
+ def headers(self):
+ ''' :class:`WSGIHeaderDict` filled with request headers. '''
if 'bottle.headers' not in self.environ:
- header = self.environ['bottle.headers'] = HeaderDict()
- for key, value in self.environ.iteritems():
- if key.startswith('HTTP_'):
- key = key[5:].replace('_','-').title()
- header[key] = value
+ self.environ['bottle.headers'] = WSGIHeaderDict(self.environ)
return self.environ['bottle.headers']
@property
@@ -905,7 +903,7 @@ class Request(threading.local, DictMixin):
This implementation currently only supports basic auth and returns
None on errors.
"""
- return parse_auth(self.environ.get('HTTP_AUTHORIZATION',''))
+ return parse_auth(self.headers.get('Autorization',''))
@property
def COOKIES(self):
@@ -915,7 +913,7 @@ class Request(threading.local, DictMixin):
Request.get_cookie() for details.
"""
if 'bottle.cookies' not in self.environ:
- raw_dict = SimpleCookie(self.environ.get('HTTP_COOKIE',''))
+ raw_dict = SimpleCookie(self.headers.get('Cookie',''))
self.environ['bottle.cookies'] = {}
for cookie in raw_dict.itervalues():
self.environ['bottle.cookies'][cookie.key] = cookie.value
@@ -1078,6 +1076,47 @@ class HeaderDict(MultiDict):
def httpkey(self, key): return str(key).replace('_','-').title()
+
+class WSGIHeaderDict(DictMixin):
+ ''' This dict-like class takes a WSGI environ dict and provides convenient
+ access to HTTP_* fields. Keys and values are stored as native strings
+ (bytes/unicode) based on the python version used (2/3) and keys are
+ case-insensistive. If the WSGI environment contains non-native strings,
+ these are de- or encoded using 'utf8' (default) or 'latin1' (fallback)
+ charset. To get the original value, use the .raw(key) method.
+
+ This is not a MultiDict because incoming headers are unique. The API
+ will remain stable even on WSGI spec changes, if possible.
+ '''
+
+ def __init__(self, environ):
+ self.cache = {}
+ self.environ = environ
+ for key, value in self.environ.iteritems():
+ key = tonat(key, 'latin1') # Headers are limited to ASCII anyway
+ if key.startswith('HTTP_'):
+ self[key[5:].replace('_','-')] = value
+
+ def __len__(self): return len(self.cache)
+ def keys(self): return self.cache.keys()
+ def __iter__(self): return iter(self.cache)
+ def __contains__(self, key): return key.title() in self.keys()
+ def __delitem__(self, key): del self.cache[key.title()]
+ def __getitem__(self, key): return self.cache[key.title()]
+ def __setitem__(self, key, value):
+ try:
+ self.cache[key.title()] = tonat(value, 'utf8')
+ except UnicodeError:
+ self.cache[key.title()] = tonat(value, 'latin1')
+
+ def raw(self, key, default=None):
+ ''' Return the raw WSGI header value for that key. '''
+ ekey = 'HTTP_%s' % key.replace('-','_').upper()
+ return self.environ.get(ekey, default)
+
+
+
+
class AppStack(list):
""" A stack implementation. """