Skip to content

Commit

Permalink
[tests] clarify the various handshake loss test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
jlaine committed Feb 9, 2022
1 parent 14adec1 commit da566b8
Showing 1 changed file with 109 additions and 104 deletions.
213 changes: 109 additions & 104 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ def test_connect_with_cipher_suite_chacha20(self):
def test_connect_with_loss_1(self):
"""
Check connection is established even in the client's INITIAL is lost.
The client's PTO fires, triggering retransmission.
"""

client_configuration = QuicConfiguration(is_client=True)
Expand Down Expand Up @@ -422,8 +424,12 @@ def test_connect_with_loss_1(self):

def test_connect_with_loss_2(self):
"""
Check connection is established even in the server's HANDSHAKE is lost.
Check connection is established even in the server's INITIAL is lost.
The client receives HANDSHAKE packets before it has the corresponding keys
and decides to retransmit its own CRYPTO to speedup handshake completion.
"""

client_configuration = QuicConfiguration(is_client=True)
client_configuration.load_verify_locations(cafile=SERVER_CACERTFILE)

Expand All @@ -446,7 +452,7 @@ def test_connect_with_loss_2(self):
self.assertEqual(datagram_sizes(items), [1280])
self.assertEqual(client.get_timer(), 0.2)

# server receives INITIAL, sends INITIAL + HANDSHAKE but second datagram is lost
# server receives INITIAL, sends INITIAL + HANDSHAKE but first datagram is lost
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
Expand All @@ -457,67 +463,58 @@ def test_connect_with_loss_2(self):
self.assertEqual(type(server.next_event()), events.ProtocolNegotiated)
self.assertIsNone(server.next_event())

# client only receives first datagram and sends ACKS
# client only receives second datagram, retransmits INITIAL
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
client.receive_datagram(items[1][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [97])
self.assertAlmostEqual(client.get_timer(), 0.325)
self.assertEqual(type(client.next_event()), events.ProtocolNegotiated)
self.assertEqual(datagram_sizes(items), [1280])
self.assertAlmostEqual(client.get_timer(), 0.3)
self.assertIsNone(client.next_event())

# client PTO - HANDSHAKE PING
now = client.get_timer()
client.handle_timer(now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [45])
self.assertAlmostEqual(client.get_timer(), 0.975)

# server receives PING, discards INITIAL and sends ACK
# server receives duplicate INITIAL, retransmits INITIAL + HANDSHAKE
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [48])
self.assertAlmostEqual(server.get_timer(), 0.25)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 3)
self.assertIsNone(server.next_event())

# ACKs are lost, server retransmits HANDSHAKE
now = server.get_timer()
server.handle_timer(now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280, 892])
self.assertAlmostEqual(server.get_timer(), 0.65)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 3)
self.assertIsNone(server.next_event())
self.assertEqual(datagram_sizes(items), [1280, 1068])
self.assertAlmostEqual(server.get_timer(), 0.35)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 1)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 2)

# handshake continues normally
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
client.receive_datagram(items[1][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [329])
self.assertAlmostEqual(client.get_timer(), 0.95)
self.assertEqual(datagram_sizes(items), [376])
self.assertAlmostEqual(client.get_timer(), 0.525)
self.assertEqual(type(client.next_event()), events.ProtocolNegotiated)
self.assertEqual(type(client.next_event()), events.HandshakeCompleted)
self.assertEqual(type(client.next_event()), events.ConnectionIdIssued)

now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [229])
self.assertAlmostEqual(server.get_timer(), 0.675)
self.assertAlmostEqual(server.get_timer(), 0.525)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 0)
self.assertEqual(type(server.next_event()), events.HandshakeCompleted)
self.assertEqual(type(server.next_event()), events.ConnectionIdIssued)

now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [32])
self.assertAlmostEqual(client.get_timer(), 60.4) # idle timeout
self.assertAlmostEqual(client.get_timer(), 60.3) # idle timeout

def test_connect_with_loss_3(self):
"""
Check connection is established even in the server's INITIAL + HANDSHAKE are lost.
The server receives duplicate CRYPTO and decides to retransmit its own
CRYPTO to speedup handshake completion.
"""

client_configuration = QuicConfiguration(is_client=True)
client_configuration.load_verify_locations(cafile=SERVER_CACERTFILE)

