1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-06 22:22:38 +03:00

Clone Quic handshake packets

Summary:
On loss timer, currently we knock all handshake packets out of the OP
list and resend everything. This means miss RTT sampling opportunities during
handshake if loss timer fires, and given our initial loss timer is likely not a
good fit for many networks, it probably fires a lot.

This diff keeps handshake packets in the OP list, and add packet cloning
support to handshake packets so we can clone them and send as probes.

With this, the handshake alarm is finally removed. PTO will take care of all
packet number space.

The diff also fixes a bug in the CloningScheduler where we missed cipher
overhead setting. That broke a few unit tests once we started to clone
handshake packets.

The writeProbingDataToSocket API is also changed to support passing a token to
it so when we clone Initial, token is added correctly. This is because during
packet cloning, we only clone frames. Headers are fresh built.

The diff also changed the cloning behavior when there is only one outstanding
packet. Currently we clone it twice and send two packets. There is no point of
doing that. Now when loss timer fires and when there is only one outstanding
packet, we only clone once.

The PacketEvent, which was an alias of PacketNumber, is now a real type that
has both PacketNumber and PacketNumberSpace to support cloning of handshake
packets. I think in the long term we should refactor PacketNumber itself into a
real type.

Reviewed By: mjoras

Differential Revision: D19863693

fbshipit-source-id: e427bb392021445a9388c15e7ea807852ddcbd08
This commit is contained in:
Yang Chi
2020-06-18 15:28:59 -07:00
committed by Facebook GitHub Bot
parent 4790a5792d
commit b8fef40c6d
22 changed files with 556 additions and 480 deletions

View File

