An attempt at adding UDP support to aiosocks. Untested due to lack of server support.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

603 lines
22 KiB

  1. import asyncio
  2. import aiosocks
  3. import unittest
  4. import socket
  5. from unittest import mock
  6. from asyncio import coroutine as coro
  7. import aiosocks.constants as c
  8. from aiosocks.protocols import BaseSocksProtocol
  9. from .helpers import fake_coroutine
  10. try:
  11. from asyncio import ensure_future
  12. except ImportError:
  13. ensure_future = asyncio.async
  14. def make_base(loop, *, dst=None, waiter=None, ap_factory=None, ssl=None):
  15. dst = dst or ('python.org', 80)
  16. proto = BaseSocksProtocol(None, None, dst=dst, ssl=ssl,
  17. loop=loop, waiter=waiter,
  18. app_protocol_factory=ap_factory)
  19. return proto
  20. def make_socks4(loop, *, addr=None, auth=None, rr=True, dst=None, r=b'',
  21. ap_factory=None, whiter=None):
  22. addr = addr or aiosocks.Socks4Addr('localhost', 1080)
  23. auth = auth or aiosocks.Socks4Auth('user')
  24. dst = dst or ('python.org', 80)
  25. proto = aiosocks.Socks4Protocol(
  26. proxy=addr, proxy_auth=auth, dst=dst, remote_resolve=rr,
  27. loop=loop, app_protocol_factory=ap_factory, waiter=whiter)
  28. proto._stream_writer = mock.Mock()
  29. proto.read_response = mock.Mock(
  30. side_effect=coro(mock.Mock(return_value=r)))
  31. proto._get_dst_addr = mock.Mock(
  32. side_effect=coro(mock.Mock(return_value=(socket.AF_INET, '127.0.0.1')))
  33. )
  34. return proto
  35. def make_socks5(loop, *, addr=None, auth=None, rr=True, dst=None, r=None,
  36. ap_factory=None, whiter=None):
  37. addr = addr or aiosocks.Socks5Addr('localhost', 1080)
  38. auth = auth or aiosocks.Socks5Auth('user', 'pwd')
  39. dst = dst or ('python.org', 80)
  40. proto = aiosocks.Socks5Protocol(
  41. proxy=addr, proxy_auth=auth, dst=dst, remote_resolve=rr,
  42. loop=loop, app_protocol_factory=ap_factory, waiter=whiter)
  43. proto._stream_writer = mock.Mock()
  44. if not isinstance(r, (list, tuple)):
  45. proto.read_response = mock.Mock(
  46. side_effect=coro(mock.Mock(return_value=r)))
  47. else:
  48. proto.read_response = mock.Mock(
  49. side_effect=coro(mock.Mock(side_effect=r)))
  50. proto._get_dst_addr = mock.Mock(
  51. side_effect=coro(mock.Mock(return_value=(socket.AF_INET, '127.0.0.1')))
  52. )
  53. return proto
  54. class TestBaseSocksProtocol(unittest.TestCase):
  55. def setUp(self):
  56. self.loop = asyncio.new_event_loop()
  57. asyncio.set_event_loop(None)
  58. def tearDown(self):
  59. self.loop.close()
  60. def test_init(self):
  61. with self.assertRaises(ValueError):
  62. BaseSocksProtocol(None, None, None, loop=self.loop,
  63. waiter=None, app_protocol_factory=None)
  64. with self.assertRaises(ValueError):
  65. BaseSocksProtocol(None, None, 123, loop=self.loop,
  66. waiter=None, app_protocol_factory=None)
  67. with self.assertRaises(ValueError):
  68. BaseSocksProtocol(None, None, ('python.org',), loop=self.loop,
  69. waiter=None, app_protocol_factory=None)
  70. def test_write_request(self):
  71. proto = make_base(self.loop)
  72. proto._stream_writer = mock.Mock()
  73. proto.write_request([b'\x00', b'\x01\x02', 0x03])
  74. proto._stream_writer.write.assert_called_with(b'\x00\x01\x02\x03')
  75. with self.assertRaises(ValueError):
  76. proto.write_request(['\x00'])
  77. def test_negotiate_os_error(self):
  78. waiter = asyncio.Future(loop=self.loop)
  79. proto = make_base(self.loop, waiter=waiter)
  80. proto.socks_request = fake_coroutine(OSError('test'))
  81. self.loop.run_until_complete(proto.negotiate(None, None))
  82. self.assertIn('test', str(waiter.exception()))
  83. def test_negotiate_socks_err(self):
  84. waiter = asyncio.Future(loop=self.loop)
  85. proto = make_base(self.loop, waiter=waiter)
  86. proto.socks_request = fake_coroutine(aiosocks.SocksError('test'))
  87. self.loop.run_until_complete(proto.negotiate(None, None))
  88. self.assertIn('Can not connect to', str(waiter.exception()))
  89. def test_negotiate_without_app_proto(self):
  90. waiter = asyncio.Future(loop=self.loop)
  91. proto = make_base(self.loop, waiter=waiter)
  92. proto.socks_request = fake_coroutine((None, None))
  93. proto._transport = True
  94. self.loop.run_until_complete(proto.negotiate(None, None))
  95. self.assertTrue(waiter.done())
  96. def test_negotiate_with_app_proto(self):
  97. waiter = asyncio.Future(loop=self.loop)
  98. proto = make_base(self.loop, waiter=waiter,
  99. ap_factory=lambda: asyncio.Protocol())
  100. proto.socks_request = fake_coroutine((None, None))
  101. self.loop.run_until_complete(proto.negotiate(None, None))
  102. self.assertTrue(waiter.done())
  103. def test_connection_lost(self):
  104. loop_mock = mock.Mock()
  105. app_proto = mock.Mock()
  106. proto = make_base(loop_mock, ap_factory=lambda: app_proto)
  107. # negotiate not completed
  108. proto._negotiate_done = False
  109. proto.connection_lost(True)
  110. self.assertFalse(loop_mock.call_soon.called)
  111. # negotiate successfully competed
  112. loop_mock.reset_mock()
  113. proto._negotiate_done = True
  114. proto.connection_lost(True)
  115. self.assertTrue(loop_mock.call_soon.called)
  116. # don't call connect_lost, if app_protocol == self
  117. # otherwise recursion
  118. loop_mock.reset_mock()
  119. proto = make_base(loop_mock, ap_factory=None)
  120. proto._negotiate_done = True
  121. proto.connection_lost(True)
  122. self.assertFalse(loop_mock.call_soon.called)
  123. def test_pause_writing(self):
  124. loop_mock = mock.Mock()
  125. app_proto = mock.Mock()
  126. proto = make_base(loop_mock, ap_factory=lambda: app_proto)
  127. # negotiate not completed
  128. proto._negotiate_done = False
  129. proto.pause_writing()
  130. self.assertFalse(proto._app_protocol.pause_writing.called)
  131. # negotiate successfully competed
  132. app_proto.reset_mock()
  133. proto._negotiate_done = True
  134. proto.pause_writing()
  135. self.assertTrue(proto._app_protocol.pause_writing.called)
  136. # don't call pause_writing, if app_protocol == self
  137. # otherwise recursion
  138. app_proto.reset_mock()
  139. proto = make_base(loop_mock)
  140. proto._negotiate_done = True
  141. proto.pause_writing()
  142. def test_resume_writing(self):
  143. loop_mock = mock.Mock()
  144. app_proto = mock.Mock()
  145. proto = make_base(loop_mock, ap_factory=lambda: app_proto)
  146. # negotiate not completed
  147. proto._negotiate_done = False
  148. # negotiate not completed
  149. with self.assertRaises(AssertionError):
  150. proto.resume_writing()
  151. self.assertFalse(proto._app_protocol.resume_writing.called)
  152. # negotiate successfully competed
  153. loop_mock.reset_mock()
  154. proto._negotiate_done = True
  155. proto.resume_writing()
  156. self.assertTrue(proto._app_protocol.resume_writing.called)
  157. # don't call resume_writing, if app_protocol == self
  158. # otherwise recursion
  159. loop_mock.reset_mock()
  160. proto = make_base(loop_mock)
  161. proto._negotiate_done = True
  162. with self.assertRaises(AssertionError):
  163. proto.resume_writing()
  164. def test_data_received(self):
  165. loop_mock = mock.Mock()
  166. app_proto = mock.Mock()
  167. proto = make_base(loop_mock, ap_factory=lambda: app_proto)
  168. # negotiate not completed
  169. proto._negotiate_done = False
  170. proto.data_received(b'123')
  171. self.assertFalse(proto._app_protocol.data_received.called)
  172. # negotiate successfully competed
  173. app_proto.reset_mock()
  174. proto._negotiate_done = True
  175. proto.data_received(b'123')
  176. self.assertTrue(proto._app_protocol.data_received.called)
  177. # don't call data_received, if app_protocol == self
  178. # otherwise recursion
  179. loop_mock.reset_mock()
  180. proto = make_base(loop_mock)
  181. proto._negotiate_done = True
  182. proto.data_received(b'123')
  183. def test_eof_received(self):
  184. loop_mock = mock.Mock()
  185. app_proto = mock.Mock()
  186. proto = make_base(loop_mock, ap_factory=lambda: app_proto)
  187. # negotiate not completed
  188. proto._negotiate_done = False
  189. proto.eof_received()
  190. self.assertFalse(proto._app_protocol.eof_received.called)
  191. # negotiate successfully competed
  192. app_proto.reset_mock()
  193. proto._negotiate_done = True
  194. proto.eof_received()
  195. self.assertTrue(proto._app_protocol.eof_received.called)
  196. # don't call pause_writing, if app_protocol == self
  197. # otherwise recursion
  198. app_proto.reset_mock()
  199. proto = make_base(loop_mock)
  200. proto._negotiate_done = True
  201. proto.eof_received()
  202. @mock.patch('aiosocks.protocols.asyncio.Task')
  203. def test_func_negotiate_cb_call(self, task_mock):
  204. loop_mock = mock.Mock()
  205. waiter = mock.Mock()
  206. proto = make_base(loop_mock, waiter=waiter)
  207. proto.socks_request = fake_coroutine((None, None))
  208. proto._negotiate_done_cb = mock.Mock()
  209. self.loop.run_until_complete(proto.negotiate(None, None))
  210. self.assertTrue(proto._negotiate_done_cb.called)
  211. self.assertFalse(task_mock.called)
  212. @mock.patch('aiosocks.protocols.asyncio.Task')
  213. def test_coro_negotiate_cb_call(self, task_mock):
  214. loop_mock = mock.Mock()
  215. waiter = mock.Mock()
  216. proto = make_base(loop_mock, waiter=waiter)
  217. proto.socks_request = fake_coroutine((None, None))
  218. proto._negotiate_done_cb = fake_coroutine(None)
  219. self.loop.run_until_complete(proto.negotiate(None, None))
  220. self.assertTrue(proto._negotiate_done_cb.called)
  221. self.assertTrue(task_mock.called)
  222. class TestSocks4Protocol(unittest.TestCase):
  223. def setUp(self):
  224. self.loop = asyncio.new_event_loop()
  225. asyncio.set_event_loop(None)
  226. def tearDown(self):
  227. self.loop.close()
  228. def test_init(self):
  229. addr = aiosocks.Socks4Addr('localhost', 1080)
  230. auth = aiosocks.Socks4Auth('user')
  231. dst = ('python.org', 80)
  232. with self.assertRaises(ValueError):
  233. aiosocks.Socks4Protocol(None, None, dst, loop=self.loop,
  234. waiter=None, app_protocol_factory=None)
  235. with self.assertRaises(ValueError):
  236. aiosocks.Socks4Protocol(None, auth, dst, loop=self.loop,
  237. waiter=None, app_protocol_factory=None)
  238. with self.assertRaises(ValueError):
  239. aiosocks.Socks4Protocol(aiosocks.Socks5Addr('host'), auth, dst,
  240. loop=self.loop, waiter=None,
  241. app_protocol_factory=None)
  242. with self.assertRaises(ValueError):
  243. aiosocks.Socks4Protocol(addr, aiosocks.Socks5Auth('l', 'p'), dst,
  244. loop=self.loop, waiter=None,
  245. app_protocol_factory=None)
  246. aiosocks.Socks4Protocol(addr, None, dst, loop=self.loop,
  247. waiter=None, app_protocol_factory=None)
  248. aiosocks.Socks4Protocol(addr, auth, dst, loop=self.loop,
  249. waiter=None, app_protocol_factory=None)
  250. def test_dst_domain_with_remote_resolve(self):
  251. proto = make_socks4(self.loop, dst=('python.org', 80),
  252. r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  253. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  254. self.loop.run_until_complete(req)
  255. proto._stream_writer.write.assert_called_with(
  256. b'\x04\x01\x00P\x00\x00\x00\x01user\x00python.org\x00'
  257. )
  258. def test_dst_domain_with_local_resolve(self):
  259. proto = make_socks4(self.loop, dst=('python.org', 80),
  260. rr=False, r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  261. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  262. self.loop.run_until_complete(req)
  263. proto._stream_writer.write.assert_called_with(
  264. b'\x04\x01\x00P\x7f\x00\x00\x01user\x00'
  265. )
  266. def test_dst_ip_with_remote_resolve(self):
  267. proto = make_socks4(self.loop, dst=('127.0.0.1', 8800),
  268. r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  269. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  270. self.loop.run_until_complete(req)
  271. proto._stream_writer.write.assert_called_with(
  272. b'\x04\x01"`\x7f\x00\x00\x01user\x00'
  273. )
  274. def test_dst_ip_with_locale_resolve(self):
  275. proto = make_socks4(self.loop, dst=('127.0.0.1', 8800),
  276. rr=False, r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  277. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  278. self.loop.run_until_complete(req)
  279. proto._stream_writer.write.assert_called_with(
  280. b'\x04\x01"`\x7f\x00\x00\x01user\x00'
  281. )
  282. def test_dst_domain_without_user(self):
  283. proto = make_socks4(self.loop, auth=aiosocks.Socks4Auth(''),
  284. dst=('python.org', 80),
  285. r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  286. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  287. self.loop.run_until_complete(req)
  288. proto._stream_writer.write.assert_called_with(
  289. b'\x04\x01\x00P\x00\x00\x00\x01\x00python.org\x00'
  290. )
  291. def test_dst_ip_without_user(self):
  292. proto = make_socks4(self.loop, auth=aiosocks.Socks4Auth(''),
  293. dst=('127.0.0.1', 8800),
  294. r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  295. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  296. self.loop.run_until_complete(req)
  297. proto._stream_writer.write.assert_called_with(
  298. b'\x04\x01"`\x7f\x00\x00\x01\x00'
  299. )
  300. def test_valid_resp_handling(self):
  301. proto = make_socks4(self.loop, r=b'\x00\x5a\x00P\x7f\x00\x00\x01')
  302. req = ensure_future(
  303. proto.socks_request(c.SOCKS_CMD_CONNECT), loop=self.loop)
  304. self.loop.run_until_complete(req)
  305. self.assertEqual(req.result(), (('python.org', 80), ('127.0.0.1', 80)))
  306. def test_invalid_reply_resp_handling(self):
  307. proto = make_socks4(self.loop, r=b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF')
  308. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  309. with self.assertRaises(aiosocks.InvalidServerReply):
  310. self.loop.run_until_complete(req)
  311. def test_socks_err_resp_handling(self):
  312. proto = make_socks4(self.loop, r=b'\x00\x5b\x00P\x7f\x00\x00\x01')
  313. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  314. with self.assertRaises(aiosocks.SocksError) as cm:
  315. self.loop.run_until_complete(req)
  316. self.assertTrue('0x5b' in str(cm.exception))
  317. def test_unknown_err_resp_handling(self):
  318. proto = make_socks4(self.loop, r=b'\x00\x5e\x00P\x7f\x00\x00\x01')
  319. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  320. with self.assertRaises(aiosocks.SocksError) as cm:
  321. self.loop.run_until_complete(req)
  322. self.assertTrue('Unknown error' in str(cm.exception))
  323. class TestSocks5Protocol(unittest.TestCase):
  324. def setUp(self):
  325. self.loop = asyncio.new_event_loop()
  326. asyncio.set_event_loop(None)
  327. def tearDown(self):
  328. self.loop.close()
  329. def test_init(self):
  330. addr = aiosocks.Socks5Addr('localhost', 1080)
  331. auth = aiosocks.Socks5Auth('user', 'pwd')
  332. dst = ('python.org', 80)
  333. with self.assertRaises(ValueError):
  334. aiosocks.Socks5Protocol(None, None, dst, loop=self.loop,
  335. waiter=None, app_protocol_factory=None)
  336. with self.assertRaises(ValueError):
  337. aiosocks.Socks5Protocol(None, auth, dst, loop=self.loop,
  338. waiter=None, app_protocol_factory=None)
  339. with self.assertRaises(ValueError):
  340. aiosocks.Socks5Protocol(aiosocks.Socks4Addr('host'),
  341. auth, dst, loop=self.loop,
  342. waiter=None, app_protocol_factory=None)
  343. with self.assertRaises(ValueError):
  344. aiosocks.Socks5Protocol(addr, aiosocks.Socks4Auth('l'),
  345. dst, loop=self.loop,
  346. waiter=None, app_protocol_factory=None)
  347. aiosocks.Socks5Protocol(addr, None, dst, loop=self.loop,
  348. waiter=None, app_protocol_factory=None)
  349. aiosocks.Socks5Protocol(addr, auth, dst, loop=self.loop,
  350. waiter=None, app_protocol_factory=None)
  351. def test_auth_inv_srv_ver(self):
  352. proto = make_socks5(self.loop, r=b'\x00\x00')
  353. req = proto.authenticate()
  354. with self.assertRaises(aiosocks.InvalidServerVersion):
  355. self.loop.run_until_complete(req)
  356. def test_auth_no_acceptable_auth_methods(self):
  357. proto = make_socks5(self.loop, r=b'\x05\xFF')
  358. req = proto.authenticate()
  359. with self.assertRaises(aiosocks.NoAcceptableAuthMethods):
  360. self.loop.run_until_complete(req)
  361. def test_auth_unsupported_auth_method(self):
  362. proto = make_socks5(self.loop, r=b'\x05\xF0')
  363. req = proto.authenticate()
  364. with self.assertRaises(aiosocks.InvalidServerReply):
  365. self.loop.run_until_complete(req)
  366. def test_auth_usr_pwd_granted(self):
  367. proto = make_socks5(self.loop, r=(b'\x05\x02', b'\x01\x00',))
  368. self.loop.run_until_complete(proto.authenticate())
  369. proto._stream_writer.write.assert_has_calls([
  370. mock.call(b'\x05\x02\x00\x02'),
  371. mock.call(b'\x01\x04user\x03pwd')
  372. ])
  373. def test_auth_invalid_reply(self):
  374. proto = make_socks5(self.loop, r=(b'\x05\x02', b'\x00\x00',))
  375. req = proto.authenticate()
  376. with self.assertRaises(aiosocks.InvalidServerReply):
  377. self.loop.run_until_complete(req)
  378. def test_auth_access_denied(self):
  379. proto = make_socks5(self.loop, r=(b'\x05\x02', b'\x01\x01',))
  380. req = proto.authenticate()
  381. with self.assertRaises(aiosocks.LoginAuthenticationFailed):
  382. self.loop.run_until_complete(req)
  383. def test_auth_anonymous_granted(self):
  384. proto = make_socks5(self.loop, r=b'\x05\x00')
  385. req = proto.authenticate()
  386. self.loop.run_until_complete(req)
  387. def test_wr_addr_ipv4(self):
  388. proto = make_socks5(self.loop)
  389. req = proto.write_address('127.0.0.1', 80)
  390. self.loop.run_until_complete(req)
  391. proto._stream_writer.write.assert_called_with(
  392. b'\x01\x7f\x00\x00\x01\x00P')
  393. def test_wr_addr_ipv6(self):
  394. proto = make_socks5(self.loop)
  395. req = proto.write_address(
  396. '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d', 80)
  397. self.loop.run_until_complete(req)
  398. proto._stream_writer.write.assert_called_with(
  399. b'\x04 \x01\r\xb8\x11\xa3\t\xd7\x1f4\x8a.\x07\xa0v]\x00P')
  400. def test_wr_addr_domain_with_remote_resolve(self):
  401. proto = make_socks5(self.loop)
  402. req = proto.write_address('python.org', 80)
  403. self.loop.run_until_complete(req)
  404. proto._stream_writer.write.assert_called_with(b'\x03\npython.org\x00P')
  405. def test_wr_addr_domain_with_locale_resolve(self):
  406. proto = make_socks5(self.loop, rr=False)
  407. req = proto.write_address('python.org', 80)
  408. self.loop.run_until_complete(req)
  409. proto._stream_writer.write.assert_called_with(
  410. b'\x01\x7f\x00\x00\x01\x00P')
  411. def test_rd_addr_ipv4(self):
  412. proto = make_socks5(
  413. self.loop, r=[b'\x01', b'\x7f\x00\x00\x01', b'\x00P'])
  414. req = ensure_future(proto.read_address(), loop=self.loop)
  415. self.loop.run_until_complete(req)
  416. self.assertEqual(req.result(), ('127.0.0.1', 80))
  417. def test_rd_addr_ipv6(self):
  418. resp = [
  419. b'\x04',
  420. b' \x01\r\xb8\x11\xa3\t\xd7\x1f4\x8a.\x07\xa0v]',
  421. b'\x00P'
  422. ]
  423. proto = make_socks5(self.loop, r=resp)
  424. req = ensure_future(proto.read_address(), loop=self.loop)
  425. self.loop.run_until_complete(req)
  426. self.assertEqual(
  427. req.result(), ('2001:db8:11a3:9d7:1f34:8a2e:7a0:765d', 80))
  428. def test_rd_addr_domain(self):
  429. proto = make_socks5(
  430. self.loop, r=[b'\x03', b'\n', b'python.org', b'\x00P'])
  431. req = ensure_future(proto.read_address(), loop=self.loop)
  432. self.loop.run_until_complete(req)
  433. self.assertEqual(req.result(), (b'python.org', 80))
  434. def test_socks_req_inv_ver(self):
  435. proto = make_socks5(self.loop, r=[b'\x05\x00', b'\x04\x00\x00'])
  436. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  437. with self.assertRaises(aiosocks.InvalidServerVersion):
  438. self.loop.run_until_complete(req)
  439. def test_socks_req_socks_srv_err(self):
  440. proto = make_socks5(self.loop, r=[b'\x05\x00', b'\x05\x02\x00'])
  441. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  442. with self.assertRaises(aiosocks.SocksError) as ct:
  443. self.loop.run_until_complete(req)
  444. self.assertTrue(
  445. 'Connection not allowed by ruleset' in str(ct.exception))
  446. def test_socks_req_unknown_err(self):
  447. proto = make_socks5(self.loop, r=[b'\x05\x00', b'\x05\xFF\x00'])
  448. req = proto.socks_request(c.SOCKS_CMD_CONNECT)
  449. with self.assertRaises(aiosocks.SocksError) as ct:
  450. self.loop.run_until_complete(req)
  451. self.assertTrue('Unknown error' in str(ct.exception))
  452. def test_socks_req_cmd_granted(self):
  453. # cmd granted
  454. resp = [b'\x05\x00',
  455. b'\x05\x00\x00',
  456. b'\x01', b'\x7f\x00\x00\x01',
  457. b'\x00P']
  458. proto = make_socks5(self.loop, r=resp)
  459. req = ensure_future(proto.socks_request(c.SOCKS_CMD_CONNECT),
  460. loop=self.loop)
  461. self.loop.run_until_complete(req)
  462. self.assertEqual(req.result(), (('python.org', 80), ('127.0.0.1', 80)))
  463. proto._stream_writer.write.assert_has_calls([
  464. mock.call(b'\x05\x02\x00\x02'),
  465. mock.call(b'\x05\x01\x00'),
  466. mock.call(b'\x03\npython.org\x00P')
  467. ])