utils.py 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from typing import Dict, Iterator
  2. from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
  3. from pip._internal.exceptions import NetworkConnectionError
  4. # The following comments and HTTP headers were originally added by
  5. # Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03.
  6. #
  7. # We use Accept-Encoding: identity here because requests defaults to
  8. # accepting compressed responses. This breaks in a variety of ways
  9. # depending on how the server is configured.
  10. # - Some servers will notice that the file isn't a compressible file
  11. # and will leave the file alone and with an empty Content-Encoding
  12. # - Some servers will notice that the file is already compressed and
  13. # will leave the file alone, adding a Content-Encoding: gzip header
  14. # - Some servers won't notice anything at all and will take a file
  15. # that's already been compressed and compress it again, and set
  16. # the Content-Encoding: gzip header
  17. # By setting this to request only the identity encoding we're hoping
  18. # to eliminate the third case. Hopefully there does not exist a server
  19. # which when given a file will notice it is already compressed and that
  20. # you're not asking for a compressed file and will then decompress it
  21. # before sending because if that's the case I don't think it'll ever be
  22. # possible to make this work.
  23. HEADERS = {'Accept-Encoding': 'identity'} # type: Dict[str, str]
  24. def raise_for_status(resp):
  25. # type: (Response) -> None
  26. http_error_msg = ''
  27. if isinstance(resp.reason, bytes):
  28. # We attempt to decode utf-8 first because some servers
  29. # choose to localize their reason strings. If the string
  30. # isn't utf-8, we fall back to iso-8859-1 for all other
  31. # encodings.
  32. try:
  33. reason = resp.reason.decode('utf-8')
  34. except UnicodeDecodeError:
  35. reason = resp.reason.decode('iso-8859-1')
  36. else:
  37. reason = resp.reason
  38. if 400 <= resp.status_code < 500:
  39. http_error_msg = (
  40. f'{resp.status_code} Client Error: {reason} for url: {resp.url}')
  41. elif 500 <= resp.status_code < 600:
  42. http_error_msg = (
  43. f'{resp.status_code} Server Error: {reason} for url: {resp.url}')
  44. if http_error_msg:
  45. raise NetworkConnectionError(http_error_msg, response=resp)
  46. def response_chunks(response, chunk_size=CONTENT_CHUNK_SIZE):
  47. # type: (Response, int) -> Iterator[bytes]
  48. """Given a requests Response, provide the data chunks.
  49. """
  50. try:
  51. # Special case for urllib3.
  52. for chunk in response.raw.stream(
  53. chunk_size,
  54. # We use decode_content=False here because we don't
  55. # want urllib3 to mess with the raw bytes we get
  56. # from the server. If we decompress inside of
  57. # urllib3 then we cannot verify the checksum
  58. # because the checksum will be of the compressed
  59. # file. This breakage will only occur if the
  60. # server adds a Content-Encoding header, which
  61. # depends on how the server was configured:
  62. # - Some servers will notice that the file isn't a
  63. # compressible file and will leave the file alone
  64. # and with an empty Content-Encoding
  65. # - Some servers will notice that the file is
  66. # already compressed and will leave the file
  67. # alone and will add a Content-Encoding: gzip
  68. # header
  69. # - Some servers won't notice anything at all and
  70. # will take a file that's already been compressed
  71. # and compress it again and set the
  72. # Content-Encoding: gzip header
  73. #
  74. # By setting this not to decode automatically we
  75. # hope to eliminate problems with the second case.
  76. decode_content=False,
  77. ):
  78. yield chunk
  79. except AttributeError:
  80. # Standard file-like object.
  81. while True:
  82. chunk = response.raw.read(chunk_size)
  83. if not chunk:
  84. break
  85. yield chunk