OLD | NEW |
1 # urllib3/response.py | 1 # urllib3/response.py |
2 # Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt) | 2 # Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) |
3 # | 3 # |
4 # This module is part of urllib3 and is released under | 4 # This module is part of urllib3 and is released under |
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php | 5 # the MIT License: http://www.opensource.org/licenses/mit-license.php |
6 | 6 |
7 | 7 |
8 import logging | 8 import logging |
9 import zlib | 9 import zlib |
| 10 import io |
10 | 11 |
11 from .exceptions import DecodeError | 12 from .exceptions import DecodeError |
12 from .packages.six import string_types as basestring, binary_type | 13 from .packages.six import string_types as basestring, binary_type |
| 14 from .util import is_fp_closed |
13 | 15 |
14 | 16 |
15 log = logging.getLogger(__name__) | 17 log = logging.getLogger(__name__) |
16 | 18 |
17 | 19 |
18 class DeflateDecoder(object): | 20 class DeflateDecoder(object): |
19 | 21 |
20 def __init__(self): | 22 def __init__(self): |
21 self._first_try = True | 23 self._first_try = True |
22 self._data = binary_type() | 24 self._data = binary_type() |
(...skipping 18 matching lines...) Expand all Loading... |
41 self._data = None | 43 self._data = None |
42 | 44 |
43 | 45 |
44 def _get_decoder(mode): | 46 def _get_decoder(mode): |
45 if mode == 'gzip': | 47 if mode == 'gzip': |
46 return zlib.decompressobj(16 + zlib.MAX_WBITS) | 48 return zlib.decompressobj(16 + zlib.MAX_WBITS) |
47 | 49 |
48 return DeflateDecoder() | 50 return DeflateDecoder() |
49 | 51 |
50 | 52 |
51 class HTTPResponse(object): | 53 class HTTPResponse(io.IOBase): |
52 """ | 54 """ |
53 HTTP Response container. | 55 HTTP Response container. |
54 | 56 |
55 Backwards-compatible to httplib's HTTPResponse but the response ``body`` is | 57 Backwards-compatible to httplib's HTTPResponse but the response ``body`` is |
56 loaded and decoded on-demand when the ``data`` property is accessed. | 58 loaded and decoded on-demand when the ``data`` property is accessed. |
57 | 59 |
58 Extra parameters for behaviour not present in httplib.HTTPResponse: | 60 Extra parameters for behaviour not present in httplib.HTTPResponse: |
59 | 61 |
60 :param preload_content: | 62 :param preload_content: |
61 If True, the response's body will be preloaded during construction. | 63 If True, the response's body will be preloaded during construction. |
62 | 64 |
63 :param decode_content: | 65 :param decode_content: |
64 If True, attempts to decode specific content-encoding's based on headers | 66 If True, attempts to decode specific content-encoding's based on headers |
65 (like 'gzip' and 'deflate') will be skipped and raw data will be used | 67 (like 'gzip' and 'deflate') will be skipped and raw data will be used |
66 instead. | 68 instead. |
67 | 69 |
68 :param original_response: | 70 :param original_response: |
69 When this HTTPResponse wrapper is generated from an httplib.HTTPResponse | 71 When this HTTPResponse wrapper is generated from an httplib.HTTPResponse |
70 object, it's convenient to include the original for debug purposes. It's | 72 object, it's convenient to include the original for debug purposes. It's |
71 otherwise unused. | 73 otherwise unused. |
72 """ | 74 """ |
73 | 75 |
74 CONTENT_DECODERS = ['gzip', 'deflate'] | 76 CONTENT_DECODERS = ['gzip', 'deflate'] |
| 77 REDIRECT_STATUSES = [301, 302, 303, 307, 308] |
75 | 78 |
76 def __init__(self, body='', headers=None, status=0, version=0, reason=None, | 79 def __init__(self, body='', headers=None, status=0, version=0, reason=None, |
77 strict=0, preload_content=True, decode_content=True, | 80 strict=0, preload_content=True, decode_content=True, |
78 original_response=None, pool=None, connection=None): | 81 original_response=None, pool=None, connection=None): |
79 self.headers = headers or {} | 82 self.headers = headers or {} |
80 self.status = status | 83 self.status = status |
81 self.version = version | 84 self.version = version |
82 self.reason = reason | 85 self.reason = reason |
83 self.strict = strict | 86 self.strict = strict |
84 self.decode_content = decode_content | 87 self.decode_content = decode_content |
(...skipping 13 matching lines...) Expand all Loading... |
98 self._body = self.read(decode_content=decode_content) | 101 self._body = self.read(decode_content=decode_content) |
99 | 102 |
100 def get_redirect_location(self): | 103 def get_redirect_location(self): |
101 """ | 104 """ |
102 Should we redirect and where to? | 105 Should we redirect and where to? |
103 | 106 |
104 :returns: Truthy redirect location string if we got a redirect status | 107 :returns: Truthy redirect location string if we got a redirect status |
105 code and valid location. ``None`` if redirect status and no | 108 code and valid location. ``None`` if redirect status and no |
106 location. ``False`` if not a redirect status code. | 109 location. ``False`` if not a redirect status code. |
107 """ | 110 """ |
108 if self.status in [301, 302, 303, 307]: | 111 if self.status in self.REDIRECT_STATUSES: |
109 return self.headers.get('location') | 112 return self.headers.get('location') |
110 | 113 |
111 return False | 114 return False |
112 | 115 |
113 def release_conn(self): | 116 def release_conn(self): |
114 if not self._pool or not self._connection: | 117 if not self._pool or not self._connection: |
115 return | 118 return |
116 | 119 |
117 self._pool._put_conn(self._connection) | 120 self._pool._put_conn(self._connection) |
118 self._connection = None | 121 self._connection = None |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 # already do. However, versions of python released before | 179 # already do. However, versions of python released before |
177 # December 15, 2012 (http://bugs.python.org/issue16298) do n
ot | 180 # December 15, 2012 (http://bugs.python.org/issue16298) do n
ot |
178 # properly close the connection in all cases. There is no ha
rm | 181 # properly close the connection in all cases. There is no ha
rm |
179 # in redundantly calling close. | 182 # in redundantly calling close. |
180 self._fp.close() | 183 self._fp.close() |
181 flush_decoder = True | 184 flush_decoder = True |
182 | 185 |
183 try: | 186 try: |
184 if decode_content and self._decoder: | 187 if decode_content and self._decoder: |
185 data = self._decoder.decompress(data) | 188 data = self._decoder.decompress(data) |
186 except (IOError, zlib.error): | 189 except (IOError, zlib.error) as e: |
187 raise DecodeError("Received response with content-encoding: %s,
but " | 190 raise DecodeError( |
188 "failed to decode it." % content_encoding) | 191 "Received response with content-encoding: %s, but " |
| 192 "failed to decode it." % content_encoding, |
| 193 e) |
189 | 194 |
190 if flush_decoder and self._decoder: | 195 if flush_decoder and decode_content and self._decoder: |
191 buf = self._decoder.decompress(binary_type()) | 196 buf = self._decoder.decompress(binary_type()) |
192 data += buf + self._decoder.flush() | 197 data += buf + self._decoder.flush() |
193 | 198 |
194 if cache_content: | 199 if cache_content: |
195 self._body = data | 200 self._body = data |
196 | 201 |
197 return data | 202 return data |
198 | 203 |
199 finally: | 204 finally: |
200 if self._original_response and self._original_response.isclosed(): | 205 if self._original_response and self._original_response.isclosed(): |
201 self.release_conn() | 206 self.release_conn() |
202 | 207 |
| 208 def stream(self, amt=2**16, decode_content=None): |
| 209 """ |
| 210 A generator wrapper for the read() method. A call will block until |
| 211 ``amt`` bytes have been read from the connection or until the |
| 212 connection is closed. |
| 213 |
| 214 :param amt: |
| 215 How much of the content to read. The generator will return up to |
| 216 much data per iteration, but may return less. This is particularly |
| 217 likely when using compressed data. However, the empty string will |
| 218 never be returned. |
| 219 |
| 220 :param decode_content: |
| 221 If True, will attempt to decode the body based on the |
| 222 'content-encoding' header. |
| 223 """ |
| 224 while not is_fp_closed(self._fp): |
| 225 data = self.read(amt=amt, decode_content=decode_content) |
| 226 |
| 227 if data: |
| 228 yield data |
| 229 |
| 230 |
203 @classmethod | 231 @classmethod |
204 def from_httplib(ResponseCls, r, **response_kw): | 232 def from_httplib(ResponseCls, r, **response_kw): |
205 """ | 233 """ |
206 Given an :class:`httplib.HTTPResponse` instance ``r``, return a | 234 Given an :class:`httplib.HTTPResponse` instance ``r``, return a |
207 corresponding :class:`urllib3.response.HTTPResponse` object. | 235 corresponding :class:`urllib3.response.HTTPResponse` object. |
208 | 236 |
209 Remaining parameters are passed to the HTTPResponse constructor, along | 237 Remaining parameters are passed to the HTTPResponse constructor, along |
210 with ``original_response=r``. | 238 with ``original_response=r``. |
211 """ | 239 """ |
212 | 240 |
(...skipping 19 matching lines...) Expand all Loading... |
232 strict=strict, | 260 strict=strict, |
233 original_response=r, | 261 original_response=r, |
234 **response_kw) | 262 **response_kw) |
235 | 263 |
236 # Backwards-compatibility methods for httplib.HTTPResponse | 264 # Backwards-compatibility methods for httplib.HTTPResponse |
237 def getheaders(self): | 265 def getheaders(self): |
238 return self.headers | 266 return self.headers |
239 | 267 |
240 def getheader(self, name, default=None): | 268 def getheader(self, name, default=None): |
241 return self.headers.get(name, default) | 269 return self.headers.get(name, default) |
| 270 |
| 271 # Overrides from io.IOBase |
| 272 def close(self): |
| 273 if not self.closed: |
| 274 self._fp.close() |
| 275 |
| 276 @property |
| 277 def closed(self): |
| 278 if self._fp is None: |
| 279 return True |
| 280 elif hasattr(self._fp, 'closed'): |
| 281 return self._fp.closed |
| 282 elif hasattr(self._fp, 'isclosed'): # Python 2 |
| 283 return self._fp.isclosed() |
| 284 else: |
| 285 return True |
| 286 |
| 287 def fileno(self): |
| 288 if self._fp is None: |
| 289 raise IOError("HTTPResponse has no file to get a fileno from") |
| 290 elif hasattr(self._fp, "fileno"): |
| 291 return self._fp.fileno() |
| 292 else: |
| 293 raise IOError("The file-like object this HTTPResponse is wrapped " |
| 294 "around has no file descriptor") |
| 295 |
| 296 def flush(self): |
| 297 if self._fp is not None and hasattr(self._fp, 'flush'): |
| 298 return self._fp.flush() |
| 299 |
| 300 def readable(self): |
| 301 return True |
OLD | NEW |