1 """HTTP library functions."""
2
3
4
5
6
7
8
9 from BaseHTTPServer import BaseHTTPRequestHandler
10 response_codes = BaseHTTPRequestHandler.responses.copy()
11
12
13 response_codes[500] = ('Internal Server Error',
14 'The server encountered an unexpected condition '
15 'which prevented it from fulfilling the request.')
16 response_codes[503] = ('Service Unavailable',
17 'The server is currently unable to handle the '
18 'request due to a temporary overloading or '
19 'maintenance of the server.')
20
21
22 import cgi
23 from email.Header import Header, decode_header
24 import re
25 import rfc822
26 HTTPDate = rfc822.formatdate
27 import time
28
29
31 url = "/".join(atoms)
32 while "//" in url:
33 url = url.replace("//", "/")
34 return url
35
37 """Return a protocol tuple from the given 'HTTP/x.y' string."""
38 return int(protocol_str[5]), int(protocol_str[7])
39
41 """Return a list of (start, stop) indices from a Range header, or None.
42
43 Each (start, stop) tuple will be composed of two ints, which are suitable
44 for use in a slicing operation. That is, the header "Range: bytes=3-6",
45 if applied against a Python string, is requesting resource[3:7]. This
46 function will return the list [(3, 7)].
47
48 If this function return an empty list, you should return HTTP 416.
49 """
50
51 if not headervalue:
52 return None
53
54 result = []
55 bytesunit, byteranges = headervalue.split("=", 1)
56 for brange in byteranges.split(","):
57 start, stop = [x.strip() for x in brange.split("-", 1)]
58 if start:
59 if not stop:
60 stop = content_length - 1
61 start, stop = map(int, (start, stop))
62 if start >= content_length:
63
64
65
66
67
68
69
70
71 continue
72 if stop < start:
73
74
75
76
77
78
79 return None
80 result.append((start, stop + 1))
81 else:
82 if not stop:
83
84 return None
85
86 result.append((content_length - int(stop), content_length))
87
88 return result
89
90
92 """An element (with parameters) from an HTTP header's element list."""
93
99
101 p = [";%s=%s" % (k, v) for k, v in self.params.iteritems()]
102 return u"%s%s" % (self.value, "".join(p))
103
106
108 """Transform 'token;key=val' to ('token', {'key': 'val'})."""
109
110
111 atoms = [x.strip() for x in elementstr.split(";")]
112 initial_value = atoms.pop(0).strip()
113 params = {}
114 for atom in atoms:
115 atom = [x.strip() for x in atom.split("=", 1) if x.strip()]
116 key = atom.pop(0)
117 if atom:
118 val = atom[0]
119 else:
120 val = ""
121 params[key] = val
122 return initial_value, params
123 parse = staticmethod(parse)
124
126 """Construct an instance from a string of the form 'token;key=val'."""
127 ival, params = cls.parse(elementstr)
128 return cls(ival, params)
129 from_str = classmethod(from_str)
130
131
132 q_separator = re.compile(r'; *q *=')
133
135 """An element (with parameters) from an Accept-* header's element list."""
136
152 from_str = classmethod(from_str)
153
155 val = self.params.get("q", "1")
156 if isinstance(val, HeaderElement):
157 val = val.value
158 return float(val)
159 qvalue = property(qvalue, doc="The qvalue, or priority, of this value.")
160
162
163
164 diff = cmp(other.qvalue, self.qvalue)
165 if diff == 0:
166 diff = cmp(str(other), str(self))
167 return diff
168
169
171 """Return a HeaderElement list from a comma-separated header str."""
172
173 if not fieldvalue:
174 return None
175 headername = fieldname.lower()
176
177 result = []
178 for element in fieldvalue.split(","):
179 if headername.startswith("accept") or headername == 'te':
180 hv = AcceptElement.from_str(element)
181 else:
182 hv = HeaderElement.from_str(element)
183 result.append(hv)
184
185 result.sort()
186 return result
187
188 -def decode_TEXT(value):
189 """Decode RFC-2047 TEXT (e.g. "=?utf-8?q?f=C3=BCr?=" -> u"f\xfcr")."""
190 atoms = decode_header(value)
191 decodedvalue = ""
192 for atom, charset in atoms:
193 if charset is not None:
194 atom = atom.decode(charset)
195 decodedvalue += atom
196 return decodedvalue
197
199 """Return legal HTTP status Code, Reason-phrase and Message.
200
201 The status arg must be an int, or a str that begins with an int.
202
203 If status is an int, or a str and no reason-phrase is supplied,
204 a default reason-phrase will be provided.
205 """
206
207 if not status:
208 status = 200
209
210 status = str(status)
211 parts = status.split(" ", 1)
212 if len(parts) == 1:
213
214 code, = parts
215 reason = None
216 else:
217 code, reason = parts
218 reason = reason.strip()
219
220 try:
221 code = int(code)
222 except ValueError:
223 raise ValueError("Illegal response status from server "
224 "(%s is non-numeric)." % repr(code))
225
226 if code < 100 or code > 599:
227 raise ValueError("Illegal response status from server "
228 "(%s is out of range)." % repr(code))
229
230 if code not in response_codes:
231
232 default_reason, message = "", ""
233 else:
234 default_reason, message = response_codes[code]
235
236 if reason is None:
237 reason = default_reason
238
239 return code, reason, message
240
241
242 image_map_pattern = re.compile(r"[0-9]+,[0-9]+")
243
245 """Build a params dictionary from a query_string."""
246 if image_map_pattern.match(query_string):
247
248
249 pm = query_string.split(",")
250 pm = {'x': int(pm[0]), 'y': int(pm[1])}
251 else:
252 pm = cgi.parse_qs(query_string, keep_blank_values)
253 for key, val in pm.items():
254 if len(val) == 1:
255 pm[key] = val[0]
256 return pm
257
277
278
280 """A case-insensitive dict subclass.
281
282 Each key is changed on entry to str(key).title().
283 """
284
287
290
293
296
297 - def get(self, key, default=None):
299
302
306
308 newdict = cls()
309 for k in seq:
310 newdict[str(k).title()] = value
311 return newdict
312 fromkeys = classmethod(fromkeys)
313
315 key = str(key).title()
316 try:
317 return self[key]
318 except KeyError:
319 self[key] = x
320 return x
321
322 - def pop(self, key, default):
324
325
327 """A dict subclass for HTTP request and response headers.
328
329 Each key is changed on entry to str(key).title(). This allows headers
330 to be case-insensitive and avoid duplicates.
331
332 Values are header values (decoded according to RFC 2047 if necessary).
333 """
334
336 """Return a list of HeaderElements for the given header (or None)."""
337 key = str(key).title()
338 h = self.get(key)
339 if h is None:
340 return []
341 return header_elements(key, h)
342
344 """Transform self into a list of (name, value) tuples."""
345 header_list = []
346 for key, v in self.iteritems():
347 if isinstance(v, unicode):
348
349
350
351
352
353 try:
354 v = v.encode("iso-8859-1")
355 except UnicodeEncodeError:
356 if protocol >= (1, 1):
357
358
359