1""" 2 sphinx.util.requests 3 ~~~~~~~~~~~~~~~~~~~~ 4 5 Simple requests package loader 6 7 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 :license: BSD, see LICENSE for details. 9""" 10 11import sys 12import warnings 13from contextlib import contextmanager 14from typing import Any, Generator, Union 15from urllib.parse import urlsplit 16 17import requests 18 19import sphinx 20from sphinx.config import Config 21from sphinx.deprecation import RemovedInSphinx50Warning 22 23try: 24 from requests.packages.urllib3.exceptions import SSLError 25except ImportError: 26 # python-requests package in Debian jessie does not provide ``requests.packages.urllib3``. 27 # So try to import the exceptions from urllib3 package. 28 from urllib3.exceptions import SSLError # type: ignore 29 30try: 31 from requests.packages.urllib3.exceptions import InsecureRequestWarning 32except ImportError: 33 try: 34 # for Debian-jessie 35 from urllib3.exceptions import InsecureRequestWarning # type: ignore 36 except ImportError: 37 # for requests < 2.4.0 38 InsecureRequestWarning = None # type: ignore 39 40 41useragent_header = [('User-Agent', 42 'Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0')] 43 44 45def is_ssl_error(exc: Exception) -> bool: 46 """Check an exception is SSLError.""" 47 warnings.warn( 48 "is_ssl_error() is outdated and likely returns incorrect results " 49 "for modern versions of Requests.", 50 RemovedInSphinx50Warning) 51 if isinstance(exc, SSLError): 52 return True 53 else: 54 args = getattr(exc, 'args', []) 55 if args and isinstance(args[0], SSLError): 56 return True 57 else: 58 return False 59 60 61@contextmanager 62def ignore_insecure_warning(**kwargs: Any) -> Generator[None, None, None]: 63 with warnings.catch_warnings(): 64 if not kwargs.get('verify') and InsecureRequestWarning: 65 # ignore InsecureRequestWarning if verify=False 66 warnings.filterwarnings("ignore", category=InsecureRequestWarning) 67 yield 68 69 70def _get_tls_cacert(url: str, config: Config) -> Union[str, bool]: 71 """Get additional CA cert for a specific URL. 72 73 This also returns ``False`` if verification is disabled. 74 And returns ``True`` if additional CA cert not found. 75 """ 76 if not config.tls_verify: 77 return False 78 79 certs = getattr(config, 'tls_cacerts', None) 80 if not certs: 81 return True 82 elif isinstance(certs, (str, tuple)): 83 return certs # type: ignore 84 else: 85 hostname = urlsplit(url)[1] 86 if '@' in hostname: 87 hostname = hostname.split('@')[1] 88 89 return certs.get(hostname, True) 90 91 92def _get_user_agent(config: Config) -> str: 93 if config.user_agent: 94 return config.user_agent 95 else: 96 return ' '.join([ 97 'Sphinx/%s' % sphinx.__version__, 98 'requests/%s' % requests.__version__, 99 'python/%s' % '.'.join(map(str, sys.version_info[:3])), 100 ]) 101 102 103def get(url: str, **kwargs: Any) -> requests.Response: 104 """Sends a GET request like requests.get(). 105 106 This sets up User-Agent header and TLS verification automatically.""" 107 headers = kwargs.setdefault('headers', {}) 108 config = kwargs.pop('config', None) 109 if config: 110 kwargs.setdefault('verify', _get_tls_cacert(url, config)) 111 headers.setdefault('User-Agent', _get_user_agent(config)) 112 else: 113 headers.setdefault('User-Agent', useragent_header[0][1]) 114 115 with ignore_insecure_warning(**kwargs): 116 return requests.get(url, **kwargs) 117 118 119def head(url: str, **kwargs: Any) -> requests.Response: 120 """Sends a HEAD request like requests.head(). 121 122 This sets up User-Agent header and TLS verification automatically.""" 123 headers = kwargs.setdefault('headers', {}) 124 config = kwargs.pop('config', None) 125 if config: 126 kwargs.setdefault('verify', _get_tls_cacert(url, config)) 127 headers.setdefault('User-Agent', _get_user_agent(config)) 128 else: 129 headers.setdefault('User-Agent', useragent_header[0][1]) 130 131 with ignore_insecure_warning(**kwargs): 132 return requests.head(url, **kwargs) 133