1# -*- coding: utf-8 -*- 2 3""" 4requests.models 5~~~~~~~~~~~~~~~ 6 7This module contains the primary objects that power Requests. 8""" 9 10import collections 11import datetime 12 13from io import BytesIO, UnsupportedOperation 14from .hooks import default_hooks 15from .structures import CaseInsensitiveDict 16 17from .auth import HTTPBasicAuth 18from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar 19from .packages.urllib3.fields import RequestField 20from .packages.urllib3.filepost import encode_multipart_formdata 21from .packages.urllib3.util import parse_url 22from .packages.urllib3.exceptions import ( 23 DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) 24from .exceptions import ( 25 HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, 26 ContentDecodingError, ConnectionError, StreamConsumedError) 27from .utils import ( 28 guess_filename, get_auth_from_url, requote_uri, 29 stream_decode_response_unicode, to_key_val_list, parse_header_links, 30 iter_slices, guess_json_utf, super_len, to_native_string) 31from .compat import ( 32 cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO, 33 is_py2, chardet, builtin_str, basestring) 34from .compat import json as complexjson 35from .status_codes import codes 36 37#: The set of HTTP status codes that indicate an automatically 38#: processable redirect. 39REDIRECT_STATI = ( 40 codes.moved, # 301 41 codes.found, # 302 42 codes.other, # 303 43 codes.temporary_redirect, # 307 44 codes.permanent_redirect, # 308 45) 46 47DEFAULT_REDIRECT_LIMIT = 30 48CONTENT_CHUNK_SIZE = 10 * 1024 49ITER_CHUNK_SIZE = 512 50 51 52class RequestEncodingMixin(object): 53 @property 54 def path_url(self): 55 """Build the path URL to use.""" 56 57 url = [] 58 59 p = urlsplit(self.url) 60 61 path = p.path 62 if not path: 63 path = '/' 64 65 url.append(path) 66 67 query = p.query 68 if query: 69 url.append('?') 70 url.append(query) 71 72 return ''.join(url) 73 74 @staticmethod 75 def _encode_params(data): 76 """Encode parameters in a piece of data. 77 78 Will successfully encode parameters when passed as a dict or a list of 79 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary 80 if parameters are supplied as a dict. 81 """ 82 83 if isinstance(data, (str, bytes)): 84 return data 85 elif hasattr(data, 'read'): 86 return data 87 elif hasattr(data, '__iter__'): 88 result = [] 89 for k, vs in to_key_val_list(data): 90 if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): 91 vs = [vs] 92 for v in vs: 93 if v is not None: 94 result.append( 95 (k.encode('utf-8') if isinstance(k, str) else k, 96 v.encode('utf-8') if isinstance(v, str) else v)) 97 return urlencode(result, doseq=True) 98 else: 99 return data 100 101 @staticmethod 102 def _encode_files(files, data): 103 """Build the body for a multipart/form-data request. 104 105 Will successfully encode files when passed as a dict or a list of 106 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary 107 if parameters are supplied as a dict. 108 109 """ 110 if (not files): 111 raise ValueError("Files must be provided.") 112 elif isinstance(data, basestring): 113 raise ValueError("Data must not be a string.") 114 115 new_fields = [] 116 fields = to_key_val_list(data or {}) 117 files = to_key_val_list(files or {}) 118 119 for field, val in fields: 120 if isinstance(val, basestring) or not hasattr(val, '__iter__'): 121 val = [val] 122 for v in val: 123 if v is not None: 124 # Don't call str() on bytestrings: in Py3 it all goes wrong. 125 if not isinstance(v, bytes): 126 v = str(v) 127 128 new_fields.append( 129 (field.decode('utf-8') if isinstance(field, bytes) else field, 130 v.encode('utf-8') if isinstance(v, str) else v)) 131 132 for (k, v) in files: 133 # support for explicit filename 134 ft = None 135 fh = None 136 if isinstance(v, (tuple, list)): 137 if len(v) == 2: 138 fn, fp = v 139 elif len(v) == 3: 140 fn, fp, ft = v 141 else: 142 fn, fp, ft, fh = v 143 else: 144 fn = guess_filename(v) or k 145 fp = v 146 147 if isinstance(fp, (str, bytes, bytearray)): 148 fdata = fp 149 else: 150 fdata = fp.read() 151 152 rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) 153 rf.make_multipart(content_type=ft) 154 new_fields.append(rf) 155 156 body, content_type = encode_multipart_formdata(new_fields) 157 158 return body, content_type 159 160 161class RequestHooksMixin(object): 162 def register_hook(self, event, hook): 163 """Properly register a hook.""" 164 165 if event not in self.hooks: 166 raise ValueError('Unsupported event specified, with event name "%s"' % (event)) 167 168 if isinstance(hook, collections.Callable): 169 self.hooks[event].append(hook) 170 elif hasattr(hook, '__iter__'): 171 self.hooks[event].extend(h for h in hook if isinstance(h, collections.Callable)) 172 173 def deregister_hook(self, event, hook): 174 """Deregister a previously registered hook. 175 Returns True if the hook existed, False if not. 176 """ 177 178 try: 179 self.hooks[event].remove(hook) 180 return True 181 except ValueError: 182 return False 183 184 185class Request(RequestHooksMixin): 186 """A user-created :class:`Request <Request>` object. 187 188 Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server. 189 190 :param method: HTTP method to use. 191 :param url: URL to send. 192 :param headers: dictionary of headers to send. 193 :param files: dictionary of {filename: fileobject} files to multipart upload. 194 :param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place. 195 :param json: json for the body to attach to the request (if files or data is not specified). 196 :param params: dictionary of URL parameters to append to the URL. 197 :param auth: Auth handler or (user, pass) tuple. 198 :param cookies: dictionary or CookieJar of cookies to attach to this request. 199 :param hooks: dictionary of callback hooks, for internal usage. 200 201 Usage:: 202 203 >>> import requests 204 >>> req = requests.Request('GET', 'http://httpbin.org/get') 205 >>> req.prepare() 206 <PreparedRequest [GET]> 207 208 """ 209 def __init__(self, method=None, url=None, headers=None, files=None, 210 data=None, params=None, auth=None, cookies=None, hooks=None, json=None): 211 212 # Default empty dicts for dict params. 213 data = [] if data is None else data 214 files = [] if files is None else files 215 headers = {} if headers is None else headers 216 params = {} if params is None else params 217 hooks = {} if hooks is None else hooks 218 219 self.hooks = default_hooks() 220 for (k, v) in list(hooks.items()): 221 self.register_hook(event=k, hook=v) 222 223 self.method = method 224 self.url = url 225 self.headers = headers 226 self.files = files 227 self.data = data 228 self.json = json 229 self.params = params 230 self.auth = auth 231 self.cookies = cookies 232 233 def __repr__(self): 234 return '<Request [%s]>' % (self.method) 235 236 def prepare(self): 237 """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it.""" 238 p = PreparedRequest() 239 p.prepare( 240 method=self.method, 241 url=self.url, 242 headers=self.headers, 243 files=self.files, 244 data=self.data, 245 json=self.json, 246 params=self.params, 247 auth=self.auth, 248 cookies=self.cookies, 249 hooks=self.hooks, 250 ) 251 return p 252 253 254class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): 255 """The fully mutable :class:`PreparedRequest <PreparedRequest>` object, 256 containing the exact bytes that will be sent to the server. 257 258 Generated from either a :class:`Request <Request>` object or manually. 259 260 Usage:: 261 262 >>> import requests 263 >>> req = requests.Request('GET', 'http://httpbin.org/get') 264 >>> r = req.prepare() 265 <PreparedRequest [GET]> 266 267 >>> s = requests.Session() 268 >>> s.send(r) 269 <Response [200]> 270 271 """ 272 273 def __init__(self): 274 #: HTTP verb to send to the server. 275 self.method = None 276 #: HTTP URL to send the request to. 277 self.url = None 278 #: dictionary of HTTP headers. 279 self.headers = None 280 # The `CookieJar` used to create the Cookie header will be stored here 281 # after prepare_cookies is called 282 self._cookies = None 283 #: request body to send to the server. 284 self.body = None 285 #: dictionary of callback hooks, for internal usage. 286 self.hooks = default_hooks() 287 288 def prepare(self, method=None, url=None, headers=None, files=None, 289 data=None, params=None, auth=None, cookies=None, hooks=None, json=None): 290 """Prepares the entire request with the given parameters.""" 291 292 self.prepare_method(method) 293 self.prepare_url(url, params) 294 self.prepare_headers(headers) 295 self.prepare_cookies(cookies) 296 self.prepare_body(data, files, json) 297 self.prepare_auth(auth, url) 298 299 # Note that prepare_auth must be last to enable authentication schemes 300 # such as OAuth to work on a fully prepared request. 301 302 # This MUST go after prepare_auth. Authenticators could add a hook 303 self.prepare_hooks(hooks) 304 305 def __repr__(self): 306 return '<PreparedRequest [%s]>' % (self.method) 307 308 def copy(self): 309 p = PreparedRequest() 310 p.method = self.method 311 p.url = self.url 312 p.headers = self.headers.copy() if self.headers is not None else None 313 p._cookies = _copy_cookie_jar(self._cookies) 314 p.body = self.body 315 p.hooks = self.hooks 316 return p 317 318 def prepare_method(self, method): 319 """Prepares the given HTTP method.""" 320 self.method = method 321 if self.method is not None: 322 self.method = to_native_string(self.method.upper()) 323 324 def prepare_url(self, url, params): 325 """Prepares the given HTTP URL.""" 326 #: Accept objects that have string representations. 327 #: We're unable to blindly call unicode/str functions 328 #: as this will include the bytestring indicator (b'') 329 #: on python 3.x. 330 #: https://github.com/kennethreitz/requests/pull/2238 331 if isinstance(url, bytes): 332 url = url.decode('utf8') 333 else: 334 url = unicode(url) if is_py2 else str(url) 335 336 # Don't do any URL preparation for non-HTTP schemes like `mailto`, 337 # `data` etc to work around exceptions from `url_parse`, which 338 # handles RFC 3986 only. 339 if ':' in url and not url.lower().startswith('http'): 340 self.url = url 341 return 342 343 # Support for unicode domain names and paths. 344 try: 345 scheme, auth, host, port, path, query, fragment = parse_url(url) 346 except LocationParseError as e: 347 raise InvalidURL(*e.args) 348 349 if not scheme: 350 error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?") 351 error = error.format(to_native_string(url, 'utf8')) 352 353 raise MissingSchema(error) 354 355 if not host: 356 raise InvalidURL("Invalid URL %r: No host supplied" % url) 357 358 # Only want to apply IDNA to the hostname 359 try: 360 host = host.encode('idna').decode('utf-8') 361 except UnicodeError: 362 raise InvalidURL('URL has an invalid label.') 363 364 # Carefully reconstruct the network location 365 netloc = auth or '' 366 if netloc: 367 netloc += '@' 368 netloc += host 369 if port: 370 netloc += ':' + str(port) 371 372 # Bare domains aren't valid URLs. 373 if not path: 374 path = '/' 375 376 if is_py2: 377 if isinstance(scheme, str): 378 scheme = scheme.encode('utf-8') 379 if isinstance(netloc, str): 380 netloc = netloc.encode('utf-8') 381 if isinstance(path, str): 382 path = path.encode('utf-8') 383 if isinstance(query, str): 384 query = query.encode('utf-8') 385 if isinstance(fragment, str): 386 fragment = fragment.encode('utf-8') 387 388 if isinstance(params, (str, bytes)): 389 params = to_native_string(params) 390 391 enc_params = self._encode_params(params) 392 if enc_params: 393 if query: 394 query = '%s&%s' % (query, enc_params) 395 else: 396 query = enc_params 397 398 url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) 399 self.url = url 400 401 def prepare_headers(self, headers): 402 """Prepares the given HTTP headers.""" 403 404 if headers: 405 self.headers = CaseInsensitiveDict((to_native_string(name), value) for name, value in headers.items()) 406 else: 407 self.headers = CaseInsensitiveDict() 408 409 def prepare_body(self, data, files, json=None): 410 """Prepares the given HTTP body data.""" 411 412 # Check if file, fo, generator, iterator. 413 # If not, run through normal process. 414 415 # Nottin' on you. 416 body = None 417 content_type = None 418 length = None 419 420 if not data and json is not None: 421 content_type = 'application/json' 422 body = complexjson.dumps(json) 423 424 is_stream = all([ 425 hasattr(data, '__iter__'), 426 not isinstance(data, (basestring, list, tuple, dict)) 427 ]) 428 429 try: 430 length = super_len(data) 431 except (TypeError, AttributeError, UnsupportedOperation): 432 length = None 433 434 if is_stream: 435 body = data 436 437 if files: 438 raise NotImplementedError('Streamed bodies and files are mutually exclusive.') 439 440 if length: 441 self.headers['Content-Length'] = builtin_str(length) 442 else: 443 self.headers['Transfer-Encoding'] = 'chunked' 444 else: 445 # Multi-part file uploads. 446 if files: 447 (body, content_type) = self._encode_files(files, data) 448 else: 449 if data: 450 body = self._encode_params(data) 451 if isinstance(data, basestring) or hasattr(data, 'read'): 452 content_type = None 453 else: 454 content_type = 'application/x-www-form-urlencoded' 455 456 self.prepare_content_length(body) 457 458 # Add content-type if it wasn't explicitly provided. 459 if content_type and ('content-type' not in self.headers): 460 self.headers['Content-Type'] = content_type 461 462 self.body = body 463 464 def prepare_content_length(self, body): 465 if hasattr(body, 'seek') and hasattr(body, 'tell'): 466 body.seek(0, 2) 467 self.headers['Content-Length'] = builtin_str(body.tell()) 468 body.seek(0, 0) 469 elif body is not None: 470 l = super_len(body) 471 if l: 472 self.headers['Content-Length'] = builtin_str(l) 473 elif (self.method not in ('GET', 'HEAD')) and (self.headers.get('Content-Length') is None): 474 self.headers['Content-Length'] = '0' 475 476 def prepare_auth(self, auth, url=''): 477 """Prepares the given HTTP auth data.""" 478 479 # If no Auth is explicitly provided, extract it from the URL first. 480 if auth is None: 481 url_auth = get_auth_from_url(self.url) 482 auth = url_auth if any(url_auth) else None 483 484 if auth: 485 if isinstance(auth, tuple) and len(auth) == 2: 486 # special-case basic HTTP auth 487 auth = HTTPBasicAuth(*auth) 488 489 # Allow auth to make its changes. 490 r = auth(self) 491 492 # Update self to reflect the auth changes. 493 self.__dict__.update(r.__dict__) 494 495 # Recompute Content-Length 496 self.prepare_content_length(self.body) 497 498 def prepare_cookies(self, cookies): 499 """Prepares the given HTTP cookie data. 500 501 This function eventually generates a ``Cookie`` header from the 502 given cookies using cookielib. Due to cookielib's design, the header 503 will not be regenerated if it already exists, meaning this function 504 can only be called once for the life of the 505 :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls 506 to ``prepare_cookies`` will have no actual effect, unless the "Cookie" 507 header is removed beforehand.""" 508 509 if isinstance(cookies, cookielib.CookieJar): 510 self._cookies = cookies 511 else: 512 self._cookies = cookiejar_from_dict(cookies) 513 514 cookie_header = get_cookie_header(self._cookies, self) 515 if cookie_header is not None: 516 self.headers['Cookie'] = cookie_header 517 518 def prepare_hooks(self, hooks): 519 """Prepares the given hooks.""" 520 # hooks can be passed as None to the prepare method and to this 521 # method. To prevent iterating over None, simply use an empty list 522 # if hooks is False-y 523 hooks = hooks or [] 524 for event in hooks: 525 self.register_hook(event, hooks[event]) 526 527 528class Response(object): 529 """The :class:`Response <Response>` object, which contains a 530 server's response to an HTTP request. 531 """ 532 533 __attrs__ = [ 534 '_content', 'status_code', 'headers', 'url', 'history', 535 'encoding', 'reason', 'cookies', 'elapsed', 'request' 536 ] 537 538 def __init__(self): 539 super(Response, self).__init__() 540 541 self._content = False 542 self._content_consumed = False 543 544 #: Integer Code of responded HTTP Status, e.g. 404 or 200. 545 self.status_code = None 546 547 #: Case-insensitive Dictionary of Response Headers. 548 #: For example, ``headers['content-encoding']`` will return the 549 #: value of a ``'Content-Encoding'`` response header. 550 self.headers = CaseInsensitiveDict() 551 552 #: File-like object representation of response (for advanced usage). 553 #: Use of ``raw`` requires that ``stream=True`` be set on the request. 554 # This requirement does not apply for use internally to Requests. 555 self.raw = None 556 557 #: Final URL location of Response. 558 self.url = None 559 560 #: Encoding to decode with when accessing r.text. 561 self.encoding = None 562 563 #: A list of :class:`Response <Response>` objects from 564 #: the history of the Request. Any redirect responses will end 565 #: up here. The list is sorted from the oldest to the most recent request. 566 self.history = [] 567 568 #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". 569 self.reason = None 570 571 #: A CookieJar of Cookies the server sent back. 572 self.cookies = cookiejar_from_dict({}) 573 574 #: The amount of time elapsed between sending the request 575 #: and the arrival of the response (as a timedelta). 576 #: This property specifically measures the time taken between sending 577 #: the first byte of the request and finishing parsing the headers. It 578 #: is therefore unaffected by consuming the response content or the 579 #: value of the ``stream`` keyword argument. 580 self.elapsed = datetime.timedelta(0) 581 582 #: The :class:`PreparedRequest <PreparedRequest>` object to which this 583 #: is a response. 584 self.request = None 585 586 def __getstate__(self): 587 # Consume everything; accessing the content attribute makes 588 # sure the content has been fully read. 589 if not self._content_consumed: 590 self.content 591 592 return dict( 593 (attr, getattr(self, attr, None)) 594 for attr in self.__attrs__ 595 ) 596 597 def __setstate__(self, state): 598 for name, value in state.items(): 599 setattr(self, name, value) 600 601 # pickled objects do not have .raw 602 setattr(self, '_content_consumed', True) 603 setattr(self, 'raw', None) 604 605 def __repr__(self): 606 return '<Response [%s]>' % (self.status_code) 607 608 def __bool__(self): 609 """Returns true if :attr:`status_code` is 'OK'.""" 610 return self.ok 611 612 def __nonzero__(self): 613 """Returns true if :attr:`status_code` is 'OK'.""" 614 return self.ok 615 616 def __iter__(self): 617 """Allows you to use a response as an iterator.""" 618 return self.iter_content(128) 619 620 @property 621 def ok(self): 622 try: 623 self.raise_for_status() 624 except HTTPError: 625 return False 626 return True 627 628 @property 629 def is_redirect(self): 630 """True if this Response is a well-formed HTTP redirect that could have 631 been processed automatically (by :meth:`Session.resolve_redirects`). 632 """ 633 return ('location' in self.headers and self.status_code in REDIRECT_STATI) 634 635 @property 636 def is_permanent_redirect(self): 637 """True if this Response one of the permanent versions of redirect""" 638 return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect)) 639 640 @property 641 def apparent_encoding(self): 642 """The apparent encoding, provided by the chardet library""" 643 return chardet.detect(self.content)['encoding'] 644 645 def iter_content(self, chunk_size=1, decode_unicode=False): 646 """Iterates over the response data. When stream=True is set on the 647 request, this avoids reading the content at once into memory for 648 large responses. The chunk size is the number of bytes it should 649 read into memory. This is not necessarily the length of each item 650 returned as decoding can take place. 651 652 If decode_unicode is True, content will be decoded using the best 653 available encoding based on the response. 654 """ 655 656 def generate(): 657 # Special case for urllib3. 658 if hasattr(self.raw, 'stream'): 659 try: 660 for chunk in self.raw.stream(chunk_size, decode_content=True): 661 yield chunk 662 except ProtocolError as e: 663 raise ChunkedEncodingError(e) 664 except DecodeError as e: 665 raise ContentDecodingError(e) 666 except ReadTimeoutError as e: 667 raise ConnectionError(e) 668 else: 669 # Standard file-like object. 670 while True: 671 chunk = self.raw.read(chunk_size) 672 if not chunk: 673 break 674 yield chunk 675 676 self._content_consumed = True 677 678 if self._content_consumed and isinstance(self._content, bool): 679 raise StreamConsumedError() 680 # simulate reading small chunks of the content 681 reused_chunks = iter_slices(self._content, chunk_size) 682 683 stream_chunks = generate() 684 685 chunks = reused_chunks if self._content_consumed else stream_chunks 686 687 if decode_unicode: 688 chunks = stream_decode_response_unicode(chunks, self) 689 690 return chunks 691 692 def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None): 693 """Iterates over the response data, one line at a time. When 694 stream=True is set on the request, this avoids reading the 695 content at once into memory for large responses. 696 697 .. note:: This method is not reentrant safe. 698 """ 699 700 pending = None 701 702 for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode): 703 704 if pending is not None: 705 chunk = pending + chunk 706 707 if delimiter: 708 lines = chunk.split(delimiter) 709 else: 710 lines = chunk.splitlines() 711 712 if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: 713 pending = lines.pop() 714 else: 715 pending = None 716 717 for line in lines: 718 yield line 719 720 if pending is not None: 721 yield pending 722 723 @property 724 def content(self): 725 """Content of the response, in bytes.""" 726 727 if self._content is False: 728 # Read the contents. 729 try: 730 if self._content_consumed: 731 raise RuntimeError( 732 'The content for this response was already consumed') 733 734 if self.status_code == 0: 735 self._content = None 736 else: 737 self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes() 738 739 except AttributeError: 740 self._content = None 741 742 self._content_consumed = True 743 # don't need to release the connection; that's been handled by urllib3 744 # since we exhausted the data. 745 return self._content 746 747 @property 748 def text(self): 749 """Content of the response, in unicode. 750 751 If Response.encoding is None, encoding will be guessed using 752 ``chardet``. 753 754 The encoding of the response content is determined based solely on HTTP 755 headers, following RFC 2616 to the letter. If you can take advantage of 756 non-HTTP knowledge to make a better guess at the encoding, you should 757 set ``r.encoding`` appropriately before accessing this property. 758 """ 759 760 # Try charset from content-type 761 content = None 762 encoding = self.encoding 763 764 if not self.content: 765 return str('') 766 767 # Fallback to auto-detected encoding. 768 if self.encoding is None: 769 encoding = self.apparent_encoding 770 771 # Decode unicode from given encoding. 772 try: 773 content = str(self.content, encoding, errors='replace') 774 except (LookupError, TypeError): 775 # A LookupError is raised if the encoding was not found which could 776 # indicate a misspelling or similar mistake. 777 # 778 # A TypeError can be raised if encoding is None 779 # 780 # So we try blindly encoding. 781 content = str(self.content, errors='replace') 782 783 return content 784 785 def json(self, **kwargs): 786 """Returns the json-encoded content of a response, if any. 787 788 :param \*\*kwargs: Optional arguments that ``json.loads`` takes. 789 """ 790 791 if not self.encoding and len(self.content) > 3: 792 # No encoding set. JSON RFC 4627 section 3 states we should expect 793 # UTF-8, -16 or -32. Detect which one to use; If the detection or 794 # decoding fails, fall back to `self.text` (using chardet to make 795 # a best guess). 796 encoding = guess_json_utf(self.content) 797 if encoding is not None: 798 try: 799 return complexjson.loads( 800 self.content.decode(encoding), **kwargs 801 ) 802 except UnicodeDecodeError: 803 # Wrong UTF codec detected; usually because it's not UTF-8 804 # but some other 8-bit codec. This is an RFC violation, 805 # and the server didn't bother to tell us what codec *was* 806 # used. 807 pass 808 return complexjson.loads(self.text, **kwargs) 809 810 @property 811 def links(self): 812 """Returns the parsed header links of the response, if any.""" 813 814 header = self.headers.get('link') 815 816 # l = MultiDict() 817 l = {} 818 819 if header: 820 links = parse_header_links(header) 821 822 for link in links: 823 key = link.get('rel') or link.get('url') 824 l[key] = link 825 826 return l 827 828 def raise_for_status(self): 829 """Raises stored :class:`HTTPError`, if one occurred.""" 830 831 http_error_msg = '' 832 833 if 400 <= self.status_code < 500: 834 http_error_msg = '%s Client Error: %s for url: %s' % (self.status_code, self.reason, self.url) 835 836 elif 500 <= self.status_code < 600: 837 http_error_msg = '%s Server Error: %s for url: %s' % (self.status_code, self.reason, self.url) 838 839 if http_error_msg: 840 raise HTTPError(http_error_msg, response=self) 841 842 def close(self): 843 """Releases the connection back to the pool. Once this method has been 844 called the underlying ``raw`` object must not be accessed again. 845 846 *Note: Should not normally need to be called explicitly.* 847 """ 848 if not self._content_consumed: 849 return self.raw.close() 850 851 return self.raw.release_conn() 852