]> arthur.barton.de Git - bup.git/blob - lib/tornado/httputil.py
Always publish (l)utimes in helpers when available and fix type conversions.
[bup.git] / lib / tornado / httputil.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2009 Facebook
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License. You may obtain
7 # a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
15 # under the License.
16
17 """HTTP utility code shared by clients and servers."""
18
19 class HTTPHeaders(dict):
20     """A dictionary that maintains Http-Header-Case for all keys.
21
22     Supports multiple values per key via a pair of new methods,
23     add() and get_list().  The regular dictionary interface returns a single
24     value per key, with multiple values joined by a comma.
25
26     >>> h = HTTPHeaders({"content-type": "text/html"})
27     >>> h.keys()
28     ['Content-Type']
29     >>> h["Content-Type"]
30     'text/html'
31
32     >>> h.add("Set-Cookie", "A=B")
33     >>> h.add("Set-Cookie", "C=D")
34     >>> h["set-cookie"]
35     'A=B,C=D'
36     >>> h.get_list("set-cookie")
37     ['A=B', 'C=D']
38
39     >>> for (k,v) in sorted(h.get_all()):
40     ...    print '%s: %s' % (k,v)
41     ...
42     Content-Type: text/html
43     Set-Cookie: A=B
44     Set-Cookie: C=D
45     """
46     def __init__(self, *args, **kwargs):
47         # Don't pass args or kwargs to dict.__init__, as it will bypass
48         # our __setitem__
49         dict.__init__(self)
50         self._as_list = {}
51         self.update(*args, **kwargs)
52
53     # new public methods
54
55     def add(self, name, value):
56         """Adds a new value for the given key."""
57         norm_name = HTTPHeaders._normalize_name(name)
58         if norm_name in self:
59             # bypass our override of __setitem__ since it modifies _as_list
60             dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)
61             self._as_list[norm_name].append(value)
62         else:
63             self[norm_name] = value
64
65     def get_list(self, name):
66         """Returns all values for the given header as a list."""
67         norm_name = HTTPHeaders._normalize_name(name)
68         return self._as_list.get(norm_name, [])
69
70     def get_all(self):
71         """Returns an iterable of all (name, value) pairs.
72
73         If a header has multiple values, multiple pairs will be
74         returned with the same name.
75         """
76         for name, list in self._as_list.iteritems():
77             for value in list:
78                 yield (name, value)
79
80     def parse_line(self, line):
81         """Updates the dictionary with a single header line.
82
83         >>> h = HTTPHeaders()
84         >>> h.parse_line("Content-Type: text/html")
85         >>> h.get('content-type')
86         'text/html'
87         """
88         name, value = line.split(":", 1)
89         self.add(name, value.strip())
90
91     @classmethod
92     def parse(cls, headers):
93         """Returns a dictionary from HTTP header text.
94
95         >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n")
96         >>> sorted(h.iteritems())
97         [('Content-Length', '42'), ('Content-Type', 'text/html')]
98         """
99         h = cls()
100         for line in headers.splitlines():
101             if line:
102                 h.parse_line(line)
103         return h
104
105     # dict implementation overrides
106
107     def __setitem__(self, name, value):
108         norm_name = HTTPHeaders._normalize_name(name)
109         dict.__setitem__(self, norm_name, value)
110         self._as_list[norm_name] = [value]
111
112     def __getitem__(self, name):
113         return dict.__getitem__(self, HTTPHeaders._normalize_name(name))
114
115     def __delitem__(self, name):
116         norm_name = HTTPHeaders._normalize_name(name)
117         dict.__delitem__(self, norm_name)
118         del self._as_list[norm_name]
119
120     def get(self, name, default=None):
121         return dict.get(self, HTTPHeaders._normalize_name(name), default)
122
123     def update(self, *args, **kwargs):
124         # dict.update bypasses our __setitem__
125         for k, v in dict(*args, **kwargs).iteritems():
126             self[k] = v
127
128     @staticmethod
129     def _normalize_name(name):
130         """Converts a name to Http-Header-Case.
131
132         >>> HTTPHeaders._normalize_name("coNtent-TYPE")
133         'Content-Type'
134         """
135         return "-".join([w.capitalize() for w in name.split("-")])
136
137
138 if __name__ == "__main__":
139     import doctest
140     doctest.testmod()