def test_receive_data_out_of_order(self): client_transport, _ = dummy_dtls_transport_pair() client = RTCSctpTransport(client_transport) client._last_received_tsn = 0 # build chunks chunks = [] chunk = DataChunk(flags=SCTP_DATA_FIRST_FRAG) chunk.user_data = b'foo' chunk.tsn = 1 chunks.append(chunk) chunk = DataChunk() chunk.user_data = b'bar' chunk.tsn = 2 chunks.append(chunk) chunk = DataChunk(flags=SCTP_DATA_LAST_FRAG) chunk.user_data = b'baz' chunk.tsn = 3 chunks.append(chunk) # receive first chunk run(client._receive_chunk(chunks[0])) self.assertEqual(client._sack_needed, True) self.assertEqual(client._sack_duplicates, []) self.assertEqual(client._sack_misordered, set()) self.assertEqual(client._last_received_tsn, 1) client._sack_needed = False # receive last chunk run(client._receive_chunk(chunks[2])) self.assertEqual(client._sack_needed, True) self.assertEqual(client._sack_duplicates, []) self.assertEqual(client._sack_misordered, set([3])) self.assertEqual(client._last_received_tsn, 1) client._sack_needed = False # receive middle chunk run(client._receive_chunk(chunks[1])) self.assertEqual(client._sack_needed, True) self.assertEqual(client._sack_duplicates, []) self.assertEqual(client._sack_misordered, set([])) self.assertEqual(client._last_received_tsn, 3) client._sack_needed = False # receive last chunk again run(client._receive_chunk(chunks[2])) self.assertEqual(client._sack_needed, True) self.assertEqual(client._sack_duplicates, [3]) self.assertEqual(client._sack_misordered, set([])) self.assertEqual(client._last_received_tsn, 3) client._sack_needed = False
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_data(self): client_transport, _ = dummy_dtls_transport_pair() client = RTCSctpTransport(client_transport) client._last_received_tsn = 0 # receive chunk chunk = DataChunk(flags=(SCTP_DATA_FIRST_FRAG | SCTP_DATA_LAST_FRAG)) chunk.user_data = b'foo' chunk.tsn = 1 run(client._receive_chunk(chunk)) self.assertEqual(client._sack_needed, True) self.assertEqual(client._sack_duplicates, []) self.assertEqual(client._last_received_tsn, 1) client._sack_needed = False # receive chunk again run(client._receive_chunk(chunk)) self.assertEqual(client._sack_needed, True) self.assertEqual(client._sack_duplicates, [1]) self.assertEqual(client._last_received_tsn, 1)
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)
def test_receive_sack_discard(self): client_transport, _ = dummy_dtls_transport_pair() client = RTCSctpTransport(client_transport) client._last_received_tsn = 0 # receive sack sack_point = client._last_sacked_tsn chunk = SackChunk() chunk.cumulative_tsn = tsn_minus_one(sack_point) run(client._receive_chunk(chunk)) # sack point must not changed self.assertEqual(client._last_sacked_tsn, sack_point)
def test_receive_heartbeat(self): client_transport, server_transport = dummy_dtls_transport_pair() client = RTCSctpTransport(client_transport) client._last_received_tsn = 0 client._remote_port = 5000 # receive heartbeat chunk = HeartbeatChunk() chunk.params.append((1, b'\x01\x02\x03\x04')) chunk.tsn = 1 run(client._receive_chunk(chunk)) # check response data = run(server_transport.recv()) packet = Packet.parse(data) self.assertEqual(len(packet.chunks), 1) self.assertTrue(isinstance(packet.chunks[0], HeartbeatAckChunk)) self.assertEqual(packet.chunks[0].params, [(1, b'\x01\x02\x03\x04')])