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 .datastructures import MultiDict
16 from .errors import AlreadyRead, RequestError
17 from .forms import multipart_form_encode, form_encode
18 from .tee import ResponseTeeInput
19 from .util import to_bytestring
20
22
23 - def __init__(self, url, method='GET', body=None, headers=None):
36
38 if not isinstance(self._headers, MultiDict):
39 self._headers = MultiDict(self._headers or [])
40 return self._headers
43 headers = property(_headers__get, _headers__set, doc=_headers__get.__doc__)
44
46 if self.url is None:
47 raise ValueError("url isn't set")
48 return urlparse.urlparse(self.url)
49 parsed_url = property(_parsed_url, doc="parsed url")
50
57 path = property(_path__get)
58
60 try:
61 h = self.parsed_url.netloc.encode('ascii')
62 except UnicodeEncodeError:
63 h = self.parsed_url.netloc.encode('idna')
64
65 hdr_host = self.headers.iget("host")
66 if not hdr_host:
67 return h
68 return hdr_host
69 host = property(_host__get)
70
72 te = self.headers.iget("transfer-encoding")
73 return (te is not None and te.lower() == "chunked")
74
77
78 - def _set_body(self, body):
79 ctype = self.headers.ipop('content-type', None)
80 clen = self.headers.ipop('content-length', None)
81
82 if isinstance(body, dict):
83 if ctype is not None and \
84 ctype.startswith("multipart/form-data"):
85 type_, opts = cgi.parse_header(ctype)
86 boundary = opts.get('boundary', uuid.uuid4().hex)
87 self._body, self.headers = multipart_form_encode(body,
88 self.headers, boundary)
89 else:
90 ctype = "application/x-www-form-urlencoded; charset=utf-8"
91 self._body = form_encode(body)
92 elif hasattr(body, "boundary"):
93 ctype = "multipart/form-data; boundary=%s" % self.body.boundary
94 clen = body.get_size()
95 self._body = body
96 else:
97 self._body = body
98
99 if not ctype:
100 ctype = 'application/octet-stream'
101 if hasattr(self.body, 'name'):
102 ctype = mimetypes.guess_type(body.name)[0]
103
104 if not clen:
105 if hasattr(self._body, 'fileno'):
106 try:
107 self._body.flush()
108 except IOError:
109 pass
110 try:
111 fno = self._body.fileno()
112 clen = str(os.fstat(fno)[6])
113 except IOError:
114 if not self.is_chunked():
115 clen = len(self._body.read())
116 elif hasattr(self._body, 'getvalue') and not \
117 self.is_chunked():
118 clen = len(self._body.getvalue())
119 elif isinstance(self._body, types.StringTypes):
120 self._body = to_bytestring(self._body)
121 clen = len(self._body)
122
123 if clen is not None:
124 self.headers['Content-Length'] = clen
125
126 if ctype is not None:
127 self.headers['Content-Type'] = ctype
128
129 - def _get_body(self):
131 body = property(_get_body, _set_body, doc="request body")
132
133
134 -class BodyWrapper(object):
135
136 - def __init__(self, resp, connection):
137 self.resp = resp
138 self.body = resp._body
139 self.connection = connection
140
141 - def __enter__(self):
143
144 - def __exit__(self, exc_type, exc_val, traceback):
146
148 """ release connection """
149 self.connection.release(self.resp.should_close)
150
151 - def __iter__(self):
153
155 try:
156 return self.body.next()
157 except StopIteration:
158 self.close()
159 raise
160
161 - def read(self, size=None):
162 data = self.body.read(size=size)
163 if not data:
164 self.close()
165 return data
166
167 - def readline(self, size=None):
168 line = self.body.readline(size=size)
169 if not line:
170 self.close()
171 return line
172
173 - def readlines(self, size=None):
174 lines = self.body.readlines(size=size)
175 if self.body.close:
176 self.close()
177 return lines
178
179
181
182 charset = "utf8"
183 unicode_errors = 'strict'
184
185 - def __init__(self, connection, request, resp):
208
210 try:
211 return getattr(self, key)
212 except AttributeError:
213 pass
214 return self.headers.iget(key)
215
218
221
223 return not self._already_read
224
225 - def body_string(self, charset=None, unicode_errors="strict"):
226 """ return body string, by default in bytestring """
227
228 if not self.can_read():
229 raise AlreadyRead()
230
231 body = self._body.read()
232 self._already_read = True
233
234
235 self.connection.release(self.should_close)
236
237 if charset is not None:
238 try:
239 body = body.decode(charset, unicode_errors)
240 except UnicodeDecodeError:
241 pass
242 return body
243
244 - def body_stream(self):
245 """ stream body """
246 if not self.can_read():
247 raise AlreadyRead()
248
249 self._already_read = True
250
251 return BodyWrapper(self, self.connection)
252
254 """ copy response input to standard output or a file if length >
255 sock.MAX_BODY. This make possible to reuse it in your
256 appplication. When all the input has been read, connection is
257 released """
258 if not hasattr(self._body, "reader"):
259
260 return self._body
261
262 return ResponseTeeInput(self, self.connection,
263 should_close=self.should_close)
264 ClientResponse = Response
265