1
2
3
4
5
6 import cgi
7 import copy
8 import mimetypes
9 import os
10 from StringIO import StringIO
11 import types
12 import urlparse
13 import uuid
14
15 from restkit.datastructures import MultiDict
16 from restkit.errors import AlreadyRead, RequestError
17 from restkit.forms import multipart_form_encode, form_encode
18 from restkit.tee import ResponseTeeInput
19 from restkit.util import to_bytestring
20 from restkit.util import parse_cookie
21
23
24 - def __init__(self, url, method='GET', body=None, headers=None):
39
41 if not isinstance(self._headers, MultiDict):
42 self._headers = MultiDict(self._headers or [])
43 return self._headers
46 headers = property(_headers__get, _headers__set, doc=_headers__get.__doc__)
47
49 if self.url is None:
50 raise ValueError("url isn't set")
51 return urlparse.urlparse(self.url)
52 parsed_url = property(_parsed_url, doc="parsed url")
53
60 path = property(_path__get)
61
68 host = property(_host__get)
69
71 te = self.headers.iget("transfer-encoding")
72 return (te is not None and te.lower() == "chunked")
73
76
77 - def _set_body(self, body):
78 ctype = self.headers.ipop('content-type', None)
79 clen = self.headers.ipop('content-length', None)
80
81 if isinstance(body, dict):
82 if ctype is not None and \
83 ctype.startswith("multipart/form-data"):
84 type_, opts = cgi.parse_header(ctype)
85 boundary = opts.get('boundary', uuid.uuid4().hex)
86 self._body, self.headers = multipart_form_encode(body,
87 self.headers, boundary)
88
89
90
91
92 ctype = self.headers.ipop('content-type', None)
93 else:
94 ctype = "application/x-www-form-urlencoded; charset=utf-8"
95 self._body = form_encode(body)
96 elif hasattr(body, "boundary") and hasattr(body, "get_size"):
97 ctype = "multipart/form-data; boundary=%s" % body.boundary
98 clen = body.get_size()
99 self._body = body
100 else:
101 self._body = body
102
103 if not ctype:
104 ctype = 'application/octet-stream'
105 if hasattr(self.body, 'name'):
106 ctype = mimetypes.guess_type(body.name)[0]
107
108 if not clen:
109 if hasattr(self._body, 'fileno'):
110 try:
111 self._body.flush()
112 except IOError:
113 pass
114 try:
115 fno = self._body.fileno()
116 clen = str(os.fstat(fno)[6])
117 except IOError:
118 if not self.is_chunked():
119 clen = len(self._body.read())
120 elif hasattr(self._body, 'getvalue') and not \
121 self.is_chunked():
122 clen = len(self._body.getvalue())
123 elif isinstance(self._body, types.StringTypes):
124 self._body = to_bytestring(self._body)
125 clen = len(self._body)
126
127 if clen is not None:
128 self.headers['Content-Length'] = clen
129
130
131
132
133 if ctype is not None:
134 self.headers['Content-Type'] = ctype
135
136 - def _get_body(self):
138 body = property(_get_body, _set_body, doc="request body")
139
141 if self.body is not None:
142 if not hasattr(self.body, 'seek') and \
143 not isinstance(self.body, types.StringTypes):
144 raise RequestError("error: '%s', body can't be rewind."
145 % msg)
146
147
148 -class BodyWrapper(object):
149
150 - def __init__(self, resp, connection):
151 self.resp = resp
152 self.body = resp._body
153 self.connection = connection
154 self._closed = False
155 self.eof = False
156
157 - def __enter__(self):
159
160 - def __exit__(self, exc_type, exc_val, traceback):
162
164 """ release connection """
165 if self._closed:
166 return
167
168 if not self.eof:
169 self.body.read()
170
171 self.connection.release(self.resp.should_close)
172 self._closed = True
173
174 - def __iter__(self):
176
178 try:
179 return self.body.next()
180 except StopIteration:
181 self.eof = True
182 self.close()
183 raise
184
185 - def read(self, n=-1):
186 data = self.body.read(n)
187 if not data:
188 self.eof = True
189 self.close()
190 return data
191
192 - def readline(self, limit=-1):
193 line = self.body.readline(limit)
194 if not line:
195 self.eof = True
196 self.close()
197 return line
198
199 - def readlines(self, hint=None):
200 lines = self.body.readlines(hint)
201 if self.body.close:
202 self.eof = True
203 self.close()
204 return lines
205
206
208
209 charset = "utf8"
210 unicode_errors = 'strict'
211
212 - def __init__(self, connection, request, resp):
213 self.request = request
214 self.connection = connection
215
216 self._resp = resp
217
218
219 self.headers = resp.headers()
220 self.status = resp.status()
221 self.status_int = resp.status_code()
222 self.version = resp.version()
223 self.headerslist = self.headers.items()
224 self.location = self.headers.get('location')
225 self.final_url = request.url
226 self.should_close = not resp.should_keep_alive()
227
228
229 if 'set-cookie' in self.headers:
230 cookie_header = self.headers.get('set-cookie')
231 self.cookies = parse_cookie(cookie_header, self.final_url)
232
233
234 self._closed = False
235 self._already_read = False
236
237 if request.method == "HEAD":
238 """ no body on HEAD, release the connection now """
239 self.connection.release(True)
240 self._body = StringIO("")
241 else:
242 self._body = resp.body_file()
243
245 try:
246 return getattr(self, key)
247 except AttributeError:
248 pass
249 return self.headers.get(key)
250
253
256
258 return not self._already_read
259
262
263 - def skip_body(self):
264 """ skip the body and release the connection """
265 if not self._already_read:
266 self._body.read()
267 self._already_read = True
268 self.connection.release(self.should_close)
269
270 - def body_string(self, charset=None, unicode_errors="strict"):
271 """ return body string, by default in bytestring """
272
273 if not self.can_read():
274 raise AlreadyRead()
275
276
277 body = self._body.read()
278 self._already_read = True
279
280 self.connection.release(self.should_close)
281
282 if charset is not None:
283 try:
284 body = body.decode(charset, unicode_errors)
285 except UnicodeDecodeError:
286 pass
287 return body
288
289 - def body_stream(self):
290 """ stream body """
291 if not self.can_read():
292 raise AlreadyRead()
293
294 self._already_read = True
295
296 return BodyWrapper(self, self.connection)
297
298
300 """ copy response input to standard output or a file if length >
301 sock.MAX_BODY. This make possible to reuse it in your
302 appplication. When all the input has been read, connection is
303 released """
304 return ResponseTeeInput(self, self.connection,
305 should_close=self.should_close)
306 ClientResponse = Response
307