Expand Down Expand Up @@ -551,59 +548,54 @@ def test_connect_with_loss_3(self):
self.assertEqual(type(server.next_event()), events.ProtocolNegotiated)
self.assertIsNone(server.next_event())

# client receives INITIAL + HANDSHAKE
# INITIAL + HANDSHAKE are lost, client retransmits INITIAL
now = client.get_timer()
client.handle_timer(now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280])
self.assertAlmostEqual(client.get_timer(), 0.6)
self.assertIsNone(client.next_event())

# server receives duplicate INITIAL, retransmits INITIAL + HANDSHAKE
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280, 1068])
self.assertEqual(server.get_timer(), 0.45)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 1)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 2)

# handshake continues normally
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
client.receive_datagram(items[1][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [376])
self.assertAlmostEqual(client.get_timer(), 0.425)
self.assertAlmostEqual(client.get_timer(), 0.625)
self.assertEqual(type(client.next_event()), events.ProtocolNegotiated)
self.assertEqual(type(client.next_event()), events.HandshakeCompleted)
self.assertEqual(type(client.next_event()), events.ConnectionIdIssued)

# server completes handshake
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [229])
self.assertAlmostEqual(server.get_timer(), 0.425)
self.assertAlmostEqual(server.get_timer(), 0.625)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 0)
self.assertEqual(type(server.next_event()), events.HandshakeCompleted)
self.assertEqual(type(server.next_event()), events.ConnectionIdIssued)

# server PTO - 1-RTT PING
now = server.get_timer()
server.handle_timer(now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [29])
self.assertAlmostEqual(server.get_timer(), 0.975)

# client receives PING, sends ACK
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [32])
self.assertAlmostEqual(client.get_timer(), 0.425)

# server receives ACK, retransmits HANDSHAKE_DONE
now += TICK
self.assertFalse(server._handshake_done_pending)
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
self.assertTrue(server._handshake_done_pending)
items = server.datagrams_to_send(now=now)
self.assertFalse(server._handshake_done_pending)
self.assertEqual(datagram_sizes(items), [224])
self.assertAlmostEqual(client.get_timer(), 60.4) # idle timeout

def test_connect_with_loss_4(self):
"""
Check connection is established even in the server's INITIAL + HANDSHAKE is lost.
The server receives duplicate CRYPTO and decides to retransmit its own
CRYPTO to speedup handshake completion.
Check connection is established even in the server's HANDSHAKE is lost.
"""

client_configuration = QuicConfiguration(is_client=True)
client_configuration.load_verify_locations(cafile=SERVER_CACERTFILE)

Expand All @@ -626,7 +618,7 @@ def test_connect_with_loss_4(self):
self.assertEqual(datagram_sizes(items), [1280])
self.assertEqual(client.get_timer(), 0.2)

# server receives INITIAL, sends INITIAL + HANDSHAKE
# server receives INITIAL, sends INITIAL + HANDSHAKE but second datagram is lost
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
Expand All @@ -637,41 +629,57 @@ def test_connect_with_loss_4(self):
self.assertEqual(type(server.next_event()), events.ProtocolNegotiated)
self.assertIsNone(server.next_event())

# INITIAL + HANDSHAKE are lost, client retransmits INITIAL
# client only receives first datagram and sends ACKS
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [97])
self.assertAlmostEqual(client.get_timer(), 0.325)
self.assertEqual(type(client.next_event()), events.ProtocolNegotiated)
self.assertIsNone(client.next_event())

# client PTO - HANDSHAKE PING
now = client.get_timer()
client.handle_timer(now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280])
self.assertAlmostEqual(client.get_timer(), 0.6)
self.assertIsNone(client.next_event())
self.assertEqual(datagram_sizes(items), [45])
self.assertAlmostEqual(client.get_timer(), 0.975)

# server receives duplicate INITIAL, retransmits INITIAL + HANDSHAKE
# server receives PING, discards INITIAL and sends ACK
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280, 1068])
self.assertEqual(server.get_timer(), 0.45)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 1)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 2)
self.assertEqual(datagram_sizes(items), [48])
self.assertAlmostEqual(server.get_timer(), 0.25)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 3)
self.assertIsNone(server.next_event())

# ACKs are lost, server retransmits HANDSHAKE
now = server.get_timer()
server.handle_timer(now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280, 892])
self.assertAlmostEqual(server.get_timer(), 0.65)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 3)
self.assertIsNone(server.next_event())

