master
py 623 lines 22.3 KB
Raw
1 # SPDX-License-Identifier: MIT
2 from __future__ import absolute_import
3 from contextlib import contextmanager
4 import zlib
5 import io
6 import logging
7 from socket import timeout as SocketTimeout
8 from socket import error as SocketError
9
10 from ._collections import HTTPHeaderDict
11 from .exceptions import (
12 BodyNotHttplibCompatible, ProtocolError, DecodeError, ReadTimeoutError,
13 ResponseNotChunked, IncompleteRead, InvalidHeader
14 )
15 from .packages.six import string_types as basestring, binary_type, PY3
16 from .packages.six.moves import http_client as httplib
17 from .connection import HTTPException, BaseSSLError
18 from .util.response import is_fp_closed, is_response_to_head
19
20 log = logging.getLogger(__name__)
21
22
23 class DeflateDecoder(object):
24
25 def __init__(self):
26 self._first_try = True
27 self._data = binary_type()
28 self._obj = zlib.decompressobj()
29
30 def __getattr__(self, name):
31 return getattr(self._obj, name)
32
33 def decompress(self, data):
34 if not data:
35 return data
36
37 if not self._first_try:
38 return self._obj.decompress(data)
39
40 self._data += data
41 try:
42 decompressed = self._obj.decompress(data)
43 if decompressed:
44 self._first_try = False
45 self._data = None
46 return decompressed
47 except zlib.error:
48 self._first_try = False
49 self._obj = zlib.decompressobj(-zlib.MAX_WBITS)
50 try:
51 return self.decompress(self._data)
52 finally:
53 self._data = None
54
55
56 class GzipDecoder(object):
57
58 def __init__(self):
59 self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS)
60
61 def __getattr__(self, name):
62 return getattr(self._obj, name)
63
64 def decompress(self, data):
65 if not data:
66 return data
67 return self._obj.decompress(data)
68
69
70 def _get_decoder(mode):
71 if mode == 'gzip':
72 return GzipDecoder()
73
74 return DeflateDecoder()
75
76
77 class HTTPResponse(io.IOBase):
78 """
79 HTTP Response container.
80
81 Backwards-compatible to httplib's HTTPResponse but the response ``body`` is
82 loaded and decoded on-demand when the ``data`` property is accessed. This
83 class is also compatible with the Python standard library's :mod:`io`
84 module, and can hence be treated as a readable object in the context of that
85 framework.
86
87 Extra parameters for behaviour not present in httplib.HTTPResponse:
88
89 :param preload_content:
90 If True, the response's body will be preloaded during construction.
91
92 :param decode_content:
93 If True, attempts to decode specific content-encoding's based on headers
94 (like 'gzip' and 'deflate') will be skipped and raw data will be used
95 instead.
96
97 :param original_response:
98 When this HTTPResponse wrapper is generated from an httplib.HTTPResponse
99 object, it's convenient to include the original for debug purposes. It's
100 otherwise unused.
101
102 :param retries:
103 The retries contains the last :class:`~urllib3.util.retry.Retry` that
104 was used during the request.
105
106 :param enforce_content_length:
107 Enforce content length checking. Body returned by server must match
108 value of Content-Length header, if present. Otherwise, raise error.
109 """
110
111 CONTENT_DECODERS = ['gzip', 'deflate']
112 REDIRECT_STATUSES = [301, 302, 303, 307, 308]
113
114 def __init__(self, body='', headers=None, status=0, version=0, reason=None,
115 strict=0, preload_content=True, decode_content=True,
116 original_response=None, pool=None, connection=None,
117 retries=None, enforce_content_length=False, request_method=None):
118
119 if isinstance(headers, HTTPHeaderDict):
120 self.headers = headers
121 else:
122 self.headers = HTTPHeaderDict(headers)
123 self.status = status
124 self.version = version
125 self.reason = reason
126 self.strict = strict
127 self.decode_content = decode_content
128 self.retries = retries
129 self.enforce_content_length = enforce_content_length
130
131 self._decoder = None
132 self._body = None
133 self._fp = None
134 self._original_response = original_response
135 self._fp_bytes_read = 0
136
137 if body and isinstance(body, (basestring, binary_type)):
138 self._body = body
139
140 self._pool = pool
141 self._connection = connection
142
143 if hasattr(body, 'read'):
144 self._fp = body
145
146 # Are we using the chunked-style of transfer encoding?
147 self.chunked = False
148 self.chunk_left = None
149 tr_enc = self.headers.get('transfer-encoding', '').lower()
150 # Don't incur the penalty of creating a list and then discarding it
151 encodings = (enc.strip() for enc in tr_enc.split(","))
152 if "chunked" in encodings:
153 self.chunked = True
154
155 # Determine length of response
156 self.length_remaining = self._init_length(request_method)
157
158 # If requested, preload the body.
159 if preload_content and not self._body:
160 self._body = self.read(decode_content=decode_content)
161
162 def get_redirect_location(self):
163 """
164 Should we redirect and where to?
165
166 :returns: Truthy redirect location string if we got a redirect status
167 code and valid location. ``None`` if redirect status and no
168 location. ``False`` if not a redirect status code.
169 """
170 if self.status in self.REDIRECT_STATUSES:
171 return self.headers.get('location')
172
173 return False
174
175 def release_conn(self):
176 if not self._pool or not self._connection:
177 return
178
179 self._pool._put_conn(self._connection)
180 self._connection = None
181
182 @property
183 def data(self):
184 # For backwords-compat with earlier urllib3 0.4 and earlier.
185 if self._body:
186 return self._body
187
188 if self._fp:
189 return self.read(cache_content=True)
190
191 @property
192 def connection(self):
193 return self._connection
194
195 def tell(self):
196 """
197 Obtain the number of bytes pulled over the wire so far. May differ from
198 the amount of content returned by :meth:``HTTPResponse.read`` if bytes
199 are encoded on the wire (e.g, compressed).
200 """
201 return self._fp_bytes_read
202
203 def _init_length(self, request_method):
204 """
205 Set initial length value for Response content if available.
206 """
207 length = self.headers.get('content-length')
208
209 if length is not None and self.chunked:
210 # This Response will fail with an IncompleteRead if it can't be
211 # received as chunked. This method falls back to attempt reading
212 # the response before raising an exception.
213 log.warning("Received response with both Content-Length and "
214 "Transfer-Encoding set. This is expressly forbidden "
215 "by RFC 7230 sec 3.3.2. Ignoring Content-Length and "
216 "attempting to process response as Transfer-Encoding: "
217 "chunked.")
218 return None
219
220 elif length is not None:
221 try:
222 # RFC 7230 section 3.3.2 specifies multiple content lengths can
223 # be sent in a single Content-Length header
224 # (e.g. Content-Length: 42, 42). This line ensures the values
225 # are all valid ints and that as long as the `set` length is 1,
226 # all values are the same. Otherwise, the header is invalid.
227 lengths = set([int(val) for val in length.split(',')])
228 if len(lengths) > 1:
229 raise InvalidHeader("Content-Length contained multiple "
230 "unmatching values (%s)" % length)
231 length = lengths.pop()
232 except ValueError:
233 length = None
234 else:
235 if length < 0:
236 length = None
237
238 # Convert status to int for comparison
239 # In some cases, httplib returns a status of "_UNKNOWN"
240 try:
241 status = int(self.status)
242 except ValueError:
243 status = 0
244
245 # Check for responses that shouldn't include a body
246 if status in (204, 304) or 100 <= status < 200 or request_method == 'HEAD':
247 length = 0
248
249 return length
250
251 def _init_decoder(self):
252 """
253 Set-up the _decoder attribute if necessary.
254 """
255 # Note: content-encoding value should be case-insensitive, per RFC 7230
256 # Section 3.2
257 content_encoding = self.headers.get('content-encoding', '').lower()
258 if self._decoder is None and content_encoding in self.CONTENT_DECODERS:
259 self._decoder = _get_decoder(content_encoding)
260
261 def _decode(self, data, decode_content, flush_decoder):
262 """
263 Decode the data passed in and potentially flush the decoder.
264 """
265 try:
266 if decode_content and self._decoder:
267 data = self._decoder.decompress(data)
268 except (IOError, zlib.error) as e:
269 content_encoding = self.headers.get('content-encoding', '').lower()
270 raise DecodeError(
271 "Received response with content-encoding: %s, but "
272 "failed to decode it." % content_encoding, e)
273
274 if flush_decoder and decode_content:
275 data += self._flush_decoder()
276
277 return data
278
279 def _flush_decoder(self):
280 """
281 Flushes the decoder. Should only be called if the decoder is actually
282 being used.
283 """
284 if self._decoder:
285 buf = self._decoder.decompress(b'')
286 return buf + self._decoder.flush()
287
288 return b''
289
290 @contextmanager
291 def _error_catcher(self):
292 """
293 Catch low-level python exceptions, instead re-raising urllib3
294 variants, so that low-level exceptions are not leaked in the
295 high-level api.
296
297 On exit, release the connection back to the pool.
298 """
299 clean_exit = False
300
301 try:
302 try:
303 yield
304
305 except SocketTimeout:
306 # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but
307 # there is yet no clean way to get at it from this context.
308 raise ReadTimeoutError(self._pool, None, 'Read timed out.')
309
310 except BaseSSLError as e:
311 # FIXME: Is there a better way to differentiate between SSLErrors?
312 if 'read operation timed out' not in str(e): # Defensive:
313 # This shouldn't happen but just in case we're missing an edge
314 # case, let's avoid swallowing SSL errors.
315 raise
316
317 raise ReadTimeoutError(self._pool, None, 'Read timed out.')
318
319 except (HTTPException, SocketError) as e:
320 # This includes IncompleteRead.
321 raise ProtocolError('Connection broken: %r' % e, e)
322
323 # If no exception is thrown, we should avoid cleaning up
324 # unnecessarily.
325 clean_exit = True
326 finally:
327 # If we didn't terminate cleanly, we need to throw away our
328 # connection.
329 if not clean_exit:
330 # The response may not be closed but we're not going to use it
331 # anymore so close it now to ensure that the connection is
332 # released back to the pool.
333 if self._original_response:
334 self._original_response.close()
335
336 # Closing the response may not actually be sufficient to close
337 # everything, so if we have a hold of the connection close that
338 # too.
339 if self._connection:
340 self._connection.close()
341
342 # If we hold the original response but it's closed now, we should
343 # return the connection back to the pool.
344 if self._original_response and self._original_response.isclosed():
345 self.release_conn()
346
347 def read(self, amt=None, decode_content=None, cache_content=False):
348 """
349 Similar to :meth:`httplib.HTTPResponse.read`, but with two additional
350 parameters: ``decode_content`` and ``cache_content``.
351
352 :param amt:
353 How much of the content to read. If specified, caching is skipped
354 because it doesn't make sense to cache partial content as the full
355 response.
356
357 :param decode_content:
358 If True, will attempt to decode the body based on the
359 'content-encoding' header.
360
361 :param cache_content:
362 If True, will save the returned data such that the same result is
363 returned despite of the state of the underlying file object. This
364 is useful if you want the ``.data`` property to continue working
365 after having ``.read()`` the file object. (Overridden if ``amt`` is
366 set.)
367 """
368 self._init_decoder()
369 if decode_content is None:
370 decode_content = self.decode_content
371
372 if self._fp is None:
373 return
374
375 flush_decoder = False
376 data = None
377
378 with self._error_catcher():
379 if amt is None:
380 # cStringIO doesn't like amt=None
381 data = self._fp.read()
382 flush_decoder = True
383 else:
384 cache_content = False
385 data = self._fp.read(amt)
386 if amt != 0 and not data: # Platform-specific: Buggy versions of Python.
387 # Close the connection when no data is returned
388 #
389 # This is redundant to what httplib/http.client _should_
390 # already do. However, versions of python released before
391 # December 15, 2012 (http://bugs.python.org/issue16298) do
392 # not properly close the connection in all cases. There is
393 # no harm in redundantly calling close.
394 self._fp.close()
395 flush_decoder = True
396 if self.enforce_content_length and self.length_remaining not in (0, None):
397 # This is an edge case that httplib failed to cover due
398 # to concerns of backward compatibility. We're
399 # addressing it here to make sure IncompleteRead is
400 # raised during streaming, so all calls with incorrect
401 # Content-Length are caught.
402 raise IncompleteRead(self._fp_bytes_read, self.length_remaining)
403
404 if data:
405 self._fp_bytes_read += len(data)
406 if self.length_remaining is not None:
407 self.length_remaining -= len(data)
408
409 data = self._decode(data, decode_content, flush_decoder)
410
411 if cache_content:
412 self._body = data
413
414 return data
415
416 def stream(self, amt=2**16, decode_content=None):
417 """
418 A generator wrapper for the read() method. A call will block until
419 ``amt`` bytes have been read from the connection or until the
420 connection is closed.
421
422 :param amt:
423 How much of the content to read. The generator will return up to
424 much data per iteration, but may return less. This is particularly
425 likely when using compressed data. However, the empty string will
426 never be returned.
427
428 :param decode_content:
429 If True, will attempt to decode the body based on the
430 'content-encoding' header.
431 """
432 if self.chunked and self.supports_chunked_reads():
433 for line in self.read_chunked(amt, decode_content=decode_content):
434 yield line
435 else:
436 while not is_fp_closed(self._fp):
437 data = self.read(amt=amt, decode_content=decode_content)
438
439 if data:
440 yield data
441
442 @classmethod
443 def from_httplib(ResponseCls, r, **response_kw):
444 """
445 Given an :class:`httplib.HTTPResponse` instance ``r``, return a
446 corresponding :class:`urllib3.response.HTTPResponse` object.
447
448 Remaining parameters are passed to the HTTPResponse constructor, along
449 with ``original_response=r``.
450 """
451 headers = r.msg
452
453 if not isinstance(headers, HTTPHeaderDict):
454 if PY3: # Python 3
455 headers = HTTPHeaderDict(headers.items())
456 else: # Python 2
457 headers = HTTPHeaderDict.from_httplib(headers)
458
459 # HTTPResponse objects in Python 3 don't have a .strict attribute
460 strict = getattr(r, 'strict', 0)
461 resp = ResponseCls(body=r,
462 headers=headers,
463 status=r.status,
464 version=r.version,
465 reason=r.reason,
466 strict=strict,
467 original_response=r,
468 **response_kw)
469 return resp
470
471 # Backwards-compatibility methods for httplib.HTTPResponse
472 def getheaders(self):
473 return self.headers
474
475 def getheader(self, name, default=None):
476 return self.headers.get(name, default)
477
478 # Overrides from io.IOBase
479 def close(self):
480 if not self.closed:
481 self._fp.close()
482
483 if self._connection:
484 self._connection.close()
485
486 @property
487 def closed(self):
488 if self._fp is None:
489 return True
490 elif hasattr(self._fp, 'isclosed'):
491 return self._fp.isclosed()
492 elif hasattr(self._fp, 'closed'):
493 return self._fp.closed
494 else:
495 return True
496
497 def fileno(self):
498 if self._fp is None:
499 raise IOError("HTTPResponse has no file to get a fileno from")
500 elif hasattr(self._fp, "fileno"):
501 return self._fp.fileno()
502 else:
503 raise IOError("The file-like object this HTTPResponse is wrapped "
504 "around has no file descriptor")
505
506 def flush(self):
507 if self._fp is not None and hasattr(self._fp, 'flush'):
508 return self._fp.flush()
509
510 def readable(self):
511 # This method is required for `io` module compatibility.
512 return True
513
514 def readinto(self, b):
515 # This method is required for `io` module compatibility.
516 temp = self.read(len(b))
517 if len(temp) == 0:
518 return 0
519 else:
520 b[:len(temp)] = temp
521 return len(temp)
522
523 def supports_chunked_reads(self):
524 """
525 Checks if the underlying file-like object looks like a
526 httplib.HTTPResponse object. We do this by testing for the fp
527 attribute. If it is present we assume it returns raw chunks as
528 processed by read_chunked().
529 """
530 return hasattr(self._fp, 'fp')
531
532 def _update_chunk_length(self):
533 # First, we'll figure out length of a chunk and then
534 # we'll try to read it from socket.
535 if self.chunk_left is not None:
536 return
537 line = self._fp.fp.readline()
538 line = line.split(b';', 1)[0]
539 try:
540 self.chunk_left = int(line, 16)
541 except ValueError:
542 # Invalid chunked protocol response, abort.
543 self.close()
544 raise httplib.IncompleteRead(line)
545
546 def _handle_chunk(self, amt):
547 returned_chunk = None
548 if amt is None:
549 chunk = self._fp._safe_read(self.chunk_left)
550 returned_chunk = chunk
551 self._fp._safe_read(2) # Toss the CRLF at the end of the chunk.
552 self.chunk_left = None
553 elif amt < self.chunk_left:
554 value = self._fp._safe_read(amt)
555 self.chunk_left = self.chunk_left - amt
556 returned_chunk = value
557 elif amt == self.chunk_left:
558 value = self._fp._safe_read(amt)
559 self._fp._safe_read(2) # Toss the CRLF at the end of the chunk.
560 self.chunk_left = None
561 returned_chunk = value
562 else: # amt > self.chunk_left
563 returned_chunk = self._fp._safe_read(self.chunk_left)
564 self._fp._safe_read(2) # Toss the CRLF at the end of the chunk.
565 self.chunk_left = None
566 return returned_chunk
567
568 def read_chunked(self, amt=None, decode_content=None):
569 """
570 Similar to :meth:`HTTPResponse.read`, but with an additional
571 parameter: ``decode_content``.
572
573 :param decode_content:
574 If True, will attempt to decode the body based on the
575 'content-encoding' header.
576 """
577 self._init_decoder()
578 # FIXME: Rewrite this method and make it a class with a better structured logic.
579 if not self.chunked:
580 raise ResponseNotChunked(
581 "Response is not chunked. "
582 "Header 'transfer-encoding: chunked' is missing.")
583 if not self.supports_chunked_reads():
584 raise BodyNotHttplibCompatible(
585 "Body should be httplib.HTTPResponse like. "
586 "It should have have an fp attribute which returns raw chunks.")
587
588 # Don't bother reading the body of a HEAD request.
589 if self._original_response and is_response_to_head(self._original_response):
590 self._original_response.close()
591 return
592
593 with self._error_catcher():
594 while True:
595 self._update_chunk_length()
596 if self.chunk_left == 0:
597 break
598 chunk = self._handle_chunk(amt)
599 decoded = self._decode(chunk, decode_content=decode_content,
600 flush_decoder=False)
601 if decoded:
602 yield decoded
603
604 if decode_content:
605 # On CPython and PyPy, we should never need to flush the
606 # decoder. However, on Jython we *might* need to, so
607 # lets defensively do it anyway.
608 decoded = self._flush_decoder()
609 if decoded: # Platform-specific: Jython.
610 yield decoded
611
612 # Chunk content ends with \r\n: discard it.
613 while True:
614 line = self._fp.fp.readline()
615 if not line:
616 # Some sites may not end with '\r\n'.
617 break
618 if line == b'\r\n':
619 break
620
621 # We read everything; close the "file".
622 if self._original_response:
623 self._original_response.close()