test_network.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. from mock import patch
  3. import httpx
  4. from searx.network.network import Network, NETWORKS, initialize
  5. from tests import SearxTestCase
  6. class TestNetwork(SearxTestCase):
  7. def setUp(self):
  8. initialize()
  9. def test_simple(self):
  10. network = Network()
  11. self.assertEqual(next(network._local_addresses_cycle), None)
  12. self.assertEqual(next(network._proxies_cycle), ())
  13. def test_ipaddress_cycle(self):
  14. network = NETWORKS['ipv6']
  15. self.assertEqual(next(network._local_addresses_cycle), '::')
  16. self.assertEqual(next(network._local_addresses_cycle), '::')
  17. network = NETWORKS['ipv4']
  18. self.assertEqual(next(network._local_addresses_cycle), '0.0.0.0')
  19. self.assertEqual(next(network._local_addresses_cycle), '0.0.0.0')
  20. network = Network(local_addresses=['192.168.0.1', '192.168.0.2'])
  21. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.1')
  22. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.2')
  23. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.1')
  24. network = Network(local_addresses=['192.168.0.0/30'])
  25. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.1')
  26. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.2')
  27. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.1')
  28. self.assertEqual(next(network._local_addresses_cycle), '192.168.0.2')
  29. network = Network(local_addresses=['fe80::/10'])
  30. self.assertEqual(next(network._local_addresses_cycle), 'fe80::1')
  31. self.assertEqual(next(network._local_addresses_cycle), 'fe80::2')
  32. self.assertEqual(next(network._local_addresses_cycle), 'fe80::3')
  33. with self.assertRaises(ValueError):
  34. Network(local_addresses=['not_an_ip_address'])
  35. def test_proxy_cycles(self):
  36. network = Network(proxies='http://localhost:1337')
  37. self.assertEqual(next(network._proxies_cycle), (('all://', 'http://localhost:1337'),))
  38. network = Network(proxies={'https': 'http://localhost:1337', 'http': 'http://localhost:1338'})
  39. self.assertEqual(
  40. next(network._proxies_cycle), (('https://', 'http://localhost:1337'), ('http://', 'http://localhost:1338'))
  41. )
  42. self.assertEqual(
  43. next(network._proxies_cycle), (('https://', 'http://localhost:1337'), ('http://', 'http://localhost:1338'))
  44. )
  45. network = Network(
  46. proxies={'https': ['http://localhost:1337', 'http://localhost:1339'], 'http': 'http://localhost:1338'}
  47. )
  48. self.assertEqual(
  49. next(network._proxies_cycle), (('https://', 'http://localhost:1337'), ('http://', 'http://localhost:1338'))
  50. )
  51. self.assertEqual(
  52. next(network._proxies_cycle), (('https://', 'http://localhost:1339'), ('http://', 'http://localhost:1338'))
  53. )
  54. with self.assertRaises(ValueError):
  55. Network(proxies=1)
  56. def test_get_kwargs_clients(self):
  57. kwargs = {
  58. 'verify': True,
  59. 'max_redirects': 5,
  60. 'timeout': 2,
  61. 'allow_redirects': True,
  62. }
  63. kwargs_client = Network.extract_kwargs_clients(kwargs)
  64. self.assertEqual(len(kwargs_client), 2)
  65. self.assertEqual(len(kwargs), 2)
  66. self.assertEqual(kwargs['timeout'], 2)
  67. self.assertEqual(kwargs['follow_redirects'], True)
  68. self.assertTrue(kwargs_client['verify'])
  69. self.assertEqual(kwargs_client['max_redirects'], 5)
  70. async def test_get_client(self):
  71. network = Network(verify=True)
  72. client1 = await network.get_client()
  73. client2 = await network.get_client(verify=True)
  74. client3 = await network.get_client(max_redirects=10)
  75. client4 = await network.get_client(verify=True)
  76. client5 = await network.get_client(verify=False)
  77. client6 = await network.get_client(max_redirects=10)
  78. self.assertEqual(client1, client2)
  79. self.assertEqual(client1, client4)
  80. self.assertNotEqual(client1, client3)
  81. self.assertNotEqual(client1, client5)
  82. self.assertEqual(client3, client6)
  83. await network.aclose()
  84. async def test_aclose(self):
  85. network = Network(verify=True)
  86. await network.get_client()
  87. await network.aclose()
  88. async def test_request(self):
  89. a_text = 'Lorem Ipsum'
  90. response = httpx.Response(status_code=200, text=a_text)
  91. with patch.object(httpx.AsyncClient, 'request', return_value=response):
  92. network = Network(enable_http=True)
  93. response = await network.request('GET', 'https://example.com/')
  94. self.assertEqual(response.text, a_text)
  95. await network.aclose()
  96. class TestNetworkRequestRetries(SearxTestCase):
  97. TEXT = 'Lorem Ipsum'
  98. @classmethod
  99. def get_response_404_then_200(cls):
  100. first = True
  101. async def get_response(*args, **kwargs):
  102. nonlocal first
  103. if first:
  104. first = False
  105. return httpx.Response(status_code=403, text=TestNetworkRequestRetries.TEXT)
  106. return httpx.Response(status_code=200, text=TestNetworkRequestRetries.TEXT)
  107. return get_response
  108. async def test_retries_ok(self):
  109. with patch.object(httpx.AsyncClient, 'request', new=TestNetworkRequestRetries.get_response_404_then_200()):
  110. network = Network(enable_http=True, retries=1, retry_on_http_error=403)
  111. response = await network.request('GET', 'https://example.com/')
  112. self.assertEqual(response.text, TestNetworkRequestRetries.TEXT)
  113. await network.aclose()
  114. async def test_retries_fail_int(self):
  115. with patch.object(httpx.AsyncClient, 'request', new=TestNetworkRequestRetries.get_response_404_then_200()):
  116. network = Network(enable_http=True, retries=0, retry_on_http_error=403)
  117. response = await network.request('GET', 'https://example.com/')
  118. self.assertEqual(response.status_code, 403)
  119. await network.aclose()
  120. async def test_retries_fail_list(self):
  121. with patch.object(httpx.AsyncClient, 'request', new=TestNetworkRequestRetries.get_response_404_then_200()):
  122. network = Network(enable_http=True, retries=0, retry_on_http_error=[403, 429])
  123. response = await network.request('GET', 'https://example.com/')
  124. self.assertEqual(response.status_code, 403)
  125. await network.aclose()
  126. async def test_retries_fail_bool(self):
  127. with patch.object(httpx.AsyncClient, 'request', new=TestNetworkRequestRetries.get_response_404_then_200()):
  128. network = Network(enable_http=True, retries=0, retry_on_http_error=True)
  129. response = await network.request('GET', 'https://example.com/')
  130. self.assertEqual(response.status_code, 403)
  131. await network.aclose()
  132. async def test_retries_exception_then_200(self):
  133. request_count = 0
  134. async def get_response(*args, **kwargs):
  135. nonlocal request_count
  136. request_count += 1
  137. if request_count < 3:
  138. raise httpx.RequestError('fake exception', request=None)
  139. return httpx.Response(status_code=200, text=TestNetworkRequestRetries.TEXT)
  140. with patch.object(httpx.AsyncClient, 'request', new=get_response):
  141. network = Network(enable_http=True, retries=2)
  142. response = await network.request('GET', 'https://example.com/')
  143. self.assertEqual(response.status_code, 200)
  144. self.assertEqual(response.text, TestNetworkRequestRetries.TEXT)
  145. await network.aclose()
  146. async def test_retries_exception(self):
  147. async def get_response(*args, **kwargs):
  148. raise httpx.RequestError('fake exception', request=None)
  149. with patch.object(httpx.AsyncClient, 'request', new=get_response):
  150. network = Network(enable_http=True, retries=0)
  151. with self.assertRaises(httpx.RequestError):
  152. await network.request('GET', 'https://example.com/')
  153. await network.aclose()
  154. class TestNetworkStreamRetries(SearxTestCase):
  155. TEXT = 'Lorem Ipsum'
  156. @classmethod
  157. def get_response_exception_then_200(cls):
  158. first = True
  159. def stream(*args, **kwargs):
  160. nonlocal first
  161. if first:
  162. first = False
  163. raise httpx.RequestError('fake exception', request=None)
  164. return httpx.Response(status_code=200, text=TestNetworkStreamRetries.TEXT)
  165. return stream
  166. async def test_retries_ok(self):
  167. with patch.object(httpx.AsyncClient, 'stream', new=TestNetworkStreamRetries.get_response_exception_then_200()):
  168. network = Network(enable_http=True, retries=1, retry_on_http_error=403)
  169. response = await network.stream('GET', 'https://example.com/')
  170. self.assertEqual(response.text, TestNetworkStreamRetries.TEXT)
  171. await network.aclose()
  172. async def test_retries_fail(self):
  173. with patch.object(httpx.AsyncClient, 'stream', new=TestNetworkStreamRetries.get_response_exception_then_200()):
  174. network = Network(enable_http=True, retries=0, retry_on_http_error=403)
  175. with self.assertRaises(httpx.RequestError):
  176. await network.stream('GET', 'https://example.com/')
  177. await network.aclose()
  178. async def test_retries_exception(self):
  179. first = True
  180. def stream(*args, **kwargs):
  181. nonlocal first
  182. if first:
  183. first = False
  184. return httpx.Response(status_code=403, text=TestNetworkRequestRetries.TEXT)
  185. return httpx.Response(status_code=200, text=TestNetworkRequestRetries.TEXT)
  186. with patch.object(httpx.AsyncClient, 'stream', new=stream):
  187. network = Network(enable_http=True, retries=0, retry_on_http_error=403)
  188. response = await network.stream('GET', 'https://example.com/')
  189. self.assertEqual(response.status_code, 403)
  190. await network.aclose()