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
22
23 - def __init__(self, url, method='GET', body=None, headers=None):
38
40 if not isinstance(self._headers, MultiDict):
41 self._headers = MultiDict(self._headers or [])
42 return self._headers
45 headers = property(_headers__get, _headers__set, doc=_headers__get.__doc__)
46
48 if self.url is None:
49 raise ValueError("url isn't set")
50 return urlparse.urlparse(self.url)
51 parsed_url = property(_parsed_url, doc="parsed url")
52
59 path = property(_path__get)
60
62 try:
63 h = self.parsed_url.netloc.encode('ascii')
64 except UnicodeEncodeError:
65 h = self.parsed_url.netloc.encode('idna')
66
67 hdr_host = self.headers.iget("host")
68 if not hdr_host:
69 return h
70 return hdr_host
71 host = property(_host__get)
72
74 te = self.headers.iget("transfer-encoding")
75 return (te is not None and te.lower() == "chunked")
76
79
80 - def _set_body(self, body):
81 ctype = self.headers.ipop('content-type', None)
82 clen = self.headers.ipop('content-length', None)
83
84 if isinstance(body, dict):
85 if ctype is not None and \
86 ctype.startswith("multipart/form-data"):
87 type_, opts = cgi.parse_header(ctype)
88 boundary = opts.get('boundary', uuid.uuid4().hex)
89 self._body, self.headers = multipart_form_encode(body,
90 self.headers, boundary)
91
92
93
94
95 ctype = self.headers.ipop('content-type', None)
96 else:
97 ctype = "application/x-www-form-urlencoded; charset=utf-8"
98 self._body = form_encode(body)
99 elif hasattr(body, "boundary") and hasattr(body, "get_size"):
100 ctype = "multipart/form-data; boundary=%s" % body.boundary
101 clen = body.get_size()
102 self._body = body
103 else:
104 self._body = body
105
106 if not ctype:
107 ctype = 'application/octet-stream'
108 if hasattr(self.body, 'name'):
109 ctype = mimetypes.guess_type(body.name)[0]
110
111 if not clen:
112 if hasattr(self._body, 'fileno'):
113 try:
114 self._body.flush()
115 except IOError:
116 pass
117 try:
118 fno = self._body.fileno()
119 clen = str(os.fstat(fno)[6])
120 except IOError:
121 if not self.is_chunked():
122 clen = len(self._body.read())
123 elif hasattr(self._body, 'getvalue') and not \
124 self.is_chunked():
125 clen = len(self._body.getvalue())
126 elif isinstance(self._body, types.StringTypes):
127 self._body = to_bytestring(self._body)
128 clen = len(self._body)
129
130 if clen is not None:
131 self.headers['Content-Length'] = clen
132
133
134
135
136 if ctype is not None:
137 self.headers['Content-Type'] = ctype
138
139 - def _get_body(self):
141 body = property(_get_body, _set_body, doc="request body")
142
143
144 -class BodyWrapper(object):
145
146 - def __init__(self, resp, connection):
147 self.resp = resp
148 self.body = resp._body
149 self.connection = connection
150
151 - def __enter__(self):
153
154 - def __exit__(self, exc_type, exc_val, traceback):
156
158 """ release connection """
159 self.connection.release(self.resp.should_close)
160
161 - def __iter__(self):
163
165 try:
166 return self.body.next()
167 except StopIteration:
168 self.close()
169 raise
170
171 - def read(self, n=-1):
172 data = self.body.read(n)
173 if not data:
174 self.close()
175 return data
176
177 - def readline(self, limit=-1):
178 line = self.body.readline(limit)
179 if not line:
180 self.close()
181 return line
182
183 - def readlines(self, hint=None):
184 lines = self.body.readlines(hint)
185 if self.body.close:
186 self.close()
187 return lines
188
189
191
192 charset = "utf8"
193 unicode_errors = 'strict'
194
195 - def __init__(self, connection, request, resp):
196 self.request = request
197 self.connection = connection
198
199 self._resp = resp
200
201
202 self.headers = resp.headers()
203 self.status = resp.status()
204 self.status_int = resp.status_code()
205 self.version = resp.version()
206 self.headerslist = self.headers.items()
207 self.location = self.headers.get('location')
208 self.final_url = request.url
209 self.should_close = not resp.should_keep_alive()
210
211
212 self._closed = False
213 self._already_read = False
214
215 if request.method == "HEAD":
216 """ no body on HEAD, release the connection now """
217 self.connection.release()
218 self._body = StringIO("")
219 else:
220 self._body = resp.body_file()
221
223 try:
224 return getattr(self, key)
225 except AttributeError:
226 pass
227 return self.headers.get(key)
228
231
234
236 return not self._already_read
237
238 - def body_string(self, charset=None, unicode_errors="strict"):
239 """ return body string, by default in bytestring """
240
241 if not self.can_read():
242 raise AlreadyRead()
243
244
245 body = self._body.read()
246 self._already_read = True
247
248
249 self.connection.release(self.should_close)
250
251 if charset is not None:
252 try:
253 body = body.decode(charset, unicode_errors)
254 except UnicodeDecodeError:
255 pass
256 return body
257
258 - def body_stream(self):
259 """ stream body """
260 if not self.can_read():
261 raise AlreadyRead()
262
263 self._already_read = True
264
265 return BodyWrapper(self, self.connection)
266
267
269 """ copy response input to standard output or a file if length >
270 sock.MAX_BODY. This make possible to reuse it in your
271 appplication. When all the input has been read, connection is
272 released """
273 return ResponseTeeInput(self, self.connection,
274 should_close=self.should_close)
275 ClientResponse = Response
276