# handshake continues normally
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
client.receive_datagram(items[1][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [376])
self.assertAlmostEqual(client.get_timer(), 0.625)
self.assertEqual(type(client.next_event()), events.ProtocolNegotiated)
self.assertEqual(datagram_sizes(items), [329])
self.assertAlmostEqual(client.get_timer(), 0.95)
self.assertEqual(type(client.next_event()), events.HandshakeCompleted)
self.assertEqual(type(client.next_event()), events.ConnectionIdIssued)

now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [229])
self.assertAlmostEqual(server.get_timer(), 0.625)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 0)
self.assertAlmostEqual(server.get_timer(), 0.675)
self.assertEqual(type(server.next_event()), events.HandshakeCompleted)
self.assertEqual(type(server.next_event()), events.ConnectionIdIssued)

Expand All @@ -683,12 +691,8 @@ def test_connect_with_loss_4(self):

def test_connect_with_loss_5(self):
"""
Check connection is established even in the server's INITIAL is lost.
The server receives duplicate CRYPTO and decides to retransmit its own
CRYPTO to speedup handshake completion.
Check connection is established even in the server's HANDSHAKE_DONE is lost.
"""

client_configuration = QuicConfiguration(is_client=True)
client_configuration.load_verify_locations(cafile=SERVER_CACERTFILE)

Expand All @@ -711,7 +715,7 @@ def test_connect_with_loss_5(self):
self.assertEqual(datagram_sizes(items), [1280])
self.assertEqual(client.get_timer(), 0.2)

# server receives INITIAL, sends INITIAL + HANDSHAKE but first datagram is lost
# server receives INITIAL, sends INITIAL + HANDSHAKE
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
Expand All @@ -722,49 +726,50 @@ def test_connect_with_loss_5(self):
self.assertEqual(type(server.next_event()), events.ProtocolNegotiated)
self.assertIsNone(server.next_event())

# client only receives second datagram, retransmits INITIAL
now += TICK
client.receive_datagram(items[1][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280])
self.assertAlmostEqual(client.get_timer(), 0.3)
self.assertIsNone(client.next_event())

# server receives duplicate INITIAL, retransmits INITIAL + HANDSHAKE
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [1280, 1068])
self.assertAlmostEqual(server.get_timer(), 0.35)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 1)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 2)

# handshake continues normally
# client receives INITIAL + HANDSHAKE
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
client.receive_datagram(items[1][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [376])
self.assertAlmostEqual(client.get_timer(), 0.525)
self.assertAlmostEqual(client.get_timer(), 0.425)
self.assertEqual(type(client.next_event()), events.ProtocolNegotiated)
self.assertEqual(type(client.next_event()), events.HandshakeCompleted)
self.assertEqual(type(client.next_event()), events.ConnectionIdIssued)

# server completes handshake, but HANDSHAKE_DONE is lost
now += TICK
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [229])
self.assertAlmostEqual(server.get_timer(), 0.525)
self.assertAlmostEqual(server.get_timer(), 0.425)
self.assertEqual(len(server._loss.spaces[0].sent_packets), 0)
self.assertEqual(len(server._loss.spaces[1].sent_packets), 0)
self.assertEqual(type(server.next_event()), events.HandshakeCompleted)
self.assertEqual(type(server.next_event()), events.ConnectionIdIssued)

# server PTO - 1-RTT PING
now = server.get_timer()
server.handle_timer(now=now)
items = server.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [29])
self.assertAlmostEqual(server.get_timer(), 0.975)

# client receives PING, sends ACK
now += TICK
client.receive_datagram(items[0][0], SERVER_ADDR, now=now)
items = client.datagrams_to_send(now=now)
self.assertEqual(datagram_sizes(items), [32])
self.assertAlmostEqual(client.get_timer(), 60.3) # idle timeout
self.assertAlmostEqual(client.get_timer(), 0.425)

# server receives ACK, retransmits HANDSHAKE_DONE
now += TICK
self.assertFalse(server._handshake_done_pending)
server.receive_datagram(items[0][0], CLIENT_ADDR, now=now)
self.assertTrue(server._handshake_done_pending)
items = server.datagrams_to_send(now=now)
self.assertFalse(server._handshake_done_pending)
self.assertEqual(datagram_sizes(items), [224])

def test_connect_with_no_transport_parameters(self):
def patch(client):
Expand Down

0 comments on commit da566b8

Please sign in to comment.