@@ -629,7 +629,8 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
initialStream->writeBuffer.append(data->clone());
updateConnection(
*conn, folly::none, packet.packet, TimePoint(), getEncodedSize(packet));
EXPECT_EQ(1, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(1, conn->outstandings.initialPacketsCount);
EXPECT_EQ(0, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(1, conn->outstandings.packets.size());
EXPECT_EQ(1, initialStream->retransmissionBuffer.size());
@@ -642,7 +643,8 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
initialStream->writeBuffer.append(data->clone());
updateConnection(
*conn, folly::none, packet.packet, TimePoint(), getEncodedSize(packet));
EXPECT_EQ(2, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(2, conn->outstandings.initialPacketsCount);
EXPECT_EQ(0, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(2, conn->outstandings.packets.size());
EXPECT_EQ(3, initialStream->retransmissionBuffer.size());
EXPECT_TRUE(initialStream->writeBuffer.empty());
@@ -654,7 +656,7 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
initialStream->retransmissionBuffer.erase(0);
initialStream->lossBuffer.emplace_back(std::move(firstBuf), 0, false);
conn->outstandings.packets.pop_front();
conn->outstandings.handshakePacketsCount--;
conn->outstandings.initialPacketsCount--;
auto handshakeStream =
getCryptoStream(*conn->cryptoState, EncryptionLevel::Handshake);
@@ -666,7 +668,8 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
handshakeStream->writeBuffer.append(data->clone());
updateConnection(
*conn, folly::none, packet.packet, TimePoint(), getEncodedSize(packet));
EXPECT_EQ(2, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(1, conn->outstandings.initialPacketsCount);
EXPECT_EQ(1, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(2, conn->outstandings.packets.size());
EXPECT_EQ(1, handshakeStream->retransmissionBuffer.size());
@@ -676,7 +679,8 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
handshakeStream->writeBuffer.append(data->clone());
updateConnection(
*conn, folly::none, packet.packet, TimePoint(), getEncodedSize(packet));
EXPECT_EQ(3, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(1, conn->outstandings.initialPacketsCount);
EXPECT_EQ(2, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(3, conn->outstandings.packets.size());
EXPECT_EQ(2, handshakeStream->retransmissionBuffer.size());
EXPECT_TRUE(handshakeStream->writeBuffer.empty());
@@ -695,6 +699,7 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
conn->outstandings.handshakePacketsCount--;
implicitAckCryptoStream(*conn, EncryptionLevel::Initial);
EXPECT_EQ(0, conn->outstandings.initialPacketsCount);
EXPECT_EQ(1, conn->outstandings.handshakePacketsCount);
EXPECT_EQ(1, conn->outstandings.packets.size());
EXPECT_TRUE(initialStream->retransmissionBuffer.empty());
@@ -702,6 +707,7 @@ TEST_F(QuicTransportFunctionsTest, TestImplicitAck) {
EXPECT_TRUE(initialStream->lossBuffer.empty());
implicitAckCryptoStream(*conn, EncryptionLevel::Handshake);
EXPECT_EQ(0, conn->outstandings.initialPacketsCount);
EXPECT_EQ(0, conn->outstandings.handshakePacketsCount);
EXPECT_TRUE(conn->outstandings.packets.empty());
EXPECT_TRUE(handshakeStream->retransmissionBuffer.empty());
@@ -960,7 +966,7 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnectionWithCloneResult) {
MaxDataFrame maxDataFrame(maxDataAmt);
conn->pendingEvents.connWindowUpdate = true;
writePacket.frames.push_back(std::move(maxDataFrame));
PacketEvent event = 1;
PacketEvent event(PacketNumberSpace::AppData, 1);
conn->outstandings.packetEvents.insert(event);
auto futureMoment = thisMoment + 50ms;
MockClock::mockNow = [=]() { return futureMoment; };
@@ -1960,7 +1966,7 @@ TEST_F(QuicTransportFunctionsTest, UpdateConnectionCloneCounter) {
MaxDataFrame(conn->flowControlState.advertisedMaxOffset);
conn->pendingEvents.connWindowUpdate = true;
packet.packet.frames.emplace_back(connWindowUpdate);
PacketEvent packetEvent = 100;
PacketEvent packetEvent(PacketNumberSpace::AppData, 100);
conn->outstandings.packetEvents.insert(packetEvent);
updateConnection(*conn, packetEvent, packet.packet, TimePoint(), 123);
EXPECT_EQ(1, conn->outstandings.clonedPacketsCount);
@@ -1982,7 +1988,9 @@ TEST_F(QuicTransportFunctionsTest, ClearBlockedFromPendingEvents) {
TEST_F(QuicTransportFunctionsTest, ClonedBlocked) {
auto conn = createConn();
auto packetEvent = conn->ackStates.appDataAckState.nextPacketNum;
PacketEvent packetEvent(
PacketNumberSpace::AppData,
conn->ackStates.appDataAckState.nextPacketNum);
auto packet = buildEmptyPacket(*conn, PacketNumberSpace::AppData);
auto stream = conn->streamManager->createNextBidirectionalStream().value();
StreamDataBlockedFrame blockedFrame(stream->id, 1000);
@@ -2043,7 +2051,9 @@ TEST_F(QuicTransportFunctionsTest, ClearRstFromPendingEvents) {
TEST_F(QuicTransportFunctionsTest, ClonedRst) {
auto conn = createConn();
auto packetEvent = conn->ackStates.appDataAckState.nextPacketNum;
PacketEvent packetEvent(
PacketNumberSpace::AppData,
conn->ackStates.appDataAckState.nextPacketNum);
auto stream = conn->streamManager->createNextBidirectionalStream().value();
auto packet = buildEmptyPacket(*conn, PacketNumberSpace::AppData);
RstStreamFrame rstStreamFrame(
@@ -2073,7 +2083,7 @@ TEST_F(QuicTransportFunctionsTest, TimeoutBasedRetxCountUpdate) {
RstStreamFrame rstStreamFrame(
stream->id, GenericApplicationErrorCode::UNKNOWN, 0);
packet.packet.frames.push_back(rstStreamFrame);
PacketEvent packetEvent = 100;
PacketEvent packetEvent(PacketNumberSpace::AppData, 100);
conn->outstandings.packetEvents.insert(packetEvent);
updateConnection(*conn, packetEvent, packet.packet, TimePoint(), 500);
EXPECT_EQ(247, conn->lossState.timeoutBasedRtxCount);
@@ -2408,20 +2418,16 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) {
StreamFrameScheduler streamScheduler(*conn);
ASSERT_FALSE(streamScheduler.hasPendingData());
// The last packet may not be a full packet
auto lastPacketSize = conn->outstandings.packets.back().encodedSize;
size_t expectedOutstandingPacketsCount = 5;
if (lastPacketSize < conn->udpSendPacketLen) {
expectedOutstandingPacketsCount++;
}
// The first packet has be a full packet
auto firstPacketSize = conn->outstandings.packets.front().encodedSize;
auto outstandingPacketsCount = conn->outstandings.packets.size();
ASSERT_EQ(firstPacketSize, conn->udpSendPacketLen);
EXPECT_CALL(mockSock, write(_, _))
.Times(1)
.WillOnce(Invoke([&](const folly::SocketAddress&,
const std::unique_ptr<folly::IOBuf>& buf) {
EXPECT_FALSE(buf->isChained());
// If the last packet isn't full, it may have the stream length field
// but the clone won't have it.
EXPECT_LE(buf->length(), lastPacketSize);
EXPECT_EQ(buf->length(), firstPacketSize);
return buf->length();
}));
writeProbingDataToSocketForTest(
@@ -2431,56 +2437,11 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) {
*aead,
*headerCipher,
getVersion(*conn));
EXPECT_EQ(
conn->outstandings.packets.size(), expectedOutstandingPacketsCount + 1);
EXPECT_EQ(conn->outstandings.packets.size(), outstandingPacketsCount + 1);
EXPECT_EQ(0, bufPtr->length());
EXPECT_EQ(0, bufPtr->headroom());
// Clone again, this time 2 pacckets.
if (lastPacketSize < conn->udpSendPacketLen) {
EXPECT_CALL(mockSock, writeGSO(_, _, _))
.Times(1)
.WillOnce(Invoke([&](const folly::SocketAddress&,
const std::unique_ptr<folly::IOBuf>& buf,
int gso) {
EXPECT_FALSE(buf->isChained());
EXPECT_LE(gso, lastPacketSize);
EXPECT_LE(buf->length(), lastPacketSize * 2);
return buf->length();
}));
} else {
EXPECT_CALL(mockSock, writeGSO(_, _, _))
.Times(1)
.WillOnce(Invoke([&](const folly::SocketAddress&,
const std::unique_ptr<folly::IOBuf>& buf,
int gso) {
EXPECT_FALSE(buf->isChained());
EXPECT_EQ(conn->udpSendPacketLen, gso);
EXPECT_EQ(buf->length(), conn->udpSendPacketLen * 4);
return buf->length();
}));
}
writeProbingDataToSocketForTest(
mockSock,
*conn,
2 /* probesToSend */,
*aead,
*headerCipher,
getVersion(*conn));
EXPECT_EQ(0, bufPtr->length());
EXPECT_EQ(0, bufPtr->headroom());
EXPECT_EQ(
conn->outstandings.packets.size(), expectedOutstandingPacketsCount + 3);
// Clear out all the small packets:
while (conn->outstandings.packets.back().encodedSize <
conn->udpSendPacketLen) {
conn->outstandings.packets.pop_back();
}
ASSERT_FALSE(conn->outstandings.packets.empty());
auto currentOutstandingPackets = conn->outstandings.packets.size();
// Clone 2 full size packets
EXPECT_CALL(mockSock, writeGSO(_, _, _))
.Times(1)
.WillOnce(Invoke([&](const folly::SocketAddress&,
@@ -2498,9 +2459,9 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) {
*aead,
*headerCipher,
getVersion(*conn));
EXPECT_EQ(conn->outstandings.packets.size(), currentOutstandingPackets + 2);
EXPECT_EQ(0, bufPtr->length());
EXPECT_EQ(0, bufPtr->headroom());
EXPECT_EQ(conn->outstandings.packets.size(), outstandingPacketsCount + 3);
}
} // namespace test