test_network.py 9.8 KB

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