diff options
author | Marcel Hellkamp <marc@gsites.de> | 2010-08-27 19:15:28 +0200 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2010-08-27 19:15:28 +0200 |
commit | 5e609047e6bcd5f8cdcb49dca222977be966b506 (patch) | |
tree | 8781c28a261bc70a49daa6bd3e5cead3d1e13ff0 | |
parent | 27f0ecde4d22471c452c07cb2b5f839bb68a192a (diff) | |
download | bottle-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-x | apidoc/api.rst | 5 | ||||
-rwxr-xr-x | apidoc/changelog.rst | 5 | ||||
-rwxr-xr-x | bottle.py | 59 |
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 @@ -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. """ |