UrlService.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. # -*- coding: utf-8 -*-
  2. # Description:
  3. # Author: Pawel Krupa (paulfantom)
  4. # Author: Ilya Mashchenko (ilyam8)
  5. # SPDX-License-Identifier: GPL-3.0-or-later
  6. import urllib3
  7. from distutils.version import StrictVersion as version
  8. from bases.FrameworkServices.SimpleService import SimpleService
  9. try:
  10. urllib3.disable_warnings()
  11. except AttributeError:
  12. pass
  13. # https://github.com/urllib3/urllib3/blob/master/CHANGES.rst#19-2014-07-04
  14. # New retry logic and urllib3.util.retry.Retry configuration object. (Issue https://github.com/urllib3/urllib3/pull/326)
  15. URLLIB3_MIN_REQUIRED_VERSION = '1.9'
  16. URLLIB3_VERSION = urllib3.__version__
  17. URLLIB3 = 'urllib3'
  18. def version_check():
  19. if version(URLLIB3_VERSION) >= version(URLLIB3_MIN_REQUIRED_VERSION):
  20. return
  21. err = '{0} version: {1}, minimum required version: {2}, please upgrade'.format(
  22. URLLIB3,
  23. URLLIB3_VERSION,
  24. URLLIB3_MIN_REQUIRED_VERSION,
  25. )
  26. raise Exception(err)
  27. class UrlService(SimpleService):
  28. def __init__(self, configuration=None, name=None):
  29. version_check()
  30. SimpleService.__init__(self, configuration=configuration, name=name)
  31. self.debug("{0} version: {1}".format(URLLIB3, URLLIB3_VERSION))
  32. self.url = self.configuration.get('url')
  33. self.user = self.configuration.get('user')
  34. self.password = self.configuration.get('pass')
  35. self.proxy_user = self.configuration.get('proxy_user')
  36. self.proxy_password = self.configuration.get('proxy_pass')
  37. self.proxy_url = self.configuration.get('proxy_url')
  38. self.method = self.configuration.get('method', 'GET')
  39. self.header = self.configuration.get('header')
  40. self.body = self.configuration.get('body')
  41. self.request_timeout = self.configuration.get('timeout', 1)
  42. self.respect_retry_after_header = self.configuration.get('respect_retry_after_header')
  43. self.tls_verify = self.configuration.get('tls_verify')
  44. self.tls_ca_file = self.configuration.get('tls_ca_file')
  45. self.tls_key_file = self.configuration.get('tls_key_file')
  46. self.tls_cert_file = self.configuration.get('tls_cert_file')
  47. self._manager = None
  48. def __make_headers(self, **header_kw):
  49. user = header_kw.get('user') or self.user
  50. password = header_kw.get('pass') or self.password
  51. proxy_user = header_kw.get('proxy_user') or self.proxy_user
  52. proxy_password = header_kw.get('proxy_pass') or self.proxy_password
  53. custom_header = header_kw.get('header') or self.header
  54. header_params = dict(keep_alive=True)
  55. proxy_header_params = dict()
  56. if user and password:
  57. header_params['basic_auth'] = '{user}:{password}'.format(user=user,
  58. password=password)
  59. if proxy_user and proxy_password:
  60. proxy_header_params['proxy_basic_auth'] = '{user}:{password}'.format(user=proxy_user,
  61. password=proxy_password)
  62. try:
  63. header, proxy_header = urllib3.make_headers(**header_params), urllib3.make_headers(**proxy_header_params)
  64. except TypeError as error:
  65. self.error('build_header() error: {error}'.format(error=error))
  66. return None, None
  67. else:
  68. header.update(custom_header or dict())
  69. return header, proxy_header
  70. def _build_manager(self, **header_kw):
  71. header, proxy_header = self.__make_headers(**header_kw)
  72. if header is None or proxy_header is None:
  73. return None
  74. proxy_url = header_kw.get('proxy_url') or self.proxy_url
  75. if proxy_url:
  76. manager = urllib3.ProxyManager
  77. params = dict(proxy_url=proxy_url, headers=header, proxy_headers=proxy_header)
  78. else:
  79. manager = urllib3.PoolManager
  80. params = dict(headers=header)
  81. tls_cert_file = self.tls_cert_file
  82. if tls_cert_file:
  83. params['cert_file'] = tls_cert_file
  84. # NOTE: key_file is useless without cert_file, but
  85. # cert_file may include the key as well.
  86. tls_key_file = self.tls_key_file
  87. if tls_key_file:
  88. params['key_file'] = tls_key_file
  89. tls_ca_file = self.tls_ca_file
  90. if tls_ca_file:
  91. params['ca_certs'] = tls_ca_file
  92. try:
  93. url = header_kw.get('url') or self.url
  94. is_https = url.startswith('https')
  95. if skip_tls_verify(is_https, self.tls_verify, tls_ca_file):
  96. params['ca_certs'] = None
  97. params['cert_reqs'] = 'CERT_NONE'
  98. if is_https:
  99. params['assert_hostname'] = False
  100. return manager(**params)
  101. except (urllib3.exceptions.ProxySchemeUnknown, TypeError) as error:
  102. self.error('build_manager() error:', str(error))
  103. return None
  104. def _get_raw_data(self, url=None, manager=None, **kwargs):
  105. """
  106. Get raw data from http request
  107. :return: str
  108. """
  109. try:
  110. response = self._do_request(url, manager, **kwargs)
  111. except Exception as error:
  112. self.error('Url: {url}. Error: {error}'.format(url=url or self.url, error=error))
  113. return None
  114. if response.status == 200:
  115. if isinstance(response.data, str):
  116. return response.data
  117. return response.data.decode(errors='ignore')
  118. else:
  119. self.debug('Url: {url}. Http response status code: {code}'.format(url=url or self.url, code=response.status))
  120. return None
  121. def _get_raw_data_with_status(self, url=None, manager=None, retries=1, redirect=True, **kwargs):
  122. """
  123. Get status and response body content from http request. Does not catch exceptions
  124. :return: int, str
  125. """
  126. response = self._do_request(url, manager, retries, redirect, **kwargs)
  127. if isinstance(response.data, str):
  128. return response.status, response.data
  129. return response.status, response.data.decode(errors='ignore')
  130. def _do_request(self, url=None, manager=None, retries=1, redirect=True, **kwargs):
  131. """
  132. Get response from http request. Does not catch exceptions
  133. :return: HTTPResponse
  134. """
  135. url = url or self.url
  136. manager = manager or self._manager
  137. retry = urllib3.Retry(retries)
  138. if hasattr(retry, 'respect_retry_after_header'):
  139. retry.respect_retry_after_header = bool(self.respect_retry_after_header)
  140. if self.body:
  141. kwargs['body'] = self.body
  142. response = manager.request(
  143. method=self.method,
  144. url=url,
  145. timeout=self.request_timeout,
  146. retries=retry,
  147. headers=manager.headers,
  148. redirect=redirect,
  149. **kwargs
  150. )
  151. return response
  152. def check(self):
  153. """
  154. Format configuration data and try to connect to server
  155. :return: boolean
  156. """
  157. if not (self.url and isinstance(self.url, str)):
  158. self.error('URL is not defined or type is not <str>')
  159. return False
  160. self._manager = self._build_manager()
  161. if not self._manager:
  162. return False
  163. try:
  164. data = self._get_data()
  165. except Exception as error:
  166. self.error('_get_data() failed. Url: {url}. Error: {error}'.format(url=self.url, error=error))
  167. return False
  168. if isinstance(data, dict) and data:
  169. return True
  170. self.error('_get_data() returned no data or type is not <dict>')
  171. return False
  172. def skip_tls_verify(is_https, tls_verify, tls_ca_file):
  173. # default 'tls_verify' value is None
  174. # logic is:
  175. # - never skip if there is 'tls_ca_file' file
  176. # - skip by default for https
  177. # - do not skip by default for http
  178. if tls_ca_file:
  179. return False
  180. if is_https and not tls_verify:
  181. return True
  182. return tls_verify is False