codekingpro/portable-devtools
114k
1# --------------------------------------------------------------------------2#3# Copyright (c) Microsoft Corporation. All rights reserved.4#5# The MIT License (MIT)6#7# Permission is hereby granted, free of charge, to any person obtaining a copy8# of this software and associated documentation files (the ""Software""), to9# deal in the Software without restriction, including without limitation the10# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or11# sell copies of the Software, and to permit persons to whom the Software is12# furnished to do so, subject to the following conditions:13#14# The above copyright notice and this permission notice shall be included in15# all copies or substantial portions of the Software.16#17# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS23# IN THE SOFTWARE.24#25# --------------------------------------------------------------------------26"""27This module is the requests implementation of Pipeline ABC28"""29from __future__ import absolute_import # we have a "requests" module that conflicts with "requests" on Py2.730import contextlib31import logging32import threading33from typing import TYPE_CHECKING, List, Callable, Iterator, Any, Union, Dict, Optional # pylint: disable=unused-import34import warnings35 36from oauthlib import oauth237import requests38from requests.models import CONTENT_CHUNK_SIZE39 40from urllib3 import Retry # Needs requests 2.16 at least to be safe41 42from ..exceptions import (43 TokenExpiredError,44 ClientRequestError,45 raise_with_traceback46)47from ..universal_http import ClientRequest48from ..universal_http.requests import BasicRequestsHTTPSender49from . import HTTPSender, HTTPPolicy, Response, Request50 51 52_LOGGER = logging.getLogger(__name__)53 54 55class RequestsCredentialsPolicy(HTTPPolicy):56 """Implementation of request-oauthlib except and retry logic.57 """58 def __init__(self, credentials):59 super(RequestsCredentialsPolicy, self).__init__()60 self._creds = credentials61 62 def send(self, request, **kwargs):63 session = request.context.session64 try:65 self._creds.signed_session(session)66 except TypeError: # Credentials does not support session injection67 _LOGGER.warning("Your credentials class does not support session injection. Performance will not be at the maximum.")68 request.context.session = session = self._creds.signed_session()69 70 try:71 try:72 return self.next.send(request, **kwargs)73 except (oauth2.rfc6749.errors.InvalidGrantError,74 oauth2.rfc6749.errors.TokenExpiredError) as err:75 error = "Token expired or is invalid. Attempting to refresh."76 _LOGGER.warning(error)77 78 try:79 try:80 self._creds.refresh_session(session)81 except TypeError: # Credentials does not support session injection82 _LOGGER.warning("Your credentials class does not support session injection. Performance will not be at the maximum.")83 request.context.session = session = self._creds.refresh_session()84 85 return self.next.send(request, **kwargs)86 except (oauth2.rfc6749.errors.InvalidGrantError,87 oauth2.rfc6749.errors.TokenExpiredError) as err:88 msg = "Token expired or is invalid."89 raise_with_traceback(TokenExpiredError, msg, err)90 91 except (requests.RequestException,92 oauth2.rfc6749.errors.OAuth2Error) as err:93 msg = "Error occurred in request."94 raise_with_traceback(ClientRequestError, msg, err)95 96class RequestsPatchSession(HTTPPolicy):97 """Implements request level configuration98 that are actually to be done at the session level.99 100 This is highly deprecated, and is totally legacy.101 The pipeline structure allows way better design for this.102 """103 _protocols = ['http://', 'https://']104 105 def send(self, request, **kwargs):106 """Patch the current session with Request level operation config.107 108 This is deprecated, we shouldn't patch the session with109 arguments at the Request, and "config" should be used.110 """111 session = request.context.session112 113 old_max_redirects = None114 if 'max_redirects' in kwargs:115 warnings.warn("max_redirects in operation kwargs is deprecated, use config.redirect_policy instead",116 DeprecationWarning)117 old_max_redirects = session.max_redirects118 session.max_redirects = int(kwargs['max_redirects'])119 120 old_trust_env = None121 if 'use_env_proxies' in kwargs:122 warnings.warn("use_env_proxies in operation kwargs is deprecated, use config.proxies instead",123 DeprecationWarning)124 old_trust_env = session.trust_env125 session.trust_env = bool(kwargs['use_env_proxies'])126 127 old_retries = {}128 if 'retries' in kwargs:129 warnings.warn("retries in operation kwargs is deprecated, use config.retry_policy instead",130 DeprecationWarning)131 max_retries = kwargs['retries']132 for protocol in self._protocols:133 old_retries[protocol] = session.adapters[protocol].max_retries134 session.adapters[protocol].max_retries = max_retries135 136 try:137 return self.next.send(request, **kwargs)138 finally:139 if old_max_redirects:140 session.max_redirects = old_max_redirects141 142 if old_trust_env:143 session.trust_env = old_trust_env144 145 if old_retries:146 for protocol in self._protocols:147 session.adapters[protocol].max_retries = old_retries[protocol]148 149class RequestsContext(object):150 def __init__(self, session):151 self.session = session152 153 154class PipelineRequestsHTTPSender(HTTPSender):155 """Implements a basic Pipeline, that supports universal HTTP lib "requests" driver.156 """157 158 def __init__(self, universal_http_requests_driver=None):159 # type: (Optional[BasicRequestsHTTPSender]) -> None160 self.driver = universal_http_requests_driver or BasicRequestsHTTPSender()161 162 def __enter__(self):163 # type: () -> PipelineRequestsHTTPSender164 self.driver.__enter__()165 return self166 167 def __exit__(self, *exc_details): # pylint: disable=arguments-differ168 self.driver.__exit__(*exc_details)169 170 def close(self):171 self.__exit__()172 173 def build_context(self):174 # type: () -> RequestsContext175 return RequestsContext(176 session=self.driver.session,177 )178 179 def send(self, request, **kwargs):180 # type: (Request[ClientRequest], Any) -> Response181 """Send request object according to configuration.182 183 :param Request request: The request object to be sent.184 """185 if request.context is None: # Should not happen, but make mypy happy and does not hurt186 request.context = self.build_context()187 188 if request.context.session is not self.driver.session:189 kwargs['session'] = request.context.session190 191 return Response(192 request,193 self.driver.send(request.http_request, **kwargs)194 )195 