1# -*- coding: utf-8 -*-
2
3"""
4requests.sessions
5~~~~~~~~~~~~~~~~~
6
7This module provides a Session object to manage and persist settings across
8requests (cookies, auth, proxies).
9"""
10import os
11import sys
12import time
13from datetime import timedelta
14from collections import OrderedDict
15
16from .auth import _basic_auth_str
17from .compat import cookielib, is_py3, urljoin, urlparse, Mapping
18from .cookies import (
19    cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies)
20from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT
21from .hooks import default_hooks, dispatch_hook
22from ._internal_utils import to_native_string
23from .utils import to_key_val_list, default_headers, DEFAULT_PORTS
24from .exceptions import (
25    TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError)
26
27from .structures import CaseInsensitiveDict
28from .adapters import HTTPAdapter
29
30from .utils import (
31    requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies,
32    get_auth_from_url, rewind_body
33)
34
35from .status_codes import codes
36
37# formerly defined here, reexposed here for backward compatibility
38from .models import REDIRECT_STATI
39
40# Preferred clock, based on which one is more accurate on a given system.
41if sys.platform == 'win32':
42    try:  # Python 3.4+
43        preferred_clock = time.perf_counter
44    except AttributeError:  # Earlier than Python 3.
45        preferred_clock = time.clock
46else:
47    preferred_clock = time.time
48
49
50def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
51    """Determines appropriate setting for a given request, taking into account
52    the explicit setting on that request, and the setting in the session. If a
53    setting is a dictionary, they will be merged together using `dict_class`
54    """
55
56    if session_setting is None:
57        return request_setting
58
59    if request_setting is None:
60        return session_setting
61
62    # Bypass if not a dictionary (e.g. verify)
63    if not (
64            isinstance(session_setting, Mapping) and
65            isinstance(request_setting, Mapping)
66    ):
67        return request_setting
68
69    merged_setting = dict_class(to_key_val_list(session_setting))
70    merged_setting.update(to_key_val_list(request_setting))
71
72    # Remove keys that are set to None. Extract keys first to avoid altering
73    # the dictionary during iteration.
74    none_keys = [k for (k, v) in merged_setting.items() if v is None]
75    for key in none_keys:
76        del merged_setting[key]
77
78    return merged_setting
79
80
81def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
82    """Properly merges both requests and session hooks.
83
84    This is necessary because when request_hooks == {'response': []}, the
85    merge breaks Session hooks entirely.
86    """
87    if session_hooks is None or session_hooks.get('response') == []:
88        return request_hooks
89
90    if request_hooks is None or request_hooks.get('response') == []:
91        return session_hooks
92
93    return merge_setting(request_hooks, session_hooks, dict_class)
94
95
96class SessionRedirectMixin(object):
97
98    def get_redirect_target(self, resp):
99        """Receives a Response. Returns a redirect URI or ``None``"""
100        # Due to the nature of how requests processes redirects this method will
101        # be called at least once upon the original response and at least twice
102        # on each subsequent redirect response (if any).
103        # If a custom mixin is used to handle this logic, it may be advantageous
104        # to cache the redirect location onto the response object as a private
105        # attribute.
106        if resp.is_redirect:
107            location = resp.headers['location']
108            # Currently the underlying http module on py3 decode headers
109            # in latin1, but empirical evidence suggests that latin1 is very
110            # rarely used with non-ASCII characters in HTTP headers.
111            # It is more likely to get UTF8 header rather than latin1.
112            # This causes incorrect handling of UTF8 encoded location headers.
113            # To solve this, we re-encode the location in latin1.
114            if is_py3:
115                location = location.encode('latin1')
116            return to_native_string(location, 'utf8')
117        return None
118
119    def should_strip_auth(self, old_url, new_url):
120        """Decide whether Authorization header should be removed when redirecting"""
121        old_parsed = urlparse(old_url)
122        new_parsed = urlparse(new_url)
123        if old_parsed.hostname != new_parsed.hostname:
124            return True
125        # Special case: allow http -> https redirect when using the standard
126        # ports. This isn't specified by RFC 7235, but is kept to avoid
127        # breaking backwards compatibility with older versions of requests
128        # that allowed any redirects on the same host.
129        if (old_parsed.scheme == 'http' and old_parsed.port in (80, None)
130                and new_parsed.scheme == 'https' and new_parsed.port in (443, None)):
131            return False
132
133        # Handle default port usage corresponding to scheme.
134        changed_port = old_parsed.port != new_parsed.port
135        changed_scheme = old_parsed.scheme != new_parsed.scheme
136        default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)
137        if (not changed_scheme and old_parsed.port in default_port
138                and new_parsed.port in default_port):
139            return False
140
141        # Standard case: root URI must match
142        return changed_port or changed_scheme
143
144    def resolve_redirects(self, resp, req, stream=False, timeout=None,
145                          verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs):
146        """Receives a Response. Returns a generator of Responses or Requests."""
147
148        hist = []  # keep track of history
149
150        url = self.get_redirect_target(resp)
151        previous_fragment = urlparse(req.url).fragment
152        while url:
153            prepared_request = req.copy()
154
155            # Update history and keep track of redirects.
156            # resp.history must ignore the original request in this loop
157            hist.append(resp)
158            resp.history = hist[1:]
159
160            try:
161                resp.content  # Consume socket so it can be released
162            except (ChunkedEncodingError, ContentDecodingError, RuntimeError):
163                resp.raw.read(decode_content=False)
164
165            if len(resp.history) >= self.max_redirects:
166                raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
167
168            # Release the connection back into the pool.
169            resp.close()
170
171            # Handle redirection without scheme (see: RFC 1808 Section 4)
172            if url.startswith('//'):
173                parsed_rurl = urlparse(resp.url)
174                url = ':'.join([to_native_string(parsed_rurl.scheme), url])
175
176            # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
177            parsed = urlparse(url)
178            if parsed.fragment == '' and previous_fragment:
179                parsed = parsed._replace(fragment=previous_fragment)
180            elif parsed.fragment:
181                previous_fragment = parsed.fragment
182            url = parsed.geturl()
183
184            # Facilitate relative 'location' headers, as allowed by RFC 7231.
185            # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
186            # Compliant with RFC3986, we percent encode the url.
187            if not parsed.netloc:
188                url = urljoin(resp.url, requote_uri(url))
189            else:
190                url = requote_uri(url)
191
192            prepared_request.url = to_native_string(url)
193
194            self.rebuild_method(prepared_request, resp)
195
196            # https://github.com/psf/requests/issues/1084
197            if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect):
198                # https://github.com/psf/requests/issues/3490
199                purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding')
200                for header in purged_headers:
201                    prepared_request.headers.pop(header, None)
202                prepared_request.body = None
203
204            headers = prepared_request.headers
205            headers.pop('Cookie', None)
206
207            # Extract any cookies sent on the response to the cookiejar
208            # in the new request. Because we've mutated our copied prepared
209            # request, use the old one that we haven't yet touched.
210            extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
211            merge_cookies(prepared_request._cookies, self.cookies)
212            prepared_request.prepare_cookies(prepared_request._cookies)
213
214            # Rebuild auth and proxy information.
215            proxies = self.rebuild_proxies(prepared_request, proxies)
216            self.rebuild_auth(prepared_request, resp)
217
218            # A failed tell() sets `_body_position` to `object()`. This non-None
219            # value ensures `rewindable` will be True, allowing us to raise an
220            # UnrewindableBodyError, instead of hanging the connection.
221            rewindable = (
222                prepared_request._body_position is not None and
223                ('Content-Length' in headers or 'Transfer-Encoding' in headers)
224            )
225
226            # Attempt to rewind consumed file-like object.
227            if rewindable:
228                rewind_body(prepared_request)
229
230            # Override the original request.
231            req = prepared_request
232
233            if yield_requests:
234                yield req
235            else:
236
237                resp = self.send(
238                    req,
239                    stream=stream,
240                    timeout=timeout,
241                    verify=verify,
242                    cert=cert,
243                    proxies=proxies,
244                    allow_redirects=False,
245                    **adapter_kwargs
246                )
247
248                extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
249
250                # extract redirect url, if any, for the next loop
251                url = self.get_redirect_target(resp)
252                yield resp
253
254    def rebuild_auth(self, prepared_request, response):
255        """When being redirected we may want to strip authentication from the
256        request to avoid leaking credentials. This method intelligently removes
257        and reapplies authentication where possible to avoid credential loss.
258        """
259        headers = prepared_request.headers
260        url = prepared_request.url
261
262        if 'Authorization' in headers and self.should_strip_auth(response.request.url, url):
263            # If we get redirected to a new host, we should strip out any
264            # authentication headers.
265            del headers['Authorization']
266
267        # .netrc might have more auth for us on our new host.
268        new_auth = get_netrc_auth(url) if self.trust_env else None
269        if new_auth is not None:
270            prepared_request.prepare_auth(new_auth)
271
272
273    def rebuild_proxies(self, prepared_request, proxies):
274        """This method re-evaluates the proxy configuration by considering the
275        environment variables. If we are redirected to a URL covered by
276        NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
277        proxy keys for this URL (in case they were stripped by a previous
278        redirect).
279
280        This method also replaces the Proxy-Authorization header where
281        necessary.
282
283        :rtype: dict
284        """
285        proxies = proxies if proxies is not None else {}
286        headers = prepared_request.headers
287        url = prepared_request.url
288        scheme = urlparse(url).scheme
289        new_proxies = proxies.copy()
290        no_proxy = proxies.get('no_proxy')
291
292        bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy)
293        if self.trust_env and not bypass_proxy:
294            environ_proxies = get_environ_proxies(url, no_proxy=no_proxy)
295
296            proxy = environ_proxies.get(scheme, environ_proxies.get('all'))
297
298            if proxy:
299                new_proxies.setdefault(scheme, proxy)
300
301        if 'Proxy-Authorization' in headers:
302            del headers['Proxy-Authorization']
303
304        try:
305            username, password = get_auth_from_url(new_proxies[scheme])
306        except KeyError:
307            username, password = None, None
308
309        if username and password:
310            headers['Proxy-Authorization'] = _basic_auth_str(username, password)
311
312        return new_proxies
313
314    def rebuild_method(self, prepared_request, response):
315        """When being redirected we may want to change the method of the request
316        based on certain specs or browser behavior.
317        """
318        method = prepared_request.method
319
320        # https://tools.ietf.org/html/rfc7231#section-6.4.4
321        if response.status_code == codes.see_other and method != 'HEAD':
322            method = 'GET'
323
324        # Do what the browsers do, despite standards...
325        # First, turn 302s into GETs.
326        if response.status_code == codes.found and method != 'HEAD':
327            method = 'GET'
328
329        # Second, if a POST is responded to with a 301, turn it into a GET.
330        # This bizarre behaviour is explained in Issue 1704.
331        if response.status_code == codes.moved and method == 'POST':
332            method = 'GET'
333
334        prepared_request.method = method
335
336
337class Session(SessionRedirectMixin):
338    """A Requests session.
339
340    Provides cookie persistence, connection-pooling, and configuration.
341
342    Basic Usage::
343
344      >>> import requests
345      >>> s = requests.Session()
346      >>> s.get('https://httpbin.org/get')
347      <Response [200]>
348
349    Or as a context manager::
350
351      >>> with requests.Session() as s:
352      ...     s.get('https://httpbin.org/get')
353      <Response [200]>
354    """
355
356    __attrs__ = [
357        'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify',
358        'cert', 'adapters', 'stream', 'trust_env',
359        'max_redirects',
360    ]
361
362    def __init__(self):
363
364        #: A case-insensitive dictionary of headers to be sent on each
365        #: :class:`Request <Request>` sent from this
366        #: :class:`Session <Session>`.
367        self.headers = default_headers()
368
369        #: Default Authentication tuple or object to attach to
370        #: :class:`Request <Request>`.
371        self.auth = None
372
373        #: Dictionary mapping protocol or protocol and host to the URL of the proxy
374        #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
375        #: be used on each :class:`Request <Request>`.
376        self.proxies = {}
377
378        #: Event-handling hooks.
379        self.hooks = default_hooks()
380
381        #: Dictionary of querystring data to attach to each
382        #: :class:`Request <Request>`. The dictionary values may be lists for
383        #: representing multivalued query parameters.
384        self.params = {}
385
386        #: Stream response content default.
387        self.stream = False
388
389        #: SSL Verification default.
390        #: Defaults to `True`, requiring requests to verify the TLS certificate at the
391        #: remote end.
392        #: If verify is set to `False`, requests will accept any TLS certificate
393        #: presented by the server, and will ignore hostname mismatches and/or
394        #: expired certificates, which will make your application vulnerable to
395        #: man-in-the-middle (MitM) attacks.
396        #: Only set this to `False` for testing.
397        self.verify = True
398
399        #: SSL client certificate default, if String, path to ssl client
400        #: cert file (.pem). If Tuple, ('cert', 'key') pair.
401        self.cert = None
402
403        #: Maximum number of redirects allowed. If the request exceeds this
404        #: limit, a :class:`TooManyRedirects` exception is raised.
405        #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
406        #: 30.
407        self.max_redirects = DEFAULT_REDIRECT_LIMIT
408
409        #: Trust environment settings for proxy configuration, default
410        #: authentication and similar.
411        self.trust_env = True
412
413        #: A CookieJar containing all currently outstanding cookies set on this
414        #: session. By default it is a
415        #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
416        #: may be any other ``cookielib.CookieJar`` compatible object.
417        self.cookies = cookiejar_from_dict({})
418
419        # Default connection adapters.
420        self.adapters = OrderedDict()
421        self.mount('https://', HTTPAdapter())
422        self.mount('http://', HTTPAdapter())
423
424    def __enter__(self):
425        return self
426
427    def __exit__(self, *args):
428        self.close()
429
430    def prepare_request(self, request):
431        """Constructs a :class:`PreparedRequest <PreparedRequest>` for
432        transmission and returns it. The :class:`PreparedRequest` has settings
433        merged from the :class:`Request <Request>` instance and those of the
434        :class:`Session`.
435
436        :param request: :class:`Request` instance to prepare with this
437            session's settings.
438        :rtype: requests.PreparedRequest
439        """
440        cookies = request.cookies or {}
441
442        # Bootstrap CookieJar.
443        if not isinstance(cookies, cookielib.CookieJar):
444            cookies = cookiejar_from_dict(cookies)
445
446        # Merge with session cookies
447        merged_cookies = merge_cookies(
448            merge_cookies(RequestsCookieJar(), self.cookies), cookies)
449
450        # Set environment's basic authentication if not explicitly set.
451        auth = request.auth
452        if self.trust_env and not auth and not self.auth:
453            auth = get_netrc_auth(request.url)
454
455        p = PreparedRequest()
456        p.prepare(
457            method=request.method.upper(),
458            url=request.url,
459            files=request.files,
460            data=request.data,
461            json=request.json,
462            headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict),
463            params=merge_setting(request.params, self.params),
464            auth=merge_setting(auth, self.auth),
465            cookies=merged_cookies,
466            hooks=merge_hooks(request.hooks, self.hooks),
467        )
468        return p
469
470    def request(self, method, url,
471            params=None, data=None, headers=None, cookies=None, files=None,
472            auth=None, timeout=None, allow_redirects=True, proxies=None,
473            hooks=None, stream=None, verify=None, cert=None, json=None):
474        """Constructs a :class:`Request <Request>`, prepares it and sends it.
475        Returns :class:`Response <Response>` object.
476
477        :param method: method for the new :class:`Request` object.
478        :param url: URL for the new :class:`Request` object.
479        :param params: (optional) Dictionary or bytes to be sent in the query
480            string for the :class:`Request`.
481        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
482            object to send in the body of the :class:`Request`.
483        :param json: (optional) json to send in the body of the
484            :class:`Request`.
485        :param headers: (optional) Dictionary of HTTP Headers to send with the
486            :class:`Request`.
487        :param cookies: (optional) Dict or CookieJar object to send with the
488            :class:`Request`.
489        :param files: (optional) Dictionary of ``'filename': file-like-objects``
490            for multipart encoding upload.
491        :param auth: (optional) Auth tuple or callable to enable
492            Basic/Digest/Custom HTTP Auth.
493        :param timeout: (optional) How long to wait for the server to send
494            data before giving up, as a float, or a :ref:`(connect timeout,
495            read timeout) <timeouts>` tuple.
496        :type timeout: float or tuple
497        :param allow_redirects: (optional) Set to True by default.
498        :type allow_redirects: bool
499        :param proxies: (optional) Dictionary mapping protocol or protocol and
500            hostname to the URL of the proxy.
501        :param stream: (optional) whether to immediately download the response
502            content. Defaults to ``False``.
503        :param verify: (optional) Either a boolean, in which case it controls whether we verify
504            the server's TLS certificate, or a string, in which case it must be a path
505            to a CA bundle to use. Defaults to ``True``. When set to
506            ``False``, requests will accept any TLS certificate presented by
507            the server, and will ignore hostname mismatches and/or expired
508            certificates, which will make your application vulnerable to
509            man-in-the-middle (MitM) attacks. Setting verify to ``False``
510            may be useful during local development or testing.
511        :param cert: (optional) if String, path to ssl client cert file (.pem).
512            If Tuple, ('cert', 'key') pair.
513        :rtype: requests.Response
514        """
515        # Create the Request.
516        req = Request(
517            method=method.upper(),
518            url=url,
519            headers=headers,
520            files=files,
521            data=data or {},
522            json=json,
523            params=params or {},
524            auth=auth,
525            cookies=cookies,
526            hooks=hooks,
527        )
528        prep = self.prepare_request(req)
529
530        proxies = proxies or {}
531
532        settings = self.merge_environment_settings(
533            prep.url, proxies, stream, verify, cert
534        )
535
536        # Send the request.
537        send_kwargs = {
538            'timeout': timeout,
539            'allow_redirects': allow_redirects,
540        }
541        send_kwargs.update(settings)
542        resp = self.send(prep, **send_kwargs)
543
544        return resp
545
546    def get(self, url, **kwargs):
547        r"""Sends a GET request. Returns :class:`Response` object.
548
549        :param url: URL for the new :class:`Request` object.
550        :param \*\*kwargs: Optional arguments that ``request`` takes.
551        :rtype: requests.Response
552        """
553
554        kwargs.setdefault('allow_redirects', True)
555        return self.request('GET', url, **kwargs)
556
557    def options(self, url, **kwargs):
558        r"""Sends a OPTIONS request. Returns :class:`Response` object.
559
560        :param url: URL for the new :class:`Request` object.
561        :param \*\*kwargs: Optional arguments that ``request`` takes.
562        :rtype: requests.Response
563        """
564
565        kwargs.setdefault('allow_redirects', True)
566        return self.request('OPTIONS', url, **kwargs)
567
568    def head(self, url, **kwargs):
569        r"""Sends a HEAD request. Returns :class:`Response` object.
570
571        :param url: URL for the new :class:`Request` object.
572        :param \*\*kwargs: Optional arguments that ``request`` takes.
573        :rtype: requests.Response
574        """
575
576        kwargs.setdefault('allow_redirects', False)
577        return self.request('HEAD', url, **kwargs)
578
579    def post(self, url, data=None, json=None, **kwargs):
580        r"""Sends a POST request. Returns :class:`Response` object.
581
582        :param url: URL for the new :class:`Request` object.
583        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
584            object to send in the body of the :class:`Request`.
585        :param json: (optional) json to send in the body of the :class:`Request`.
586        :param \*\*kwargs: Optional arguments that ``request`` takes.
587        :rtype: requests.Response
588        """
589
590        return self.request('POST', url, data=data, json=json, **kwargs)
591
592    def put(self, url, data=None, **kwargs):
593        r"""Sends a PUT request. Returns :class:`Response` object.
594
595        :param url: URL for the new :class:`Request` object.
596        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
597            object to send in the body of the :class:`Request`.
598        :param \*\*kwargs: Optional arguments that ``request`` takes.
599        :rtype: requests.Response
600        """
601
602        return self.request('PUT', url, data=data, **kwargs)
603
604    def patch(self, url, data=None, **kwargs):
605        r"""Sends a PATCH request. Returns :class:`Response` object.
606
607        :param url: URL for the new :class:`Request` object.
608        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
609            object to send in the body of the :class:`Request`.
610        :param \*\*kwargs: Optional arguments that ``request`` takes.
611        :rtype: requests.Response
612        """
613
614        return self.request('PATCH', url, data=data, **kwargs)
615
616    def delete(self, url, **kwargs):
617        r"""Sends a DELETE request. Returns :class:`Response` object.
618
619        :param url: URL for the new :class:`Request` object.
620        :param \*\*kwargs: Optional arguments that ``request`` takes.
621        :rtype: requests.Response
622        """
623
624        return self.request('DELETE', url, **kwargs)
625
626    def send(self, request, **kwargs):
627        """Send a given PreparedRequest.
628
629        :rtype: requests.Response
630        """
631        # Set defaults that the hooks can utilize to ensure they always have
632        # the correct parameters to reproduce the previous request.
633        kwargs.setdefault('stream', self.stream)
634        kwargs.setdefault('verify', self.verify)
635        kwargs.setdefault('cert', self.cert)
636        kwargs.setdefault('proxies', self.proxies)
637
638        # It's possible that users might accidentally send a Request object.
639        # Guard against that specific failure case.
640        if isinstance(request, Request):
641            raise ValueError('You can only send PreparedRequests.')
642
643        # Set up variables needed for resolve_redirects and dispatching of hooks
644        allow_redirects = kwargs.pop('allow_redirects', True)
645        stream = kwargs.get('stream')
646        hooks = request.hooks
647
648        # Get the appropriate adapter to use
649        adapter = self.get_adapter(url=request.url)
650
651        # Start time (approximately) of the request
652        start = preferred_clock()
653
654        # Send the request
655        r = adapter.send(request, **kwargs)
656
657        # Total elapsed time of the request (approximately)
658        elapsed = preferred_clock() - start
659        r.elapsed = timedelta(seconds=elapsed)
660
661        # Response manipulation hooks
662        r = dispatch_hook('response', hooks, r, **kwargs)
663
664        # Persist cookies
665        if r.history:
666
667            # If the hooks create history then we want those cookies too
668            for resp in r.history:
669                extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
670
671        extract_cookies_to_jar(self.cookies, request, r.raw)
672
673        # Resolve redirects if allowed.
674        if allow_redirects:
675            # Redirect resolving generator.
676            gen = self.resolve_redirects(r, request, **kwargs)
677            history = [resp for resp in gen]
678        else:
679            history = []
680
681        # Shuffle things around if there's history.
682        if history:
683            # Insert the first (original) request at the start
684            history.insert(0, r)
685            # Get the last request made
686            r = history.pop()
687            r.history = history
688
689        # If redirects aren't being followed, store the response on the Request for Response.next().
690        if not allow_redirects:
691            try:
692                r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs))
693            except StopIteration:
694                pass
695
696        if not stream:
697            r.content
698
699        return r
700
701    def merge_environment_settings(self, url, proxies, stream, verify, cert):
702        """
703        Check the environment and merge it with some settings.
704
705        :rtype: dict
706        """
707        # Gather clues from the surrounding environment.
708        if self.trust_env:
709            # Set environment's proxies.
710            no_proxy = proxies.get('no_proxy') if proxies is not None else None
711            env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
712            for (k, v) in env_proxies.items():
713                proxies.setdefault(k, v)
714
715            # Look for requests environment configuration and be compatible
716            # with cURL.
717            if verify is True or verify is None:
718                verify = (os.environ.get('REQUESTS_CA_BUNDLE') or
719                          os.environ.get('CURL_CA_BUNDLE'))
720
721        # Merge all the kwargs.
722        proxies = merge_setting(proxies, self.proxies)
723        stream = merge_setting(stream, self.stream)
724        verify = merge_setting(verify, self.verify)
725        cert = merge_setting(cert, self.cert)
726
727        return {'verify': verify, 'proxies': proxies, 'stream': stream,
728                'cert': cert}
729
730    def get_adapter(self, url):
731        """
732        Returns the appropriate connection adapter for the given URL.
733
734        :rtype: requests.adapters.BaseAdapter
735        """
736        for (prefix, adapter) in self.adapters.items():
737
738            if url.lower().startswith(prefix.lower()):
739                return adapter
740
741        # Nothing matches :-/
742        raise InvalidSchema("No connection adapters were found for {!r}".format(url))
743
744    def close(self):
745        """Closes all adapters and as such the session"""
746        for v in self.adapters.values():
747            v.close()
748
749    def mount(self, prefix, adapter):
750        """Registers a connection adapter to a prefix.
751
752        Adapters are sorted in descending order by prefix length.
753        """
754        self.adapters[prefix] = adapter
755        keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
756
757        for key in keys_to_move:
758            self.adapters[key] = self.adapters.pop(key)
759
760    def __getstate__(self):
761        state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
762        return state
763
764    def __setstate__(self, state):
765        for attr, value in state.items():
766            setattr(self, attr, value)
767
768
769def session():
770    """
771    Returns a :class:`Session` for context-management.
772
773    .. deprecated:: 1.0.0
774
775        This method has been deprecated since version 1.0.0 and is only kept for
776        backwards compatibility. New code should use :class:`~requests.sessions.Session`
777        to create a session. This may be removed at a future date.
778
779    :rtype: Session
780    """
781    return Session()
782