diff options
| author | cce <devnull@localhost> | 2005-12-26 06:39:30 +0000 |
|---|---|---|
| committer | cce <devnull@localhost> | 2005-12-26 06:39:30 +0000 |
| commit | 9c4ee3831cd606f2a9c0a23ded20f356db3100c2 (patch) | |
| tree | 6318647da97100ec65d83e5000701c8bc58ae12f /paste | |
| parent | a438d9ab87e23a55b6c73f6919c6c696e2d0ba0b (diff) | |
| download | paste-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.py | 37 | ||||
| -rw-r--r-- | paste/response.py | 12 | ||||
| -rw-r--r-- | paste/util/wrapper.py | 56 |
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'] |
