1
2
3
4
5
6 from StringIO import StringIO
7 import urlparse
8
9 from restkit.errors import BadStatusLine, ParserError
12 """ HTTP Parser compatible 1.0 & 1.1
13 This parser can parse HTTP requests and response.
14 """
15
16 - def __init__(self, ptype='response', should_close=False):
17 self.status_line = ""
18 self.status_int = None
19 self.reason = ""
20 self.status = ""
21 self.headers = []
22 self.headers_dict = {}
23 self.raw_version = "HTTP/1.0"
24 self.raw_path = ""
25 self.version = (1,0)
26 self.method = ""
27 self.path = ""
28 self.query_string = ""
29 self.fragment = ""
30 self._content_len = None
31 self.start_offset = 0
32 self.chunk_size = 0
33 self._chunk_eof = False
34 self.type = ptype
35 self._should_close = should_close
36
37 @classmethod
41
42 @classmethod
44 """ return parser object for requests """
45 return cls(ptype='request')
46
48 """ take a string as buffer and an header dict
49 (empty or not). It return new position or -1
50 if parsing isn't done. headers dict is updated
51 with new headers.
52 """
53 line = buf.getvalue()
54 i = line.find("\r\n\r\n")
55 if i != -1:
56 r = line[:i]
57 pos = i+4
58 buf2 = StringIO()
59 buf2.write(line[pos:])
60 return self.finalize_headers(headers, r, buf2)
61 return False
62
64 """ parse the headers """
65 lines = headers_str.split("\r\n")
66
67
68 self._first_line(lines.pop(0))
69
70
71
72
73 _headers = {}
74 hname = ""
75 for line in lines:
76 if line.startswith('\t') or line.startswith(' '):
77 headers[hname] += line.strip()
78 else:
79 try:
80 hname =self._parse_headerl(_headers, line)
81 except ValueError:
82
83 pass
84 self.headers_dict = _headers
85 headers.extend(list(_headers.items()))
86 self.headers = headers
87 self._content_len = int(_headers.get('Content-Length',0))
88
89 if self.type == 'request':
90 (_, _, self.path, self.query_string, self.fragment) = \
91 urlparse.urlsplit(self.raw_path)
92
93 return buf2
94
96 self.raw_version = version.strip()
97 try:
98 major, minor = self.raw_version.split("HTTP/")[1].split(".")
99 self.version = (int(major), int(minor))
100 except IndexError:
101 self.version = (1, 0)
102
122
124 """ parse header line"""
125 name, value = line.split(":", 1)
126 name = name.strip().title()
127 value = value.rsplit("\r\n",1)[0].strip()
128 if name in hdrs:
129 hdrs[name] = "%s, %s" % (hdrs[name], value)
130 else:
131 hdrs[name] = value
132 return name
133
134 @property
136 if self._should_close:
137 return True
138 elif self.headers_dict.get("Connection") == "close":
139 return True
140 elif self.headers_dict.get("Connection") == "Keep-Alive":
141 return False
142 elif self.version <= (1, 0):
143 return True
144 return False
145
146 @property
148 """ is TE: chunked ?"""
149 return (self.headers_dict.get('Transfer-Encoding') == "chunked")
150
151 @property
152 - def content_len(self):
153 """ return content length as integer or
154 None."""
155 transfert_encoding = self.headers_dict.get('Transfer-Encoding')
156 content_length = self.headers_dict.get('Content-Length')
157 if transfert_encoding != "chunked":
158 if content_length is None:
159 return 0
160 return int(content_length)
161 else:
162 return None
163
164 - def body_eof(self):
165 """do we have all the body ?"""
166 if self.is_chunked:
167 if self._chunk_eof:
168 return True
169 elif self._content_len == 0:
170 return True
171 return False
172
174 line = buf.getvalue()
175 buf2 = StringIO()
176
177 if not self.start_offset:
178 i = line.find("\r\n")
179 if i != -1:
180 chunk = line[:i].strip().split(";", 1)
181 chunk_size = int(chunk.pop(0), 16)
182 self.start_offset = i+2
183 self.chunk_size = chunk_size
184
185 if self.start_offset:
186 if self.chunk_size == 0:
187 self._chunk_eof = True
188 buf2.write(line[:self.start_offset])
189 return '', buf2
190 else:
191 chunk = line[self.start_offset:self.start_offset+self.chunk_size]
192 end_offset = self.start_offset + self.chunk_size + 2
193
194 if buf.len >= end_offset:
195 buf2.write(line[end_offset:])
196 self.chunk_size = 0
197 return chunk, buf2
198 return '', buf
199
201 line = buf.getvalue()
202 i = line.find("\r\n\r\n")
203 return (i != -1)
204
205 - def filter_body(self, buf):
206 """\
207 Filter body and return a tuple: (body_chunk, new_buffer)
208 Both can be None, and new_buffer is always None if its empty.
209 """
210 dlen = buf.len
211 chunk = ''
212
213 if self.is_chunked:
214 try:
215 chunk, buf2 = self.read_chunk(buf)
216 except Exception, e:
217 raise ParserError("chunked decoding error [%s]" % str(e))
218
219 if not chunk:
220 return '', buf
221 else:
222 buf2 = StringIO()
223 if self._content_len > 0:
224 nr = min(dlen, self._content_len)
225 chunk = buf.getvalue()[:nr]
226 self._content_len -= nr
227
228 self.start_offset = 0
229 buf2.seek(0, 2)
230 return (chunk, buf2)
231