summaryrefslogtreecommitdiff
path: root/paste
diff options
context:
space:
mode:
authorcce <devnull@localhost>2005-12-26 06:39:30 +0000
committercce <devnull@localhost>2005-12-26 06:39:30 +0000
commit9c4ee3831cd606f2a9c0a23ded20f356db3100c2 (patch)
tree6318647da97100ec65d83e5000701c8bc58ae12f /paste
parenta438d9ab87e23a55b6c73f6919c6c696e2d0ba0b (diff)
downloadpaste-9c4ee3831cd606f2a9c0a23ded20f356db3100c2.tar.gz
- added normalize_headers to paste.httpheaders which sorts a set
of outgoing headers and capitalizes them to exactly match the RFC for extremely dumb user agents (which exist... unfortunately) that don't do case-insensitive matches and assume Camel-Case - added ResponseHeaderWrapper to use python properties to access headers which don't use multiple-entities (like Set-Cookie) - updated comment to header_value to explain why , separator works in most cases (but not all)
Diffstat (limited to 'paste')
-rw-r--r--paste/httpheaders.py37
-rw-r--r--paste/response.py12
-rw-r--r--paste/util/wrapper.py56
3 files changed, 88 insertions, 17 deletions
diff --git a/paste/httpheaders.py b/paste/httpheaders.py
index 7ca81e8..b043a5c 100644
--- a/paste/httpheaders.py
+++ b/paste/httpheaders.py
@@ -32,13 +32,10 @@ It is planned that HTTPHeader will grow three methods:
a WSGI ``response_headers`` list.
"""
-__all__ = ['get_header','known_headers','HTTPHeader' ]
+__all__ = ['get_header', 'HTTPHeader', 'normalize_headers' ]
_headers = {}
-def known_headers():
- return _headers.values()
-
def get_header(name, raiseError=True):
"""
This function finds the corresponding ``HTTPHeader`` for the
@@ -114,7 +111,7 @@ class HTTPHeader(object):
self.style = style
self.category = category
self._catsort = {'general': 1, 'request': 2, 'response': 2,
- 'entity': 3}[category]
+ 'entity': 3}[category]
assert self.name.lower() not in _headers
_headers[self.name.lower()] = self
return self
@@ -132,10 +129,38 @@ class HTTPHeader(object):
if self._catsort != other._catsort:
return self._catsort < other._catsort
return self.name < other.name
- return self.name < other
+ return False
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, self.name)
+
+def normalize_headers(response_headers):
+ """
+ This alters the underlying response_headers to use the common
+ name for each header; as well as sorting them with general
+ headers first, followed by request/response headers, then
+ entity headers, and unknown headers last.
+ """
+ category = {}
+ for idx in range(len(response_headers)):
+ (key,val) = response_headers[idx]
+ head = get_header(key,False)
+ if not head:
+ newhead = '-'.join(x.capitalize() for x in \
+ key.replace("_","-").split("-"))
+ response_headers[idx] = (newhead,val)
+ category[newhead] = 4
+ continue
+ response_headers[idx] = (str(head),val)
+ category[str(head)] = head._catsort
+ def compare(a,b):
+ ac = category[a[0]]
+ bc = category[b[0]]
+ if ac == bc:
+ return cmp(a[0],b[0])
+ return cmp(ac,bc)
+ response_headers.sort(compare)
+
#
# For now, construct a minimalistic version of the field-names; at a
# later date more complicated headers may sprout content constructors.
diff --git a/paste/response.py b/paste/response.py
index 4362d67..9547db8 100644
--- a/paste/response.py
+++ b/paste/response.py
@@ -80,7 +80,17 @@ def header_value(headers, name):
"""
Returns the header's value, or None if no such header. If a
header appears more than once, all the values of the headers
- are joined with ','
+ are joined with ','. Note that this is consistent /w RFC 2616
+ section 4.2 which states:
+
+ It MUST be possible to combine the multiple header fields
+ into one "field-name: field-value" pair, without changing
+ the semantics of the message, by appending each subsequent
+ field-value to the first, each separated by a comma.
+
+ However, note that the original netscape usage of 'Set-Cookie',
+ especially in MSIE which contains an 'expires' date will is not
+ compatible with this particular concatination method.
"""
name = name.lower()
result = [value for header, value in headers
diff --git a/paste/util/wrapper.py b/paste/util/wrapper.py
index 89eaf17..e90e11a 100644
--- a/paste/util/wrapper.py
+++ b/paste/util/wrapper.py
@@ -10,6 +10,7 @@ This module contains wrapper objects for WSGI ``environ`` and
"""
from paste import httpheaders
+from paste.response import replace_header, remove_header, header_value
class EnvironWrapper(object):
"""
@@ -43,11 +44,12 @@ _proplist = [
'wsgi.multithread', 'wsgi.multiprocess', 'wsgi.run_once',
'wsgi.file_wrapper'
]
-for head in dir(httpheaders):
- if head.startswith("HTTP_"):
- if 'response' != getattr(httpheaders,head).category:
- # Only add general, request, and entity headers
- _proplist.append(head)
+
+for name in dir(httpheaders):
+ if name.startswith("HTTP_"):
+ header = getattr(httpheaders,name)
+ if header and 'response' != header.category:
+ _proplist.append(name)
for item in _proplist:
key = item
@@ -57,21 +59,55 @@ for item in _proplist:
return self.environ.get(tmp,None)
def set(self,val,tmp=key):
dict.__setitem__(self.environ,tmp,val)
- return self
setattr(EnvironWrapper, "GET_" + item, get)
setattr(EnvironWrapper, "SET_" + item, set)
setattr(EnvironWrapper, item, property(get,set))
del _proplist
+class ResponseHeadersWrapper(object):
+ """
+ Used to wrap the ``response_headers`` to provide handy mechanism
+ for dealing with the headers using properties.
+
+ wrapped = wrap(response_headers)
+ wrapped.HTTP_CONTENT_ENCODING = 'text/plain'
+ etc.
+
+ wrapped.sort()
+ """
+ def __new__(cls, response_headers):
+ assert list == type(response_headers)
+ self = object.__new__(cls)
+ self.response_headers = response_headers
+ return self
+
+for name in dir(httpheaders):
+ if not name.startswith("HTTP_"):
+ continue
+ header = getattr(httpheaders,name)
+ if 'request' == header.category or 'multi-entry' == header.style:
+ continue
+ name = name[5:]
+ def get(self,tmp=str(header)):
+ return header_value(self.response_headers,tmp)
+ def set(self,val,tmp=str(header)):
+ return replace_header(self.response_headers,tmp,val)
+ setattr(ResponseHeadersWrapper, "GET_" + name, get)
+ setattr(ResponseHeadersWrapper, "SET_" + name, set)
+ setattr(ResponseHeadersWrapper, name, property(get,set))
+
def wrap(obj):
"""
Wraps a WSGI ``environ`` and ``result_headers`` with corresponding
``dict`` and ``list`` items that can be passed on up/down stream.
"""
- if isinstance(obj, EnvironWrapper):
- return obj
if isinstance(obj, dict):
return EnvironWrapper(obj)
- assert False, "Only EnvironWrapper so far"
+ if isinstance(obj, list):
+ return ResponseHeadersWrapper(obj)
+ assert False, ("The object being wrapped is neither a ``dict`` type "
+ "(which corresponds to a WSGI ``environ``), nor is "
+ "it a ``list`` type (which corresponds to WSGI "
+ "``response_headers``.")
-__all__ = ['wrap','EnvironWrapper']
+__all__ = ['wrap','EnvironWrapper', 'ResponseHeadersWrapper']