def test_bad_cookie(self): client_transport, server_transport = dummy_dtls_transport_pair() client = RTCSctpTransport(client_transport) server = RTCSctpTransport(server_transport) # corrupt cookie real_send_chunk = client._send_chunk async def mock_send_chunk(chunk): if isinstance(chunk, CookieEchoChunk): chunk.body = b'garbage' return await real_send_chunk(chunk) client._send_chunk = mock_send_chunk server.start(client.getCapabilities(), client.port) client.start(server.getCapabilities(), server.port) # check outcome run(asyncio.sleep(0.5)) self.assertEqual(client.state, RTCSctpTransport.State.COOKIE_ECHOED) self.assertEqual(server.state, RTCSctpTransport.State.CLOSED) # shutdown run(client.stop()) run(server.stop()) self.assertEqual(client.state, RTCSctpTransport.State.CLOSED) self.assertEqual(server.state, RTCSctpTransport.State.CLOSED)
def test_t2_expired_when_shutdown_sent(self): async def mock_send_chunk(chunk): pass client_transport = DummyDtlsTransport() client = RTCSctpTransport(client_transport) client._last_received_tsn = 0 client._send_chunk = mock_send_chunk chunk = ShutdownChunk() # fails once client.state = RTCSctpTransport.State.SHUTDOWN_SENT client._t2_start(chunk) client._t2_expired() self.assertEqual(client._t2_failures, 1) self.assertIsNotNone(client._t2_handle) self.assertEqual(client.state, RTCSctpTransport.State.SHUTDOWN_SENT) # fails 10 times client._t2_failures = 9 client._t2_expired() self.assertEqual(client._t2_failures, 10) self.assertIsNotNone(client._t2_handle) self.assertEqual(client.state, RTCSctpTransport.State.SHUTDOWN_SENT) # fails 11 times client._t2_expired() self.assertEqual(client._t2_failures, 11) self.assertIsNone(client._t2_handle) self.assertEqual(client.state, RTCSctpTransport.State.CLOSED)
def test_t3_expired(self): async def mock_send_chunk(chunk): pass async def mock_transmit(): pass client_transport = DummyDtlsTransport() client = RTCSctpTransport(client_transport) client._send_chunk = mock_send_chunk # 1 chunk run(client._send(123, 456, b'M' * USERDATA_MAX_LENGTH)) self.assertIsNotNone(client._t3_handle) self.assertEqual(len(client._outbound_queue), 1) self.assertEqual(client._outbound_queue_pos, 1) # t3 expires client._transmit = mock_transmit client._t3_expired() self.assertIsNone(client._t3_handle) self.assertEqual(len(client._outbound_queue), 1) self.assertEqual(client._outbound_queue_pos, 0) # let async code complete run(asyncio.sleep(0))
def test_send_sack(self): sack = None async def mock_send_chunk(c): nonlocal sack sack = c client_transport = DummyDtlsTransport() client = RTCSctpTransport(client_transport) client._last_received_tsn = 123 client._send_chunk = mock_send_chunk run(client._send_sack()) self.assertIsNotNone(sack) self.assertEqual(sack.duplicates, []) self.assertEqual(sack.gaps, []) self.assertEqual(sack.cumulative_tsn, 123)
def test_send_data(self): async def mock_send_chunk(chunk): pass client_transport = DummyDtlsTransport() client = RTCSctpTransport(client_transport) client._send_chunk = mock_send_chunk # no data run(client._transmit()) self.assertIsNone(client._t3_handle) self.assertEqual(client._outbound_queue_pos, 0) # 1 chunk run(client._send(123, 456, b'M' * USERDATA_MAX_LENGTH)) self.assertIsNotNone(client._t3_handle) self.assertEqual(len(client._outbound_queue), 1) self.assertEqual(client._outbound_queue_pos, 1)
def test_send_sack_with_gaps(self): sack = None async def mock_send_chunk(c): nonlocal sack sack = c client_transport = DummyDtlsTransport() client = RTCSctpTransport(client_transport) client._last_received_tsn = 12 client._sack_misordered = [14, 15, 17] client._send_chunk = mock_send_chunk run(client._send_sack()) self.assertIsNotNone(sack) self.assertEqual(sack.duplicates, []) self.assertEqual(sack.gaps, [(2, 3), (5, 5)]) self.assertEqual(sack.cumulative_tsn, 12)
def test_send_data_over_cwnd(self): async def mock_send_chunk(chunk): pass client_transport = DummyDtlsTransport() client = RTCSctpTransport(client_transport) client._send_chunk = mock_send_chunk client._ssthresh = 131072 # STEP 1 - queue 4 chunks, but cwnd only allows 3 run(client._send(123, 456, b'M' * USERDATA_MAX_LENGTH * 4)) # T3 timer was started self.assertIsNotNone(client._t3_handle) self.assertEqual(len(client._outbound_queue), 4) self.assertEqual(client._outbound_queue_pos, 3) # STEP 2 - sack comes in acknowledging 2 chunks previous_timer = client._t3_handle sack = SackChunk() sack.cumulative_tsn = client._outbound_queue[1].tsn run(client._receive_chunk(sack)) # T3 timer was restarted self.assertIsNotNone(client._t3_handle) self.assertNotEqual(client._t3_handle, previous_timer) self.assertEqual(len(client._outbound_queue), 2) self.assertEqual(client._outbound_queue_pos, 2) # STEP 3 - sack comes in acknowledging 2 more chunks sack = SackChunk() sack.cumulative_tsn = client._outbound_queue[1].tsn run(client._receive_chunk(sack)) # T3 timer was stopped self.assertIsNone(client._t3_handle) self.assertEqual(len(client._outbound_queue), 0) self.assertEqual(client._outbound_queue_pos, 0)
def test_receive_shutdown(self): async def mock_send_chunk(chunk): pass client_transport, _ = dummy_dtls_transport_pair() client = RTCSctpTransport(client_transport) client._last_received_tsn = 0 client._send_chunk = mock_send_chunk client.state = RTCSctpTransport.State.ESTABLISHED # receive shutdown chunk = ShutdownChunk() chunk.cumulative_tsn = tsn_minus_one(client._last_sacked_tsn) run(client._receive_chunk(chunk)) self.assertEqual(client.state, RTCSctpTransport.State.SHUTDOWN_ACK_SENT) # receive shutdown complete chunk = ShutdownCompleteChunk() run(client._receive_chunk(chunk)) self.assertEqual(client.state, RTCSctpTransport.State.CLOSED)