|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833 |
- """
- requests.sessions
- ~~~~~~~~~~~~~~~~~
-
- This module provides a Session object to manage and persist settings across
- requests (cookies, auth, proxies).
- """
- import os
- import sys
- import time
- from collections import OrderedDict
- from datetime import timedelta
-
- from ._internal_utils import to_native_string
- from .adapters import HTTPAdapter
- from .auth import _basic_auth_str
- from .compat import Mapping, cookielib, urljoin, urlparse
- from .cookies import (
- RequestsCookieJar,
- cookiejar_from_dict,
- extract_cookies_to_jar,
- merge_cookies,
- )
- from .exceptions import (
- ChunkedEncodingError,
- ContentDecodingError,
- InvalidSchema,
- TooManyRedirects,
- )
- from .hooks import default_hooks, dispatch_hook
-
- # formerly defined here, reexposed here for backward compatibility
- from .models import ( # noqa: F401
- DEFAULT_REDIRECT_LIMIT,
- REDIRECT_STATI,
- PreparedRequest,
- Request,
- )
- from .status_codes import codes
- from .structures import CaseInsensitiveDict
- from .utils import ( # noqa: F401
- DEFAULT_PORTS,
- default_headers,
- get_auth_from_url,
- get_environ_proxies,
- get_netrc_auth,
- requote_uri,
- resolve_proxies,
- rewind_body,
- should_bypass_proxies,
- to_key_val_list,
- )
-
- # Preferred clock, based on which one is more accurate on a given system.
- if sys.platform == "win32":
- preferred_clock = time.perf_counter
- else:
- preferred_clock = time.time
-
-
- def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
- """Determines appropriate setting for a given request, taking into account
- the explicit setting on that request, and the setting in the session. If a
- setting is a dictionary, they will be merged together using `dict_class`
- """
-
- if session_setting is None:
- return request_setting
-
- if request_setting is None:
- return session_setting
-
- # Bypass if not a dictionary (e.g. verify)
- if not (
- isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping)
- ):
- return request_setting
-
- merged_setting = dict_class(to_key_val_list(session_setting))
- merged_setting.update(to_key_val_list(request_setting))
-
- # Remove keys that are set to None. Extract keys first to avoid altering
- # the dictionary during iteration.
- none_keys = [k for (k, v) in merged_setting.items() if v is None]
- for key in none_keys:
- del merged_setting[key]
-
- return merged_setting
-
-
- def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
- """Properly merges both requests and session hooks.
-
- This is necessary because when request_hooks == {'response': []}, the
- merge breaks Session hooks entirely.
- """
- if session_hooks is None or session_hooks.get("response") == []:
- return request_hooks
-
- if request_hooks is None or request_hooks.get("response") == []:
- return session_hooks
-
- return merge_setting(request_hooks, session_hooks, dict_class)
-
-
- class SessionRedirectMixin:
- def get_redirect_target(self, resp):
- """Receives a Response. Returns a redirect URI or ``None``"""
- # Due to the nature of how requests processes redirects this method will
- # be called at least once upon the original response and at least twice
- # on each subsequent redirect response (if any).
- # If a custom mixin is used to handle this logic, it may be advantageous
- # to cache the redirect location onto the response object as a private
- # attribute.
- if resp.is_redirect:
- location = resp.headers["location"]
- # Currently the underlying http module on py3 decode headers
- # in latin1, but empirical evidence suggests that latin1 is very
- # rarely used with non-ASCII characters in HTTP headers.
- # It is more likely to get UTF8 header rather than latin1.
- # This causes incorrect handling of UTF8 encoded location headers.
- # To solve this, we re-encode the location in latin1.
- location = location.encode("latin1")
- return to_native_string(location, "utf8")
- return None
-
- def should_strip_auth(self, old_url, new_url):
- """Decide whether Authorization header should be removed when redirecting"""
- old_parsed = urlparse(old_url)
- new_parsed = urlparse(new_url)
- if old_parsed.hostname != new_parsed.hostname:
- return True
- # Special case: allow http -> https redirect when using the standard
- # ports. This isn't specified by RFC 7235, but is kept to avoid
- # breaking backwards compatibility with older versions of requests
- # that allowed any redirects on the same host.
- if (
- old_parsed.scheme == "http"
- and old_parsed.port in (80, None)
- and new_parsed.scheme == "https"
- and new_parsed.port in (443, None)
- ):
- return False
-
- # Handle default port usage corresponding to scheme.
- changed_port = old_parsed.port != new_parsed.port
- changed_scheme = old_parsed.scheme != new_parsed.scheme
- default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)
- if (
- not changed_scheme
- and old_parsed.port in default_port
- and new_parsed.port in default_port
- ):
- return False
-
- # Standard case: root URI must match
- return changed_port or changed_scheme
-
- def resolve_redirects(
- self,
- resp,
- req,
- stream=False,
- timeout=None,
- verify=True,
- cert=None,
- proxies=None,
- yield_requests=False,
- **adapter_kwargs,
- ):
- """Receives a Response. Returns a generator of Responses or Requests."""
-
- hist = [] # keep track of history
-
- url = self.get_redirect_target(resp)
- previous_fragment = urlparse(req.url).fragment
- while url:
- prepared_request = req.copy()
-
- # Update history and keep track of redirects.
- # resp.history must ignore the original request in this loop
- hist.append(resp)
- resp.history = hist[1:]
-
- try:
- resp.content # Consume socket so it can be released
- except (ChunkedEncodingError, ContentDecodingError, RuntimeError):
- resp.raw.read(decode_content=False)
-
- if len(resp.history) >= self.max_redirects:
- raise TooManyRedirects(
- f"Exceeded {self.max_redirects} redirects.", response=resp
- )
-
- # Release the connection back into the pool.
- resp.close()
-
- # Handle redirection without scheme (see: RFC 1808 Section 4)
- if url.startswith("//"):
- parsed_rurl = urlparse(resp.url)
- url = ":".join([to_native_string(parsed_rurl.scheme), url])
-
- # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
- parsed = urlparse(url)
- if parsed.fragment == "" and previous_fragment:
- parsed = parsed._replace(fragment=previous_fragment)
- elif parsed.fragment:
- previous_fragment = parsed.fragment
- url = parsed.geturl()
-
- # Facilitate relative 'location' headers, as allowed by RFC 7231.
- # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
- # Compliant with RFC3986, we percent encode the url.
- if not parsed.netloc:
- url = urljoin(resp.url, requote_uri(url))
- else:
- url = requote_uri(url)
-
- prepared_request.url = to_native_string(url)
-
- self.rebuild_method(prepared_request, resp)
-
- # https://github.com/psf/requests/issues/1084
- if resp.status_code not in (
- codes.temporary_redirect,
- codes.permanent_redirect,
- ):
- # https://github.com/psf/requests/issues/3490
- purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding")
- for header in purged_headers:
- prepared_request.headers.pop(header, None)
- prepared_request.body = None
-
- headers = prepared_request.headers
- headers.pop("Cookie", None)
-
- # Extract any cookies sent on the response to the cookiejar
- # in the new request. Because we've mutated our copied prepared
- # request, use the old one that we haven't yet touched.
- extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
- merge_cookies(prepared_request._cookies, self.cookies)
- prepared_request.prepare_cookies(prepared_request._cookies)
-
- # Rebuild auth and proxy information.
- proxies = self.rebuild_proxies(prepared_request, proxies)
- self.rebuild_auth(prepared_request, resp)
-
- # A failed tell() sets `_body_position` to `object()`. This non-None
- # value ensures `rewindable` will be True, allowing us to raise an
- # UnrewindableBodyError, instead of hanging the connection.
- rewindable = prepared_request._body_position is not None and (
- "Content-Length" in headers or "Transfer-Encoding" in headers
- )
-
- # Attempt to rewind consumed file-like object.
- if rewindable:
- rewind_body(prepared_request)
-
- # Override the original request.
- req = prepared_request
-
- if yield_requests:
- yield req
- else:
-
- resp = self.send(
- req,
- stream=stream,
- timeout=timeout,
- verify=verify,
- cert=cert,
- proxies=proxies,
- allow_redirects=False,
- **adapter_kwargs,
- )
-
- extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
-
- # extract redirect url, if any, for the next loop
- url = self.get_redirect_target(resp)
- yield resp
-
- def rebuild_auth(self, prepared_request, response):
- """When being redirected we may want to strip authentication from the
- request to avoid leaking credentials. This method intelligently removes
- and reapplies authentication where possible to avoid credential loss.
- """
- headers = prepared_request.headers
- url = prepared_request.url
-
- if "Authorization" in headers and self.should_strip_auth(
- response.request.url, url
- ):
- # If we get redirected to a new host, we should strip out any
- # authentication headers.
- del headers["Authorization"]
-
- # .netrc might have more auth for us on our new host.
- new_auth = get_netrc_auth(url) if self.trust_env else None
- if new_auth is not None:
- prepared_request.prepare_auth(new_auth)
-
- def rebuild_proxies(self, prepared_request, proxies):
- """This method re-evaluates the proxy configuration by considering the
- environment variables. If we are redirected to a URL covered by
- NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
- proxy keys for this URL (in case they were stripped by a previous
- redirect).
-
- This method also replaces the Proxy-Authorization header where
- necessary.
-
- :rtype: dict
- """
- headers = prepared_request.headers
- scheme = urlparse(prepared_request.url).scheme
- new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
-
- if "Proxy-Authorization" in headers:
- del headers["Proxy-Authorization"]
-
- try:
- username, password = get_auth_from_url(new_proxies[scheme])
- except KeyError:
- username, password = None, None
-
- # urllib3 handles proxy authorization for us in the standard adapter.
- # Avoid appending this to TLS tunneled requests where it may be leaked.
- if not scheme.startswith('https') and username and password:
- headers["Proxy-Authorization"] = _basic_auth_str(username, password)
-
- return new_proxies
-
- def rebuild_method(self, prepared_request, response):
- """When being redirected we may want to change the method of the request
- based on certain specs or browser behavior.
- """
- method = prepared_request.method
-
- # https://tools.ietf.org/html/rfc7231#section-6.4.4
- if response.status_code == codes.see_other and method != "HEAD":
- method = "GET"
-
- # Do what the browsers do, despite standards...
- # First, turn 302s into GETs.
- if response.status_code == codes.found and method != "HEAD":
- method = "GET"
-
- # Second, if a POST is responded to with a 301, turn it into a GET.
- # This bizarre behaviour is explained in Issue 1704.
- if response.status_code == codes.moved and method == "POST":
- method = "GET"
-
- prepared_request.method = method
-
-
- class Session(SessionRedirectMixin):
- """A Requests session.
-
- Provides cookie persistence, connection-pooling, and configuration.
-
- Basic Usage::
-
- >>> import requests
- >>> s = requests.Session()
- >>> s.get('https://httpbin.org/get')
- <Response [200]>
-
- Or as a context manager::
-
- >>> with requests.Session() as s:
- ... s.get('https://httpbin.org/get')
- <Response [200]>
- """
-
- __attrs__ = [
- "headers",
- "cookies",
- "auth",
- "proxies",
- "hooks",
- "params",
- "verify",
- "cert",
- "adapters",
- "stream",
- "trust_env",
- "max_redirects",
- ]
-
- def __init__(self):
-
- #: A case-insensitive dictionary of headers to be sent on each
- #: :class:`Request <Request>` sent from this
- #: :class:`Session <Session>`.
- self.headers = default_headers()
-
- #: Default Authentication tuple or object to attach to
- #: :class:`Request <Request>`.
- self.auth = None
-
- #: Dictionary mapping protocol or protocol and host to the URL of the proxy
- #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
- #: be used on each :class:`Request <Request>`.
- self.proxies = {}
-
- #: Event-handling hooks.
- self.hooks = default_hooks()
-
- #: Dictionary of querystring data to attach to each
- #: :class:`Request <Request>`. The dictionary values may be lists for
- #: representing multivalued query parameters.
- self.params = {}
-
- #: Stream response content default.
- self.stream = False
-
- #: SSL Verification default.
- #: Defaults to `True`, requiring requests to verify the TLS certificate at the
- #: remote end.
- #: If verify is set to `False`, requests will accept any TLS certificate
- #: presented by the server, and will ignore hostname mismatches and/or
- #: expired certificates, which will make your application vulnerable to
- #: man-in-the-middle (MitM) attacks.
- #: Only set this to `False` for testing.
- self.verify = True
-
- #: SSL client certificate default, if String, path to ssl client
- #: cert file (.pem). If Tuple, ('cert', 'key') pair.
- self.cert = None
-
- #: Maximum number of redirects allowed. If the request exceeds this
- #: limit, a :class:`TooManyRedirects` exception is raised.
- #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
- #: 30.
- self.max_redirects = DEFAULT_REDIRECT_LIMIT
-
- #: Trust environment settings for proxy configuration, default
- #: authentication and similar.
- self.trust_env = True
-
- #: A CookieJar containing all currently outstanding cookies set on this
- #: session. By default it is a
- #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
- #: may be any other ``cookielib.CookieJar`` compatible object.
- self.cookies = cookiejar_from_dict({})
-
- # Default connection adapters.
- self.adapters = OrderedDict()
- self.mount("https://", HTTPAdapter())
- self.mount("http://", HTTPAdapter())
-
- def __enter__(self):
- return self
-
- def __exit__(self, *args):
- self.close()
-
- def prepare_request(self, request):
- """Constructs a :class:`PreparedRequest <PreparedRequest>` for
- transmission and returns it. The :class:`PreparedRequest` has settings
- merged from the :class:`Request <Request>` instance and those of the
- :class:`Session`.
-
- :param request: :class:`Request` instance to prepare with this
- session's settings.
- :rtype: requests.PreparedRequest
- """
- cookies = request.cookies or {}
-
- # Bootstrap CookieJar.
- if not isinstance(cookies, cookielib.CookieJar):
- cookies = cookiejar_from_dict(cookies)
-
- # Merge with session cookies
- merged_cookies = merge_cookies(
- merge_cookies(RequestsCookieJar(), self.cookies), cookies
- )
-
- # Set environment's basic authentication if not explicitly set.
- auth = request.auth
- if self.trust_env and not auth and not self.auth:
- auth = get_netrc_auth(request.url)
-
- p = PreparedRequest()
- p.prepare(
- method=request.method.upper(),
- url=request.url,
- files=request.files,
- data=request.data,
- json=request.json,
- headers=merge_setting(
- request.headers, self.headers, dict_class=CaseInsensitiveDict
- ),
- params=merge_setting(request.params, self.params),
- auth=merge_setting(auth, self.auth),
- cookies=merged_cookies,
- hooks=merge_hooks(request.hooks, self.hooks),
- )
- return p
-
- def request(
- self,
- method,
- url,
- params=None,
- data=None,
- headers=None,
- cookies=None,
- files=None,
- auth=None,
- timeout=None,
- allow_redirects=True,
- proxies=None,
- hooks=None,
- stream=None,
- verify=None,
- cert=None,
- json=None,
- ):
- """Constructs a :class:`Request <Request>`, prepares it and sends it.
- Returns :class:`Response <Response>` object.
-
- :param method: method for the new :class:`Request` object.
- :param url: URL for the new :class:`Request` object.
- :param params: (optional) Dictionary or bytes to be sent in the query
- string for the :class:`Request`.
- :param data: (optional) Dictionary, list of tuples, bytes, or file-like
- object to send in the body of the :class:`Request`.
- :param json: (optional) json to send in the body of the
- :class:`Request`.
- :param headers: (optional) Dictionary of HTTP Headers to send with the
- :class:`Request`.
- :param cookies: (optional) Dict or CookieJar object to send with the
- :class:`Request`.
- :param files: (optional) Dictionary of ``'filename': file-like-objects``
- for multipart encoding upload.
- :param auth: (optional) Auth tuple or callable to enable
- Basic/Digest/Custom HTTP Auth.
- :param timeout: (optional) How long to wait for the server to send
- data before giving up, as a float, or a :ref:`(connect timeout,
- read timeout) <timeouts>` tuple.
- :type timeout: float or tuple
- :param allow_redirects: (optional) Set to True by default.
- :type allow_redirects: bool
- :param proxies: (optional) Dictionary mapping protocol or protocol and
- hostname to the URL of the proxy.
- :param stream: (optional) whether to immediately download the response
- content. Defaults to ``False``.
- :param verify: (optional) Either a boolean, in which case it controls whether we verify
- the server's TLS certificate, or a string, in which case it must be a path
- to a CA bundle to use. Defaults to ``True``. When set to
- ``False``, requests will accept any TLS certificate presented by
- the server, and will ignore hostname mismatches and/or expired
- certificates, which will make your application vulnerable to
- man-in-the-middle (MitM) attacks. Setting verify to ``False``
- may be useful during local development or testing.
- :param cert: (optional) if String, path to ssl client cert file (.pem).
- If Tuple, ('cert', 'key') pair.
- :rtype: requests.Response
- """
- # Create the Request.
- req = Request(
- method=method.upper(),
- url=url,
- headers=headers,
- files=files,
- data=data or {},
- json=json,
- params=params or {},
- auth=auth,
- cookies=cookies,
- hooks=hooks,
- )
- prep = self.prepare_request(req)
-
- proxies = proxies or {}
-
- settings = self.merge_environment_settings(
- prep.url, proxies, stream, verify, cert
- )
-
- # Send the request.
- send_kwargs = {
- "timeout": timeout,
- "allow_redirects": allow_redirects,
- }
- send_kwargs.update(settings)
- resp = self.send(prep, **send_kwargs)
-
- return resp
-
- def get(self, url, **kwargs):
- r"""Sends a GET request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- kwargs.setdefault("allow_redirects", True)
- return self.request("GET", url, **kwargs)
-
- def options(self, url, **kwargs):
- r"""Sends a OPTIONS request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- kwargs.setdefault("allow_redirects", True)
- return self.request("OPTIONS", url, **kwargs)
-
- def head(self, url, **kwargs):
- r"""Sends a HEAD request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- kwargs.setdefault("allow_redirects", False)
- return self.request("HEAD", url, **kwargs)
-
- def post(self, url, data=None, json=None, **kwargs):
- r"""Sends a POST request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param data: (optional) Dictionary, list of tuples, bytes, or file-like
- object to send in the body of the :class:`Request`.
- :param json: (optional) json to send in the body of the :class:`Request`.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- return self.request("POST", url, data=data, json=json, **kwargs)
-
- def put(self, url, data=None, **kwargs):
- r"""Sends a PUT request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param data: (optional) Dictionary, list of tuples, bytes, or file-like
- object to send in the body of the :class:`Request`.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- return self.request("PUT", url, data=data, **kwargs)
-
- def patch(self, url, data=None, **kwargs):
- r"""Sends a PATCH request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param data: (optional) Dictionary, list of tuples, bytes, or file-like
- object to send in the body of the :class:`Request`.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- return self.request("PATCH", url, data=data, **kwargs)
-
- def delete(self, url, **kwargs):
- r"""Sends a DELETE request. Returns :class:`Response` object.
-
- :param url: URL for the new :class:`Request` object.
- :param \*\*kwargs: Optional arguments that ``request`` takes.
- :rtype: requests.Response
- """
-
- return self.request("DELETE", url, **kwargs)
-
- def send(self, request, **kwargs):
- """Send a given PreparedRequest.
-
- :rtype: requests.Response
- """
- # Set defaults that the hooks can utilize to ensure they always have
- # the correct parameters to reproduce the previous request.
- kwargs.setdefault("stream", self.stream)
- kwargs.setdefault("verify", self.verify)
- kwargs.setdefault("cert", self.cert)
- if "proxies" not in kwargs:
- kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)
-
- # It's possible that users might accidentally send a Request object.
- # Guard against that specific failure case.
- if isinstance(request, Request):
- raise ValueError("You can only send PreparedRequests.")
-
- # Set up variables needed for resolve_redirects and dispatching of hooks
- allow_redirects = kwargs.pop("allow_redirects", True)
- stream = kwargs.get("stream")
- hooks = request.hooks
-
- # Get the appropriate adapter to use
- adapter = self.get_adapter(url=request.url)
-
- # Start time (approximately) of the request
- start = preferred_clock()
-
- # Send the request
- r = adapter.send(request, **kwargs)
-
- # Total elapsed time of the request (approximately)
- elapsed = preferred_clock() - start
- r.elapsed = timedelta(seconds=elapsed)
-
- # Response manipulation hooks
- r = dispatch_hook("response", hooks, r, **kwargs)
-
- # Persist cookies
- if r.history:
-
- # If the hooks create history then we want those cookies too
- for resp in r.history:
- extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
-
- extract_cookies_to_jar(self.cookies, request, r.raw)
-
- # Resolve redirects if allowed.
- if allow_redirects:
- # Redirect resolving generator.
- gen = self.resolve_redirects(r, request, **kwargs)
- history = [resp for resp in gen]
- else:
- history = []
-
- # Shuffle things around if there's history.
- if history:
- # Insert the first (original) request at the start
- history.insert(0, r)
- # Get the last request made
- r = history.pop()
- r.history = history
-
- # If redirects aren't being followed, store the response on the Request for Response.next().
- if not allow_redirects:
- try:
- r._next = next(
- self.resolve_redirects(r, request, yield_requests=True, **kwargs)
- )
- except StopIteration:
- pass
-
- if not stream:
- r.content
-
- return r
-
- def merge_environment_settings(self, url, proxies, stream, verify, cert):
- """
- Check the environment and merge it with some settings.
-
- :rtype: dict
- """
- # Gather clues from the surrounding environment.
- if self.trust_env:
- # Set environment's proxies.
- no_proxy = proxies.get("no_proxy") if proxies is not None else None
- env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
- for (k, v) in env_proxies.items():
- proxies.setdefault(k, v)
-
- # Look for requests environment configuration
- # and be compatible with cURL.
- if verify is True or verify is None:
- verify = (
- os.environ.get("REQUESTS_CA_BUNDLE")
- or os.environ.get("CURL_CA_BUNDLE")
- or verify
- )
-
- # Merge all the kwargs.
- proxies = merge_setting(proxies, self.proxies)
- stream = merge_setting(stream, self.stream)
- verify = merge_setting(verify, self.verify)
- cert = merge_setting(cert, self.cert)
-
- return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}
-
- def get_adapter(self, url):
- """
- Returns the appropriate connection adapter for the given URL.
-
- :rtype: requests.adapters.BaseAdapter
- """
- for (prefix, adapter) in self.adapters.items():
-
- if url.lower().startswith(prefix.lower()):
- return adapter
-
- # Nothing matches :-/
- raise InvalidSchema(f"No connection adapters were found for {url!r}")
-
- def close(self):
- """Closes all adapters and as such the session"""
- for v in self.adapters.values():
- v.close()
-
- def mount(self, prefix, adapter):
- """Registers a connection adapter to a prefix.
-
- Adapters are sorted in descending order by prefix length.
- """
- self.adapters[prefix] = adapter
- keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
-
- for key in keys_to_move:
- self.adapters[key] = self.adapters.pop(key)
-
- def __getstate__(self):
- state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
- return state
-
- def __setstate__(self, state):
- for attr, value in state.items():
- setattr(self, attr, value)
-
-
- def session():
- """
- Returns a :class:`Session` for context-management.
-
- .. deprecated:: 1.0.0
-
- This method has been deprecated since version 1.0.0 and is only kept for
- backwards compatibility. New code should use :class:`~requests.sessions.Session`
- to create a session. This may be removed at a future date.
-
- :rtype: Session
- """
- return Session()
|