1from collections import MutableMapping
2from webob.compat import (
3    iteritems_,
4    string_types,
5    )
6from webob.multidict import MultiDict
7
8__all__ = ['ResponseHeaders', 'EnvironHeaders']
9
10class ResponseHeaders(MultiDict):
11    """
12        Dictionary view on the response headerlist.
13        Keys are normalized for case and whitespace.
14    """
15    def __getitem__(self, key):
16        key = key.lower()
17        for k, v in reversed(self._items):
18            if k.lower() == key:
19                return v
20        raise KeyError(key)
21
22    def getall(self, key):
23        key = key.lower()
24        result = []
25        for k, v in self._items:
26            if k.lower() == key:
27                result.append(v)
28        return result
29
30    def mixed(self):
31        r = self.dict_of_lists()
32        for key, val in iteritems_(r):
33            if len(val) == 1:
34                r[key] = val[0]
35        return r
36
37    def dict_of_lists(self):
38        r = {}
39        for key, val in iteritems_(self):
40            r.setdefault(key.lower(), []).append(val)
41        return r
42
43    def __setitem__(self, key, value):
44        norm_key = key.lower()
45        items = self._items
46        for i in range(len(items)-1, -1, -1):
47            if items[i][0].lower() == norm_key:
48                del items[i]
49        self._items.append((key, value))
50
51    def __delitem__(self, key):
52        key = key.lower()
53        items = self._items
54        found = False
55        for i in range(len(items)-1, -1, -1):
56            if items[i][0].lower() == key:
57                del items[i]
58                found = True
59        if not found:
60            raise KeyError(key)
61
62    def __contains__(self, key):
63        key = key.lower()
64        for k, v in self._items:
65            if k.lower() == key:
66                return True
67        return False
68
69    has_key = __contains__
70
71    def setdefault(self, key, default=None):
72        c_key = key.lower()
73        for k, v in self._items:
74            if k.lower() == c_key:
75                return v
76        self._items.append((key, default))
77        return default
78
79    def pop(self, key, *args):
80        if len(args) > 1:
81            raise TypeError("pop expected at most 2 arguments, got %s"
82                              % repr(1 + len(args)))
83        key = key.lower()
84        for i in range(len(self._items)):
85            if self._items[i][0].lower() == key:
86                v = self._items[i][1]
87                del self._items[i]
88                return v
89        if args:
90            return args[0]
91        else:
92            raise KeyError(key)
93
94
95
96
97
98
99key2header = {
100    'CONTENT_TYPE': 'Content-Type',
101    'CONTENT_LENGTH': 'Content-Length',
102    'HTTP_CONTENT_TYPE': 'Content_Type',
103    'HTTP_CONTENT_LENGTH': 'Content_Length',
104}
105
106header2key = dict([(v.upper(),k) for (k,v) in key2header.items()])
107
108def _trans_key(key):
109    if not isinstance(key, string_types):
110        return None
111    elif key in key2header:
112        return key2header[key]
113    elif key.startswith('HTTP_'):
114        return key[5:].replace('_', '-').title()
115    else:
116        return None
117
118def _trans_name(name):
119    name = name.upper()
120    if name in header2key:
121        return header2key[name]
122    return 'HTTP_'+name.replace('-', '_')
123
124class EnvironHeaders(MutableMapping):
125    """An object that represents the headers as present in a
126    WSGI environment.
127
128    This object is a wrapper (with no internal state) for a WSGI
129    request object, representing the CGI-style HTTP_* keys as a
130    dictionary.  Because a CGI environment can only hold one value for
131    each key, this dictionary is single-valued (unlike outgoing
132    headers).
133    """
134
135    def __init__(self, environ):
136        self.environ = environ
137
138    def __getitem__(self, hname):
139        return self.environ[_trans_name(hname)]
140
141    def __setitem__(self, hname, value):
142        self.environ[_trans_name(hname)] = value
143
144    def __delitem__(self, hname):
145        del self.environ[_trans_name(hname)]
146
147    def keys(self):
148        return filter(None, map(_trans_key, self.environ))
149
150    def __contains__(self, hname):
151        return _trans_name(hname) in self.environ
152
153    def __len__(self):
154        return len(list(self.keys()))
155
156    def __iter__(self):
157        for k in self.keys():
158            yield k
159