test_network.py 9.9 KB

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