|
1 | 1 | import asyncio |
2 | 2 | import functools |
3 | 3 | import os |
| 4 | +import random |
4 | 5 | import socket |
5 | 6 | import unittest |
6 | 7 | from unittest import mock |
@@ -1249,6 +1250,62 @@ async def test_repr(self): |
1249 | 1250 | conn._id = 1 |
1250 | 1251 | self.assertEqual(repr(conn), "Connection(1)") |
1251 | 1252 |
|
| 1253 | + @asynctest |
| 1254 | + async def test_connection_ephemeral_ports(self): |
| 1255 | + addresses = ["127.0.0.1"] |
| 1256 | + |
| 1257 | + # Let the OS pick a random port - should always yield a candidate |
| 1258 | + conn1 = ice.Connection(ice_controlling=True) |
| 1259 | + c = await conn1.get_component_candidates(0, addresses) |
| 1260 | + self.assertTrue(c[0].port >= 1 and c[0].port <= 65535) |
| 1261 | + |
| 1262 | + # Try opening a new connection with the same port - should never yield candidates |
| 1263 | + conn2 = ice.Connection(ice_controlling=True, ephemeral_ports=[c[0].port]) |
| 1264 | + c = await conn2.get_component_candidates(0, addresses) |
| 1265 | + self.assertEqual(len(c), 0) # port already in use, no candidates |
| 1266 | + await conn1.close() |
| 1267 | + |
| 1268 | + # Empty set of ports - illegal argument |
| 1269 | + conn3 = ice.Connection(ice_controlling=True, ephemeral_ports=[]) |
| 1270 | + with self.assertRaises(ValueError): |
| 1271 | + await conn3.get_component_candidates(0, addresses) |
| 1272 | + |
| 1273 | + # Range of 100 ports |
| 1274 | + lower = random.randint(1024, 65536 - 100) |
| 1275 | + upper = lower + 100 |
| 1276 | + ports = set(range(lower, upper)) - set([5353]) |
| 1277 | + |
| 1278 | + # Exhaust the range of ports - should always yield candidates |
| 1279 | + conns = [] |
| 1280 | + for i in range(0, len(ports)): |
| 1281 | + conn = ice.Connection(ice_controlling=True, ephemeral_ports=ports) |
| 1282 | + c = await conn.get_component_candidates(i, addresses) |
| 1283 | + if c: |
| 1284 | + self.assertTrue(c[0].port >= lower and c[0].port < upper) |
| 1285 | + conns.append(conn) |
| 1286 | + self.assertGreaterEqual(len(conns), len(ports) - 1) # account for at most 1 port in use by another process |
| 1287 | + |
| 1288 | + # Open one more connection from the same range - should never yield candidates |
| 1289 | + conn = ice.Connection(ice_controlling=True, ephemeral_ports=ports) |
| 1290 | + c = await conn.get_component_candidates(0, addresses) |
| 1291 | + self.assertEqual(len(c), 0) # all ports are exhausted, no candidates |
| 1292 | + |
| 1293 | + # Close one connection and try again - should always yield a candidate |
| 1294 | + await conns.pop().close() |
| 1295 | + conn = ice.Connection(ice_controlling=True, ephemeral_ports=ports) |
| 1296 | + c = await conn.get_component_candidates(0, addresses) |
| 1297 | + self.assertTrue(c[0].port >= lower and c[0].port < upper) |
| 1298 | + await conn.close() |
| 1299 | + |
| 1300 | + # cleanup |
| 1301 | + for conn in conns: |
| 1302 | + await conn.close() |
| 1303 | + |
| 1304 | + # Bind to wildcard local address - should always yield a candidate |
| 1305 | + conn = ice.Connection(ice_controlling=True) |
| 1306 | + c = await conn.get_component_candidates(0, [None]) |
| 1307 | + self.assertTrue(c[0].port >= 1 and c[0].port <= 65535) |
| 1308 | + await conn.close() |
1252 | 1309 |
|
1253 | 1310 | class StunProtocolTest(unittest.TestCase): |
1254 | 1311 | @asynctest |
|
0 commit comments