1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
"""Losses and T3 expirations are counted per association (see sctp_stats.py).
Replayed against aiortc's own RTCSctpTransport: five chunks sent and the first
one lost, so three acknowledgements in a row report it missing and the sender
enters fast recovery, once. Then T3 expires, once.
"""
import asyncio
from aiortc.rtcsctptransport import RTCSctpTransport, SackChunk
from meshbay_node.transport.webrtc import sctp_stats
class _Dtls:
state = "connected"
def _register_data_receiver(self, *a):
pass
class _Pc:
def __init__(self, sctp):
self.sctp = sctp
def _sack(cumulative_tsn: int, gaps: list) -> SackChunk:
sack = SackChunk()
sack.cumulative_tsn = cumulative_tsn % 2**32
sack.gaps = gaps
sack.duplicates = []
sack.advertised_rwnd = 1 << 20
return sack
async def _replay() -> RTCSctpTransport:
s = RTCSctpTransport(_Dtls())
s._association_state = s.State.ESTABLISHED
async def sent(*_a, **_k):
pass
s._send_chunk = sent
for name in ("_t3_start", "_t3_restart", "_t3_cancel"):
setattr(s, name, lambda: None)
s._cwnd = 100_000
for _ in range(5):
await s._send(1, 51, b"x" * 1000)
first = s._sent_queue[0].tsn
for last_seen in (2, 3, 4):
await s._receive_sack_chunk(_sack(first - 1, [(2, last_seen + 1)]))
# Still recovering from the same loss: one episode, not a second.
await s._receive_sack_chunk(_sack(first - 1, [(2, 5)]))
s._t3_expired()
return s
def test_a_loss_and_a_t3_expiry_are_each_counted_once():
s = asyncio.run(_replay())
assert s._mb_losses == 1
assert s._mb_t3 == 1
assert sctp_stats.describe(_Pc(s)).endswith("loss=1 t3=1")
def test_an_association_that_lost_nothing_says_so():
async def fresh() -> RTCSctpTransport:
return RTCSctpTransport(_Dtls())
s = asyncio.run(fresh())
assert sctp_stats.describe(_Pc(s)).endswith("rtt=? loss=0 t3=0")
def test_a_session_without_an_association_adds_nothing():
assert sctp_stats.describe(None) == ""
|