123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- from typing import Dict, Iterator
- from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
- from pip._internal.exceptions import NetworkConnectionError
- # The following comments and HTTP headers were originally added by
- # Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03.
- #
- # We use Accept-Encoding: identity here because requests defaults to
- # accepting compressed responses. This breaks in a variety of ways
- # depending on how the server is configured.
- # - Some servers will notice that the file isn't a compressible file
- # and will leave the file alone and with an empty Content-Encoding
- # - Some servers will notice that the file is already compressed and
- # will leave the file alone, adding a Content-Encoding: gzip header
- # - Some servers won't notice anything at all and will take a file
- # that's already been compressed and compress it again, and set
- # the Content-Encoding: gzip header
- # By setting this to request only the identity encoding we're hoping
- # to eliminate the third case. Hopefully there does not exist a server
- # which when given a file will notice it is already compressed and that
- # you're not asking for a compressed file and will then decompress it
- # before sending because if that's the case I don't think it'll ever be
- # possible to make this work.
- HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"}
- def raise_for_status(resp: Response) -> None:
- http_error_msg = ""
- if isinstance(resp.reason, bytes):
- # We attempt to decode utf-8 first because some servers
- # choose to localize their reason strings. If the string
- # isn't utf-8, we fall back to iso-8859-1 for all other
- # encodings.
- try:
- reason = resp.reason.decode("utf-8")
- except UnicodeDecodeError:
- reason = resp.reason.decode("iso-8859-1")
- else:
- reason = resp.reason
- if 400 <= resp.status_code < 500:
- http_error_msg = (
- f"{resp.status_code} Client Error: {reason} for url: {resp.url}"
- )
- elif 500 <= resp.status_code < 600:
- http_error_msg = (
- f"{resp.status_code} Server Error: {reason} for url: {resp.url}"
- )
- if http_error_msg:
- raise NetworkConnectionError(http_error_msg, response=resp)
- def response_chunks(
- response: Response, chunk_size: int = CONTENT_CHUNK_SIZE
- ) -> Iterator[bytes]:
- """Given a requests Response, provide the data chunks."""
- try:
- # Special case for urllib3.
- for chunk in response.raw.stream(
- chunk_size,
- # We use decode_content=False here because we don't
- # want urllib3 to mess with the raw bytes we get
- # from the server. If we decompress inside of
- # urllib3 then we cannot verify the checksum
- # because the checksum will be of the compressed
- # file. This breakage will only occur if the
- # server adds a Content-Encoding header, which
- # depends on how the server was configured:
- # - Some servers will notice that the file isn't a
- # compressible file and will leave the file alone
- # and with an empty Content-Encoding
- # - Some servers will notice that the file is
- # already compressed and will leave the file
- # alone and will add a Content-Encoding: gzip
- # header
- # - Some servers won't notice anything at all and
- # will take a file that's already been compressed
- # and compress it again and set the
- # Content-Encoding: gzip header
- #
- # By setting this not to decode automatically we
- # hope to eliminate problems with the second case.
- decode_content=False,
- ):
- yield chunk
- except AttributeError:
- # Standard file-like object.
- while True:
- chunk = response.raw.read(chunk_size)
- if not chunk:
- break
- yield chunk
|