1
2
3
4
5
6
7 import urlparse
8
9 from restkit.errors import BadStatusLine, ParserError
10 from restkit.util import normalize_name
13
14 - def __init__(self, ptype='response', should_close=False):
15 self.status_line = ""
16 self.status_int = None
17 self.reason = ""
18 self.status = ""
19 self.headers = []
20 self.headers_dict = {}
21 self.raw_version = "HTTP/1.0"
22 self.raw_path = ""
23 self.version = (1,0)
24 self.method = ""
25 self.path = ""
26 self.query_string = ""
27 self.fragment = ""
28 self._content_len = None
29 self.start_offset = 0
30 self.chunk_size = 0
31 self._chunk_eof = False
32 self.type = ptype
33 self._should_close = should_close
34
35 @classmethod
38
39 @classmethod
41 return cls(ptype='request')
42
44 """ take a string as buffer and an header dict
45 (empty or not). It return new position or -1
46 if parsing isn't done. headers dict is updated
47 with new headers.
48 """
49
50 s = "".join(buf)
51 i = s.find("\r\n\r\n")
52 if i != -1:
53 r = s[:i]
54 pos = i+4
55 return self.finalize_headers(headers, r, pos)
56 return -1
57
59 """ parse the headers """
60 lines = headers_str.split("\r\n")
61
62
63 self._first_line(lines.pop(0))
64
65
66
67
68 _headers = {}
69 hname = ""
70 for line in lines:
71 if line == "\t":
72 headers[hname] += line.strip()
73 else:
74 try:
75 hname =self._parse_headerl(_headers, line)
76 except ValueError:
77
78 pass
79 self.headers_dict = _headers
80 headers.extend(list(_headers.items()))
81 self.headers = headers
82 self._content_len = int(_headers.get('Content-Length',0))
83 if self.type == 'request':
84 (_, _, self.path, self.query_string, self.fragment) = \
85 urlparse.urlsplit(self.raw_path)
86 return pos
87
89 """ parse first line """
90 self.status_line = status_line = line.strip()
91
92 try:
93 if self.type == 'response':
94 version, self.status = status_line.split(" ", 1)
95 else:
96 method, path, version = status_line.split(" ")
97 except ValueError:
98 return
99
100 version = version.strip()
101 self.raw_version = version
102 try:
103 major, minor = version.split("HTTP/")[1].split(".")
104 version = (int(major), int(minor))
105 except IndexError:
106 version = (1, 0)
107
108 self.version = version
109
110 if self.type == 'response':
111 try:
112 try:
113 self.status_int, self.reason = self.status.split(" ", 1)
114 except ValueError:
115 self.status_int = self.status
116 self.status_int = int(self.status_int)
117 except ValueError:
118 raise BadStatusLine("can't find status code")
119 else:
120 self.method = method.upper()
121 self.raw_path = path
122
124 """ parse header line"""
125 name, value = line.split(":", 1)
126 name = normalize_name(name.strip())
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 s = "".join(data)
175 if not self.start_offset:
176 i = s.find("\r\n")
177 if i != -1:
178 chunk = s[:i].strip().split(";", 1)
179 chunk_size = int(chunk.pop(0), 16)
180 self.start_offset = i+2
181 self.chunk_size = chunk_size
182
183 if self.start_offset:
184 if self.chunk_size == 0:
185 self._chunk_eof = True
186 ret = '', data[:self.start_offset]
187 return ret
188 else:
189 chunk = s[self.start_offset:self.start_offset+self.chunk_size]
190 end_offset = self.start_offset + self.chunk_size + 2
191
192 if len(data) >= end_offset:
193 ret = chunk, data[end_offset:]
194 self.chunk_size = 0
195 return ret
196 return '', data
197
199 s = "".join(data)
200 i = s.find("\r\n\r\n")
201 return (i != -1)
202
203 - def filter_body(self, data):
204 """\
205 Filter body and return a tuple: (body_chunk, new_buffer)
206 Both can be None, and new_buffer is always None if its empty.
207 """
208 dlen = len(data)
209 chunk = ''
210 if self.is_chunked:
211 try:
212 chunk, data = self.read_chunk(data)
213 except Exception, e:
214 raise ParserError("chunked decoding error [%s]" % str(e))
215
216 if not chunk:
217 return '', data
218 else:
219 if self._content_len > 0:
220 nr = min(dlen, self._content_len)
221 chunk = "".join(data[:nr])
222 self._content_len -= nr
223 data = []
224
225 self.start_offset = 0
226 return (chunk, data)
227