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

Reset is a unidirectional event

Summary:
Sending a reset shouldn't affect read states. Receiving a reset
shouldn't affect send states.

Reviewed By: afrind

Differential Revision: D15578287

fbshipit-source-id: 65c5e30666fd9e4c295317ba4c3e0653edbb78ff
This commit is contained in:
Yang Chi
2019-05-31 13:23:35 -07:00
committed by Facebook Github Bot
parent fc0c10c830
commit 75ab1ce6d5
6 changed files with 44 additions and 36 deletions

View File

@@ -1188,6 +1188,37 @@ TEST_F(QuicTransportTest, SendPathResponse) {
EXPECT_TRUE(foundPathResponse); EXPECT_TRUE(foundPathResponse);
} }
TEST_F(QuicTransportTest, CloneAfterRecvReset) {
auto& conn = transport_->getConnectionState();
auto streamId = transport_->createBidirectionalStream().value();
transport_->writeChain(streamId, IOBuf::create(0), true, false);
loopForWrites();
EXPECT_EQ(1, conn.outstandingPackets.size());
auto stream = conn.streamManager->getStream(streamId);
EXPECT_EQ(1, stream->retransmissionBuffer.size());
EXPECT_EQ(0, stream->retransmissionBuffer.back().offset);
EXPECT_EQ(0, stream->retransmissionBuffer.back().data.chainLength());
EXPECT_TRUE(stream->retransmissionBuffer.back().eof);
EXPECT_TRUE(stream->lossBuffer.empty());
EXPECT_EQ(0, stream->writeBuffer.chainLength());
EXPECT_EQ(1, stream->currentWriteOffset);
EXPECT_EQ(0, *stream->finalWriteOffset);
RstStreamFrame rstFrame(streamId, GenericApplicationErrorCode::UNKNOWN, 0);
invokeStreamReceiveStateMachine(conn, *stream, std::move(rstFrame));
// This will clone twice. :/ Maybe we should change this to clone only once in
// the future, thus the EXPECT were written with LT and LE. But it will clone
// for sure and we shouldn't crash.
transport_->lossTimeout().timeoutExpired();
EXPECT_LT(1, conn.outstandingPackets.size());
size_t cloneCounter = std::count_if(
conn.outstandingPackets.begin(),
conn.outstandingPackets.end(),
[](const auto& packet) { return packet.associatedEvent.hasValue(); });
EXPECT_LE(1, cloneCounter);
}
TEST_F(QuicTransportTest, ClonePathResponse) { TEST_F(QuicTransportTest, ClonePathResponse) {
auto& conn = transport_->getConnectionState(); auto& conn = transport_->getConnectionState();
// knock every handshake outstanding packets out // knock every handshake outstanding packets out

View File

@@ -3500,7 +3500,8 @@ TEST_F(QuicClientTransportAfterStartTest, SendReset) {
const auto& readCbs = client->getReadCallbacks(); const auto& readCbs = client->getReadCallbacks();
const auto& conn = client->getConn(); const auto& conn = client->getConn();
EXPECT_EQ(0, readCbs.count(streamId)); // ReadCallbacks are not affected by reseting send state
EXPECT_EQ(1, readCbs.count(streamId));
// readable list can still be populated after a reset. // readable list can still be populated after a reset.
EXPECT_FALSE(conn.streamManager->writableContains(streamId)); EXPECT_FALSE(conn.streamManager->writableContains(streamId));
auto packet = packetToBuf(createAckPacket( auto packet = packetToBuf(createAckPacket(
@@ -3605,7 +3606,8 @@ TEST_F(QuicClientTransportAfterStartTest, SendResetAfterEom) {
verifyShortPackets(sentPackets); verifyShortPackets(sentPackets);
const auto& readCbs = client->getReadCallbacks(); const auto& readCbs = client->getReadCallbacks();
const auto& conn = client->getConn(); const auto& conn = client->getConn();
EXPECT_EQ(readCbs.count(streamId), 0); // ReadCallback are not affected by reseting send state.
EXPECT_EQ(1, readCbs.count(streamId));
// readable list can still be populated after a reset. // readable list can still be populated after a reset.
EXPECT_FALSE(conn.streamManager->writableContains(streamId)); EXPECT_FALSE(conn.streamManager->writableContains(streamId));

View File

@@ -84,10 +84,10 @@ folly::Optional<PacketEvent> PacketRebuilder::rebuildFromPacket(
notPureAck |= ret; notPureAck |= ret;
return ret; return ret;
} }
// If a stream is already Closed, or HalfClosedLocal, we should not // If a stream is already Closed, we should not clone and resend this
// clone and resend this stream data. But should we abort the cloning // stream data. But should we abort the cloning of this packet and
// of this packet and move on to the next packet? I'm gonna err on the // move on to the next packet? I'm gonna err on the aggressive side
// aggressive side for now and call it success. // for now and call it success.
return true; return true;
}, },
[&](const WriteCryptoFrame& cryptoFrame) { [&](const WriteCryptoFrame& cryptoFrame) {

View File

@@ -1208,33 +1208,17 @@ TEST_F(QuicServerTransportTest, RecvRstStreamFrame) {
auto packet = std::move(builder).buildPacket(); auto packet = std::move(builder).buildPacket();
deliverData(packetToBuf(packet)); deliverData(packetToBuf(packet));
// Verify pendingEvents on connection: // Verify stream receive state is cleaned up but send state isn't:
EXPECT_TRUE(stream->streamWriteError.hasValue());
const RstStreamFrame* rstStreamFrame = nullptr;
for (auto& outstandingPacket : server->getNonConstConn().outstandingPackets) {
auto& frames = outstandingPacket.packet.frames;
for (auto& frame : frames) {
auto rstFramePtr = boost::get<RstStreamFrame>(&frame);
if (rstFramePtr && rstFramePtr->streamId == streamId) {
rstStreamFrame = rstFramePtr;
break;
}
}
}
// Verify stream state is cleaned up:
auto updatedStream = auto updatedStream =
server->getNonConstConn().streamManager->findStream(streamId); server->getNonConstConn().streamManager->findStream(streamId);
ASSERT_TRUE(updatedStream); ASSERT_TRUE(updatedStream);
EXPECT_TRUE(updatedStream->readBuffer.empty()); EXPECT_TRUE(updatedStream->readBuffer.empty());
EXPECT_TRUE(updatedStream->writeBuffer.empty()); // We can verify retx buffer isn't empty here. The writeBuffer though could be
EXPECT_TRUE(updatedStream->retransmissionBuffer.empty()); // empty since deliverData can cause a write synchrously.
EXPECT_FALSE(updatedStream->retransmissionBuffer.empty());
EXPECT_EQ( EXPECT_EQ(
words.at(0).length() + words.at(1).length(), words.at(0).length() + words.at(1).length(),
updatedStream->finalReadOffset.value()); updatedStream->finalReadOffset.value());
EXPECT_EQ(
words.at(2).length() + words.at(3).length(),
updatedStream->currentWriteOffset);
// updatedStream still writable since receiving rst has no impact on egress // updatedStream still writable since receiving rst has no impact on egress
EXPECT_TRUE(updatedStream->writable()); EXPECT_TRUE(updatedStream->writable());
} }

View File

@@ -19,7 +19,6 @@ void resetQuicStream(QuicStreamState& stream, ApplicationErrorCode error) {
stream.readBuffer.clear(); stream.readBuffer.clear();
stream.lossBuffer.clear(); stream.lossBuffer.clear();
stream.streamWriteError = error; stream.streamWriteError = error;
stream.streamReadError = error;
stream.conn.streamManager->updateReadableStreams(stream); stream.conn.streamManager->updateReadableStreams(stream);
stream.conn.streamManager->updateWritableStreams(stream); stream.conn.streamManager->updateWritableStreams(stream);
stream.conn.streamManager->updateLossStreams(stream); stream.conn.streamManager->updateLossStreams(stream);
@@ -41,17 +40,10 @@ void onResetQuicStream(QuicStreamState& stream, RstStreamFrame&& frame) {
} }
// Verify that the flow control is consistent. // Verify that the flow control is consistent.
updateFlowControlOnStreamData(stream, stream.maxOffsetObserved, frame.offset); updateFlowControlOnStreamData(stream, stream.maxOffsetObserved, frame.offset);
auto writeBufferLen = stream.writeBuffer.chainLength();
updateFlowControlOnWriteToSocket(stream, writeBufferLen);
// Drop read buffer: // Drop read buffer:
stream.readBuffer.clear(); stream.readBuffer.clear();
// We can also drop retx buffer and writeBuffer:
stream.retransmissionBuffer.clear();
stream.writeBuffer.clear();
stream.lossBuffer.clear();
stream.finalReadOffset = frame.offset; stream.finalReadOffset = frame.offset;
stream.streamReadError = frame.errorCode; stream.streamReadError = frame.errorCode;
stream.streamWriteError = frame.errorCode;
bool appReadAllBytes = stream.finalReadOffset && bool appReadAllBytes = stream.finalReadOffset &&
stream.currentReadOffset > *stream.finalReadOffset; stream.currentReadOffset > *stream.finalReadOffset;
if (!appReadAllBytes) { if (!appReadAllBytes) {

View File

@@ -19,7 +19,7 @@ namespace test {
class StreamStateFunctionsTests : public Test {}; class StreamStateFunctionsTests : public Test {};
TEST_F(StreamStateFunctionsTests, SanityTest) { TEST_F(StreamStateFunctionsTests, BasicResetTest) {
QuicServerConnectionState conn; QuicServerConnectionState conn;
StreamId streamId = 0xbaad; StreamId streamId = 0xbaad;
QuicStreamState stream(streamId, conn); QuicStreamState stream(streamId, conn);
@@ -44,7 +44,6 @@ TEST_F(StreamStateFunctionsTests, SanityTest) {
EXPECT_TRUE(stream.writeBuffer.empty()); EXPECT_TRUE(stream.writeBuffer.empty());
EXPECT_TRUE(stream.retransmissionBuffer.empty()); EXPECT_TRUE(stream.retransmissionBuffer.empty());
EXPECT_TRUE(stream.readBuffer.empty()); EXPECT_TRUE(stream.readBuffer.empty());
EXPECT_TRUE(stream.streamReadError.hasValue());
// The rest are untouched: // The rest are untouched:
EXPECT_EQ(stream.id, streamId); EXPECT_EQ(stream.id, streamId);