diff --git a/quic/api/QuicTransportBase.cpp b/quic/api/QuicTransportBase.cpp index 6f3454576..6d08b89a3 100644 --- a/quic/api/QuicTransportBase.cpp +++ b/quic/api/QuicTransportBase.cpp @@ -1801,7 +1801,7 @@ folly::Expected QuicTransportBase::resetStream( return folly::makeUnexpected(LocalErrorCode::STREAM_CLOSED); } // Invoke state machine - invokeStreamStateMachine( + invokeStreamSendStateMachine( *conn_, *stream, StreamEvents::SendReset(errorCode)); for (auto pendingResetIt = conn_->pendingEvents.resets.begin(); closeState_ == CloseState::OPEN && diff --git a/quic/api/test/QuicTransportBaseTest.cpp b/quic/api/test/QuicTransportBaseTest.cpp index 9f86fea19..d64e94fd6 100644 --- a/quic/api/test/QuicTransportBaseTest.cpp +++ b/quic/api/test/QuicTransportBaseTest.cpp @@ -281,7 +281,8 @@ class TestQuicTransport void closeStream(StreamId id) { QuicStreamState* stream = conn_->streamManager->getStream(id); - stream->state = StreamStates::Closed{}; + stream->send.state = StreamSendStates::Closed(); + stream->recv.state = StreamReceiveStates::Closed(); conn_->streamManager->addClosed(id); auto deliveryCb = deliveryCallbacks_.find(id); diff --git a/quic/api/test/QuicTransportTest.cpp b/quic/api/test/QuicTransportTest.cpp index 878247e2f..a48c7da7c 100644 --- a/quic/api/test/QuicTransportTest.cpp +++ b/quic/api/test/QuicTransportTest.cpp @@ -775,7 +775,7 @@ TEST_F(QuicTransportTest, RstStream) { auto stream = transport_->getConnectionState().streamManager->findStream(streamId); ASSERT_TRUE(stream); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); EXPECT_TRUE(stream->retransmissionBuffer.empty()); EXPECT_TRUE(stream->writeBuffer.empty()); EXPECT_FALSE(stream->writable()); @@ -1245,7 +1245,7 @@ TEST_F(QuicTransportTest, RstWrittenStream) { } EXPECT_TRUE(foundReset); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); EXPECT_TRUE(stream->retransmissionBuffer.empty()); EXPECT_TRUE(stream->writeBuffer.empty()); EXPECT_FALSE(stream->writable()); @@ -1311,7 +1311,7 @@ TEST_F(QuicTransportTest, WriteAfterSendRst) { transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); EXPECT_TRUE(stream->retransmissionBuffer.empty()); EXPECT_TRUE(stream->writeBuffer.empty()); EXPECT_FALSE(stream->writable()); diff --git a/quic/client/QuicClientTransport.cpp b/quic/client/QuicClientTransport.cpp index 1a2f9ea90..0208c09a2 100644 --- a/quic/client/QuicClientTransport.cpp +++ b/quic/client/QuicClientTransport.cpp @@ -355,7 +355,7 @@ void QuicClientTransport::processPacketData( auto stream = conn_->streamManager->getStream(frame.streamId); if (stream) { - invokeStreamStateMachine( + invokeStreamSendStateMachine( *conn_, *stream, StreamEvents::RstAck(frame)); } }, @@ -368,7 +368,7 @@ void QuicClientTransport::processPacketData( << " closed=" << (ackedStream == nullptr) << " " << *this; if (ackedStream) { - invokeStreamStateMachine( + invokeStreamSendStateMachine( *conn_, *ackedStream, StreamEvents::AckStreamFrame(frame)); @@ -398,7 +398,7 @@ void QuicClientTransport::processPacketData( if (!stream) { return; } - invokeStreamStateMachine(*conn_, *stream, std::move(frame)); + invokeStreamReceiveStateMachine(*conn_, *stream, std::move(frame)); }, [&](ReadCryptoFrame& cryptoFrame) { pktHasRetransmittableData = true; @@ -425,7 +425,7 @@ void QuicClientTransport::processPacketData( << *conn_; return; } - invokeStreamStateMachine(*conn_, *stream, std::move(frame)); + invokeStreamReceiveStateMachine(*conn_, *stream, std::move(frame)); }, [&](MaxDataFrame& connWindowUpdate) { VLOG(10) << "Client received max data offset=" diff --git a/quic/client/state/ClientStateMachine.h b/quic/client/state/ClientStateMachine.h index 005bae6c0..f26071218 100644 --- a/quic/client/state/ClientStateMachine.h +++ b/quic/client/state/ClientStateMachine.h @@ -82,6 +82,7 @@ void ClientInvalidStateHandler(QuicClientConnectionState& state); struct QuicClientStateMachine { using StateData = QuicClientConnectionState; + using UserData = QuicClientConnectionState; static constexpr auto InvalidEventHandler = &ClientInvalidStateHandler; }; diff --git a/quic/codec/QuicPacketRebuilder.h b/quic/codec/QuicPacketRebuilder.h index 9825082fd..a1d96392b 100644 --- a/quic/codec/QuicPacketRebuilder.h +++ b/quic/codec/QuicPacketRebuilder.h @@ -43,10 +43,8 @@ class PacketRebuilder { PacketEvent cloneOutstandingPacket(OutstandingPacket& packet); bool retransmittable(const QuicStreamState& stream) const { - return matchesStates< - StreamStateData, - StreamStates::Open, - StreamStates::HalfClosedRemote>(stream.state); + return matchesStates( + stream.send.state); } Buf cloneCryptoRetransmissionBuffer( diff --git a/quic/codec/test/QuicPacketRebuilderTest.cpp b/quic/codec/test/QuicPacketRebuilderTest.cpp index 74a84c91c..55620a40b 100644 --- a/quic/codec/test/QuicPacketRebuilderTest.cpp +++ b/quic/codec/test/QuicPacketRebuilderTest.cpp @@ -175,7 +175,7 @@ TEST_F(QuicPacketRebuilderTest, RebuildAfterResetStream) { ASSERT_EQ(1, packet1.packet.frames.size()); // Then we reset the stream - invokeStreamStateMachine( + invokeStreamSendStateMachine( conn, *stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); diff --git a/quic/common/test/TestUtils.cpp b/quic/common/test/TestUtils.cpp index 47e48d3b4..e786d55a4 100644 --- a/quic/common/test/TestUtils.cpp +++ b/quic/common/test/TestUtils.cpp @@ -75,7 +75,8 @@ PacketNum rstStreamAndSendPacket( auto aead = createNoOpAead(); auto headerCipher = createNoOpHeaderCipher(); auto version = conn.version.value_or(*conn.originalVersion); - invokeStreamStateMachine(conn, stream, StreamEvents::SendReset(errorCode)); + invokeStreamSendStateMachine( + conn, stream, StreamEvents::SendReset(errorCode)); writeQuicDataToSocket( sock, conn, diff --git a/quic/flowcontrol/test/QuicFlowControlTest.cpp b/quic/flowcontrol/test/QuicFlowControlTest.cpp index 1ce9abf13..820b9a223 100644 --- a/quic/flowcontrol/test/QuicFlowControlTest.cpp +++ b/quic/flowcontrol/test/QuicFlowControlTest.cpp @@ -262,7 +262,8 @@ TEST_F(QuicFlowControlTest, DontSendStreamWindowUpdateTwice) { TEST_F(QuicFlowControlTest, DontSendStreamWindowUpdateOnRemoteHalfClosed) { StreamId id = 3; QuicStreamState stream(id, conn_); - stream.state = StreamStates::HalfClosedRemote(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Closed(); // Should not send window update maybeSendStreamWindowUpdate(stream, Clock::now()); EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id)); @@ -476,7 +477,8 @@ TEST_F(QuicFlowControlTest, LostStreamWindowUpdateHalfClosedRemote) { stream.currentReadOffset = 300; stream.flowControlState.windowSize = 500; stream.flowControlState.advertisedMaxOffset = 400; - stream.state = StreamStates::HalfClosedRemote(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Closed(); // Should not send window update onStreamWindowUpdateLost(stream); diff --git a/quic/loss/test/QuicLossFunctionsTest.cpp b/quic/loss/test/QuicLossFunctionsTest.cpp index 1ba4cc59b..d04cc93ad 100644 --- a/quic/loss/test/QuicLossFunctionsTest.cpp +++ b/quic/loss/test/QuicLossFunctionsTest.cpp @@ -443,7 +443,7 @@ TEST_F(QuicLossFunctionsTest, TestMarkPacketLossAfterStreamReset) { *stream1, *buf, true); - invokeStreamStateMachine( + invokeStreamSendStateMachine( *conn, *stream1, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); diff --git a/quic/server/state/ServerStateMachine.cpp b/quic/server/state/ServerStateMachine.cpp index 6a1edc8ac..693579002 100644 --- a/quic/server/state/ServerStateMachine.cpp +++ b/quic/server/state/ServerStateMachine.cpp @@ -690,7 +690,7 @@ void onServerReadDataFromOpen( auto ackedStream = conn.streamManager->getStream(frame.streamId); if (ackedStream) { - invokeStreamStateMachine( + invokeStreamSendStateMachine( conn, *ackedStream, StreamEvents::AckStreamFrame(frame)); @@ -708,7 +708,7 @@ void onServerReadDataFromOpen( auto stream = conn.streamManager->getStream(frame.streamId); if (stream) { - invokeStreamStateMachine( + invokeStreamSendStateMachine( conn, *stream, StreamEvents::RstAck(frame)); } }, @@ -734,7 +734,7 @@ void onServerReadDataFromOpen( if (!stream) { return; } - invokeStreamStateMachine(conn, *stream, frame); + invokeStreamReceiveStateMachine(conn, *stream, frame); }, [&](ReadCryptoFrame& cryptoFrame) { pktHasRetransmittableData = true; @@ -763,7 +763,7 @@ void onServerReadDataFromOpen( // Ignore data from closed streams that we don't have the // state for any more. if (stream) { - invokeStreamStateMachine(conn, *stream, frame); + invokeStreamReceiveStateMachine(conn, *stream, frame); } }, [&](MaxDataFrame& connWindowUpdate) { diff --git a/quic/server/state/ServerStateMachine.h b/quic/server/state/ServerStateMachine.h index 79511da49..5e3ad3670 100644 --- a/quic/server/state/ServerStateMachine.h +++ b/quic/server/state/ServerStateMachine.h @@ -177,4 +177,3 @@ void onConnectionMigration( QuicServerConnectionState& conn, const folly::SocketAddress& newPeerAddress); } // namespace quic - diff --git a/quic/server/test/QuicServerTransportTest.cpp b/quic/server/test/QuicServerTransportTest.cpp index 419adfe5b..890db1a82 100644 --- a/quic/server/test/QuicServerTransportTest.cpp +++ b/quic/server/test/QuicServerTransportTest.cpp @@ -1079,7 +1079,8 @@ TEST_F(QuicServerTransportTest, TestOpenAckStreamFrame) { EXPECT_EQ( stream->retransmissionBuffer.size(), originalRetransSize - buffersInPacket1); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); + EXPECT_TRUE(isState(stream->recv)); // Dup ack auto packet2 = createAckPacket( @@ -1092,7 +1093,8 @@ TEST_F(QuicServerTransportTest, TestOpenAckStreamFrame) { EXPECT_EQ( stream->retransmissionBuffer.size(), originalRetransSize - buffersInPacket1); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); + EXPECT_TRUE(isState(stream->recv)); IntervalSet acks2 = {{packetNum1, lastPacketNum}}; auto packet3 = createAckPacket( @@ -1103,7 +1105,8 @@ TEST_F(QuicServerTransportTest, TestOpenAckStreamFrame) { deliverData(packetToBuf(packet3)); EXPECT_EQ(stream->retransmissionBuffer.size(), 0); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); + EXPECT_TRUE(isState(stream->recv)); auto empty = IOBuf::create(0); server->writeChain(streamId, std::move(empty), true, false); @@ -1123,7 +1126,8 @@ TEST_F(QuicServerTransportTest, TestOpenAckStreamFrame) { acks3, PacketNumberSpace::AppData); deliverData(packetToBuf(packet4)); - EXPECT_TRUE(isState(*stream)); + EXPECT_TRUE(isState(stream->send)); + EXPECT_TRUE(isState(stream->recv)); } TEST_F(QuicServerTransportTest, RecvRstStreamFrameNonexistClientStream) { @@ -3018,7 +3022,8 @@ TEST_P( 0 /* cipherOverhead */, 0 /* largestAcked */, std::make_pair( - LongHeader::Types::ZeroRtt, server->getConn().supportedVersions[0]))); + LongHeader::Types::ZeroRtt, server->getConn().supportedVersions[0]), + false)); deliverData(std::move(packetData), false); if (GetParam().acceptZeroRtt) { if (!GetParam().chloSync) { @@ -3049,7 +3054,9 @@ TEST_P( streamId, *data, 0 /* cipherOverhead */, - 0 /* largestAcked */)); + 0 /* largestAcked */, + folly::none, + false)); deliverData(std::move(packetData)); EXPECT_EQ(server->getConn().streamManager->streamCount(), 0); EXPECT_EQ(server->getConn().pendingOneRttData->size(), 1); @@ -3076,7 +3083,8 @@ TEST_P( 0 /* cipherOverhead */, 0 /* largestAcked */, std::make_pair( - LongHeader::Types::ZeroRtt, server->getConn().supportedVersions[0]))); + LongHeader::Types::ZeroRtt, server->getConn().supportedVersions[0]), + false)); deliverData(std::move(packetData), false); if (GetParam().acceptZeroRtt) { if (!GetParam().chloSync) { diff --git a/quic/state/QPRFunctions.cpp b/quic/state/QPRFunctions.cpp index 54e741983..1a28c8acc 100644 --- a/quic/state/QPRFunctions.cpp +++ b/quic/state/QPRFunctions.cpp @@ -113,10 +113,8 @@ void onRecvMinStreamDataFrame( PacketNum packetNum) { if (isReceivingStream(stream->conn.nodeType, stream->id) || (isSendingStream(stream->conn.nodeType, stream->id) && - !matchesStates< - StreamStateData, - StreamStates::Open, - StreamStates::HalfClosedRemote>(stream->state))) { + !matchesStates( + stream->send.state))) { throw QuicTransportException( "MinStreamDataFrame on receiving-only stream or " "sending-only stream but not opened", diff --git a/quic/state/QuicStateFunctions.h b/quic/state/QuicStateFunctions.h index 9672d7a29..5b879a33a 100644 --- a/quic/state/QuicStateFunctions.h +++ b/quic/state/QuicStateFunctions.h @@ -36,11 +36,20 @@ void updateRtt( std::chrono::microseconds ackDelay); template -void invokeStreamStateMachine( +void invokeStreamSendStateMachine( QuicConnectionStateBase&, QuicStreamState& stream, Event event) { - invokeHandler(stream, std::move(event)); + invokeHandler(stream.send, std::move(event), stream); +} + +template +void invokeStreamReceiveStateMachine( + QuicConnectionStateBase&, + QuicStreamState& stream, + Event event) { + invokeHandler( + stream.recv, std::move(event), stream); } bool isConnectionPaced(const QuicConnectionStateBase& conn) noexcept; diff --git a/quic/state/QuicStreamManager.cpp b/quic/state/QuicStreamManager.cpp index c4c389ac8..b2a470403 100644 --- a/quic/state/QuicStreamManager.cpp +++ b/quic/state/QuicStreamManager.cpp @@ -305,9 +305,8 @@ void QuicStreamManager::removeClosedStream(StreamId streamId) { return; } VLOG(10) << "Removing closed stream=" << streamId; - bool matchClosedState = - matchesStates(it->second.state); - DCHECK(matchClosedState); + bool inTerminalStates = it->second.inTerminalStates(); + DCHECK(inTerminalStates); readableStreams_.erase(streamId); peekableStreams_.erase(streamId); writableStreams_.erase(streamId); diff --git a/quic/state/SimpleFrameFunctions.cpp b/quic/state/SimpleFrameFunctions.cpp index c00f0826c..21dc8d478 100644 --- a/quic/state/SimpleFrameFunctions.cpp +++ b/quic/state/SimpleFrameFunctions.cpp @@ -129,7 +129,7 @@ bool updateSimpleFrameOnPacketReceived( [&](const StopSendingFrame& frame) { auto stream = conn.streamManager->getStream(frame.streamId); if (stream) { - invokeStreamStateMachine(conn, *stream, frame); + invokeStreamSendStateMachine(conn, *stream, frame); } return true; }, diff --git a/quic/state/StateData.cpp b/quic/state/StateData.cpp index f3553bf49..855bd91b9 100644 --- a/quic/state/StateData.cpp +++ b/quic/state/StateData.cpp @@ -32,6 +32,13 @@ QuicStreamState::QuicStreamState(StreamId idIn, QuicConnectionStateBase& connIn) : isLocalStream(connIn.nodeType, idIn) ? conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote : conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal; + if (isUnidirectionalStream(idIn)) { + if (isLocalStream(connIn.nodeType, idIn)) { + recv.state = StreamReceiveStates::Invalid(); + } else { + send.state = StreamSendStates::Invalid(); + } + } } std::ostream& operator<<(std::ostream& os, const QuicConnectionStateBase& st) { diff --git a/quic/state/StateMachine.h b/quic/state/StateMachine.h index 3ba2e7b09..e1345414f 100644 --- a/quic/state/StateMachine.h +++ b/quic/state/StateMachine.h @@ -28,6 +28,7 @@ namespace quic { * 2. Create a Machine type with an alias of StateData * struct Machine { * using StateData = StateDataType; + * using UserData = UserDataType; * constexpr auto InvalidEventHandler = &InvalidEventHandler; * } * 3. Create types for each state @@ -96,12 +97,15 @@ struct HandlerBase { } }; -#define QUIC_DECLARE_STATE_HANDLER(machine, state, event, ...) \ - template <> \ - class Handler \ - : public HandlerBase { \ - public: \ - static void handle(typename machine::StateData& data, event evt); \ +#define QUIC_DECLARE_STATE_HANDLER(machine, state, event, ...) \ + template <> \ + class Handler \ + : public HandlerBase { \ + public: \ + static void handle( \ + typename machine::StateData& data, \ + event evt, \ + typename machine::UserData& userData); \ }; #define QUIC_DECLARE_STATE_HANDLER_T(state, event, ...) \ @@ -109,7 +113,10 @@ struct HandlerBase { class Handler \ : public HandlerBase { \ public: \ - static void handle(typename Machine::StateData& data, event evt); \ + static void handle( \ + typename Machine::StateData& data, \ + event evt, \ + typename Machine::UserData&); \ template \ static void transit(typename Machine::StateData& data) { \ HandlerBase::template transit< \ @@ -123,28 +130,39 @@ struct HandlerBase { */ template struct Handler : public HandlerBase { - static void handle(typename Machine::StateData& data, Event /*event*/) { - Machine::InvalidEventHandler(data); + static void handle( + typename Machine::StateData& /*state*/, + Event /*event*/, + typename Machine::UserData& userData) { + Machine::InvalidEventHandler(userData); } }; template struct state_visitor : public boost::static_visitor<> { - explicit state_visitor(Event event, typename Machine::StateData& data) - : event_(std::move(event)), data_(data) {} + explicit state_visitor( + Event event, + typename Machine::StateData& data, + typename Machine::UserData& userData) + : event_(std::move(event)), data_(data), userData_(userData) {} template void operator()(const State& /*state*/) { - Handler::handle(data_, std::move(event_)); + Handler::handle(data_, std::move(event_), userData_); } Event event_; typename Machine::StateData& data_; + typename Machine::UserData& userData_; }; template -void invokeHandler(typename Machine::StateData& data, Event event) { - auto visitor = state_visitor(std::move(event), data); +void invokeHandler( + typename Machine::StateData& data, + Event event, + typename Machine::UserData& userData) { + auto visitor = + state_visitor(std::move(event), data, userData); return boost::apply_visitor(visitor, data.state); } } diff --git a/quic/state/StreamData.h b/quic/state/StreamData.h index f1ff5d582..f1083ecac 100644 --- a/quic/state/StreamData.h +++ b/quic/state/StreamData.h @@ -81,38 +81,57 @@ struct QuicStreamLike { struct QuicConnectionStateBase; -struct StreamStates { - // The stream is open +struct StreamSendStates { + // Ready, Send and FinSent are collapsed into Open struct Open {}; - // The stream has closed its write - struct HalfClosedLocal {}; + // RST has been sent + struct ResetSent {}; - // The stream has closed read. - struct HalfClosedRemote {}; - - // The stream is waiting for the ack of the reset stream - struct WaitingForRstAck {}; - - // The stream is now closed. + // All data acked or RST acked. Collapsed Data Received and Reset Received struct Closed {}; + + // Used for peer initiated unidirectional streams + struct Invalid {}; }; -using StreamStateData = boost::variant< - StreamStates::Open, - StreamStates::HalfClosedLocal, - StreamStates::HalfClosedRemote, - StreamStates::WaitingForRstAck, - StreamStates::Closed>; +struct StreamReceiveStates { + // Renamed Receive to Open + struct Open {}; -inline std::string streamStateToString(const StreamStateData& state) { + // All data or rst received + struct Closed {}; + + // Used for self-initiated unidirectional streams + struct Invalid {}; +}; + +using StreamSendStateData = boost::variant< + StreamSendStates::Open, + StreamSendStates::ResetSent, + StreamSendStates::Closed, + StreamSendStates::Invalid>; + +using StreamReceiveStateData = boost::variant< + StreamReceiveStates::Open, + StreamReceiveStates::Closed, + StreamReceiveStates::Invalid>; + +inline std::string streamStateToString(const StreamSendStateData& state) { return folly::variant_match( state, - [](const StreamStates::Open&) { return "Open"; }, - [](const StreamStates::HalfClosedLocal&) { return "HalfClosedLocal"; }, - [](const StreamStates::HalfClosedRemote&) { return "HalfClosedRemote"; }, - [](const StreamStates::WaitingForRstAck&) { return "WaitingForRstAck"; }, - [](const StreamStates::Closed&) { return "Closed"; }); + [](const StreamSendStates::Open&) { return "Open"; }, + [](const StreamSendStates::ResetSent&) { return "ResetSent"; }, + [](const StreamSendStates::Closed&) { return "Closed"; }, + [](const StreamSendStates::Invalid&) { return "Invalid"; }); +} + +inline std::string streamStateToString(const StreamReceiveStateData& state) { + return folly::variant_match( + state, + [](const StreamReceiveStates::Open&) { return "Open"; }, + [](const StreamReceiveStates::Closed&) { return "Closed"; }, + [](const StreamReceiveStates::Invalid&) { return "Invalid"; }); } struct QuicStreamState : public QuicStreamLike { @@ -145,7 +164,12 @@ struct QuicStreamState : public QuicStreamLike { folly::Optional streamWriteError; // State machine data - StreamStateData state{StreamStates::Open()}; + struct Send { + StreamSendStateData state{StreamSendStates::Open()}; + } send; + struct Recv { + StreamReceiveStateData state{StreamReceiveStates::Open()}; + } recv; // The packet number of the latest packet that contains a MaxStreamDataFrame // sent out by us. @@ -167,20 +191,29 @@ struct QuicStreamState : public QuicStreamLike { // lastHolbTime indicates whether the stream is HOL blocked at the moment. uint32_t holbCount{0}; + // Returns true if both send and receive state machines are in a terminal + // state + bool inTerminalStates() const { + return matchesStates< + StreamSendStateData, + StreamSendStates::Closed, + StreamSendStates::Invalid>(send.state) && + matchesStates< + StreamReceiveStateData, + StreamReceiveStates::Closed, + StreamReceiveStates::Invalid>(recv.state); + } + // If the stream is still writable. bool writable() const { - return matchesStates< - StreamStateData, - StreamStates::Open, - StreamStates::HalfClosedRemote>(state) && + return matchesStates( + send.state) && !finalWriteOffset.hasValue(); } bool shouldSendFlowControl() const { - return matchesStates< - StreamStateData, - StreamStates::Open, - StreamStates::HalfClosedLocal>(state); + return matchesStates( + recv.state); } bool hasWritableData() const { diff --git a/quic/state/stream/StreamClosedHandlers.h b/quic/state/stream/StreamClosedHandlers.h index 0bb7529e8..5111f2642 100644 --- a/quic/state/stream/StreamClosedHandlers.h +++ b/quic/state/stream/StreamClosedHandlers.h @@ -12,16 +12,21 @@ #include namespace quic { - -inline void -Handler::handle( +template +void invokeStreamSendStateMachine( + QuicConnectionStateBase&, QuicStreamState& stream, - ReadStreamFrame frame) { - if (isSendingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "ReadStreamFrame on unidirectional sending stream", - TransportErrorCode::STREAM_STATE_ERROR); - } + Event event); + +inline void Handler< + StreamReceiveStateMachine, + StreamReceiveStates::Closed, + ReadStreamFrame>:: + handle( + QuicStreamState::Recv& state, + ReadStreamFrame frame, + QuicStreamState& stream) { + CHECK(!isSendingStream(stream.conn.nodeType, stream.id)); VLOG_IF(10, frame.fin) << "Closed: Received data with fin" << " stream=" << stream.id << " " << stream.conn; appendDataToReadBuffer( @@ -29,64 +34,64 @@ Handler::handle( } inline void -Handler::handle( - QuicStreamState& stream, - StopSendingFrame /* frame */) { - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "StopSendingFrame on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +Handler:: + handle( + QuicStreamState::Send& /*state*/, + StopSendingFrame /*frame*/, + QuicStreamState& /*stream*/) { + // no-op, we're already done sending } -inline void -Handler::handle( - QuicStreamState& stream, - RstStreamFrame rst) { - if (isSendingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "RstStreamFrame on unidirectional sending stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +inline void Handler< + StreamReceiveStateMachine, + StreamReceiveStates::Closed, + RstStreamFrame>:: + handle( + QuicStreamState::Recv& state, + RstStreamFrame rst, + QuicStreamState& stream) { + // TODO: Remove + invokeStreamSendStateMachine( + stream.conn, + stream, + StreamEvents::SendReset(GenericApplicationErrorCode::NO_ERROR)); // This will check whether the reset is still consistent with the stream. onResetQuicStream(stream, std::move(rst)); } inline void Handler< - StreamStateMachine, - StreamStates::Closed, + StreamSendStateMachine, + StreamSendStates::Closed, StreamEvents::AckStreamFrame>:: - handle(QuicStreamState& stream, StreamEvents::AckStreamFrame /*ack*/) { - // do nothing here, we're done with handling our write data. - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "AckStreamFrame on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } + handle( + QuicStreamState::Send& /*state*/, + StreamEvents::AckStreamFrame /*ack*/, + QuicStreamState& stream) { DCHECK(stream.retransmissionBuffer.empty()); DCHECK(stream.writeBuffer.empty()); } -inline void -Handler::handle( - QuicStreamState& stream, - StreamEvents::RstAck /*ack*/) { - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "RstAck on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +inline void Handler< + StreamSendStateMachine, + StreamSendStates::Closed, + StreamEvents::RstAck>:: + handle( + QuicStreamState::Send& /*state*/, + StreamEvents::RstAck /*ack*/, + QuicStreamState& /*stream*/) { // Just discard the ack if we are already in Closed state. } -inline void -Handler:: - handle(QuicStreamState& stream, StreamEvents::SendReset) { - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "SendReset on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +inline void Handler< + StreamSendStateMachine, + StreamSendStates::Closed, + StreamEvents::SendReset>:: + handle( + QuicStreamState::Send& state, + StreamEvents::SendReset, + QuicStreamState& stream) { + // TODO: remove this as a valid state transition + LOG(ERROR) << "Ignoring SendReset from closed state. This may be a bug"; // Discard the send reset. } } // namespace quic diff --git a/quic/state/stream/StreamHalfClosedLocalHandlers.h b/quic/state/stream/StreamHalfClosedLocalHandlers.h deleted file mode 100644 index 32808fa0a..000000000 --- a/quic/state/stream/StreamHalfClosedLocalHandlers.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -// override-include-guard - -#include - -namespace quic { - -inline void -Handler:: - handle(QuicStreamState& stream, RstStreamFrame rst) { - // We transition before invoking onResetQuicStream so that reset stream - // can use the state to make decisions. - VLOG(10) << "HalfClosedLocal: Received reset, transition to closed" - << " stream=" << stream.id << " " << stream.conn; - transit(stream); - onResetQuicStream(stream, std::move(rst)); - stream.conn.streamManager->addClosed(stream.id); -} - -inline void -Handler:: - handle(QuicStreamState& stream, ReadStreamFrame frame) { - VLOG_IF(10, frame.fin) << "HalfClosedLocal: Received data with fin" - << " stream=" << stream.id - << " offset=" << frame.offset - << " readOffset=" << stream.currentReadOffset << " " - << stream.conn; - appendDataToReadBuffer( - stream, StreamBuffer(std::move(frame.data), frame.offset, frame.fin)); - if (isAllDataReceived(stream)) { - stream.conn.streamManager->addClosed(stream.id); - VLOG(10) << "HalfClosedLocal: Transition to closed" - << " stream=" << stream.id << " " << stream.conn; - transit(stream); - } - stream.conn.streamManager->updateReadableStreams(stream); -} - -inline void Handler< - StreamStateMachine, - StreamStates::HalfClosedLocal, - StreamEvents::AckStreamFrame>:: - handle(QuicStreamState& stream, StreamEvents::AckStreamFrame /*ack*/) { - // do nothing here, we already got acks for all the bytes till fin. - DCHECK(stream.retransmissionBuffer.empty()); - DCHECK(stream.writeBuffer.empty()); -} - -inline void -Handler:: - handle(QuicStreamState& /*stream*/, StopSendingFrame /*frame*/) {} - -inline void Handler< - StreamStateMachine, - StreamStates::HalfClosedLocal, - StreamEvents::SendReset>:: - handle(QuicStreamState& stream, StreamEvents::SendReset rst) { - resetQuicStream(stream, rst.errorCode); - appendPendingStreamReset(stream.conn, stream, rst.errorCode); - // Move the state machine: - VLOG(10) << "HalfClosedLocal: Transition to WaitingForRstAck" - << " stream=" << stream.id << " " << stream.conn; - transit(stream); -} -} // namespace quic diff --git a/quic/state/stream/StreamHalfClosedRemoteHandlers.h b/quic/state/stream/StreamHalfClosedRemoteHandlers.h deleted file mode 100644 index 1b151a405..000000000 --- a/quic/state/stream/StreamHalfClosedRemoteHandlers.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -// override-include-guard - -#include -#include - -namespace quic { - -inline void -Handler:: - handle(QuicStreamState& stream, ReadStreamFrame frame) { - VLOG_IF(10, frame.fin) << "HalfClosedRemote: Received data with fin" - << " stream=" << stream.id << " " << stream.conn; - appendDataToReadBuffer( - stream, StreamBuffer(std::move(frame.data), frame.offset, frame.fin)); -} - -inline void Handler< - StreamStateMachine, - StreamStates::HalfClosedRemote, - StreamEvents::AckStreamFrame>:: - handle(QuicStreamState& stream, StreamEvents::AckStreamFrame ack) { - // Clean up the acked buffers from the retransmissionBuffer. - auto ackedBuffer = std::lower_bound( - stream.retransmissionBuffer.begin(), - stream.retransmissionBuffer.end(), - ack.ackedFrame.offset, - [](const auto& buffer, const auto& offset) { - return buffer.offset < offset; - }); - - if (ackedBuffer != stream.retransmissionBuffer.end()) { - // Since the StreamFrames that are ACKed are computed from the outstanding - // packets, we always know that the retransmission buffer corresponds to - // 1 buffer in the retranmission buffer. - CHECK_EQ(ackedBuffer->offset, ack.ackedFrame.offset); - CHECK_EQ(ackedBuffer->data.chainLength(), ack.ackedFrame.len); - - VLOG(10) << "HalfClosedRemote: stream data acked stream=" << stream.id - << " offset=" << ackedBuffer->offset - << " len=" << ackedBuffer->data.chainLength() - << " eof=" << ackedBuffer->eof << " " << stream.conn; - stream.retransmissionBuffer.erase(ackedBuffer); - } else { - VLOG(10) << __func__ << ": offset " << ack.ackedFrame.offset - << " no longer in retransmissionBuffer"; - } - - // This stream may be able to invoke some deliveryCallbacks: - stream.conn.streamManager->addDeliverable(stream.id); - - // Check for whether or not we have ACKed all bytes until our FIN. - if (allBytesTillFinAcked(stream)) { - stream.conn.streamManager->addClosed(stream.id); - VLOG(10) << "HalfClosedRemote: Transition to closed stream=" << stream.id - << " " << stream.conn; - transit(stream); - } -} - -inline void -Handler:: - handle(QuicStreamState& stream, StopSendingFrame frame) { - stream.conn.streamManager->addStopSending(stream.id, frame.errorCode); -} - -inline void -Handler:: - handle(QuicStreamState& stream, RstStreamFrame rst) { - // We transit before invoking onResetQuicStream because it will check the - // state of the stream for flow control. - transit(stream); - onResetQuicStream(stream, std::move(rst)); - // TODO: remove. - appendPendingStreamReset( - stream.conn, stream, GenericApplicationErrorCode::NO_ERROR); -} - -inline void Handler< - StreamStateMachine, - StreamStates::HalfClosedRemote, - StreamEvents::SendReset>:: - handle(QuicStreamState& stream, StreamEvents::SendReset rst) { - resetQuicStream(stream, rst.errorCode); - appendPendingStreamReset(stream.conn, stream, rst.errorCode); - // Move the state machine: - transit(stream); -} -} // namespace quic diff --git a/quic/state/stream/StreamOpenHandlers.h b/quic/state/stream/StreamOpenHandlers.h index 145f1de5c..13bb794af 100644 --- a/quic/state/stream/StreamOpenHandlers.h +++ b/quic/state/stream/StreamOpenHandlers.h @@ -9,95 +9,92 @@ // override-include-guard #include -#include namespace quic { - -inline void -Handler::handle( +template +void invokeStreamSendStateMachine( + QuicConnectionStateBase&, QuicStreamState& stream, - ReadStreamFrame frame) { - if (isSendingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "ReadStreamFrame on unidirectional sending stream", - TransportErrorCode::STREAM_STATE_ERROR); - } + Event event); +inline void +Handler:: + handle( + QuicStreamState::Recv& state, + ReadStreamFrame frame, + QuicStreamState& stream) { VLOG_IF(10, frame.fin) << "Open: Received data with fin" << " stream=" << stream.id << " " << stream.conn; appendDataToReadBuffer( stream, StreamBuffer(std::move(frame.data), frame.offset, frame.fin)); if (isAllDataReceived(stream)) { - if (isUnidirectionalStream(stream.id)) { - VLOG(10) << "Open: Transition to Closed" - << " stream=" << stream.id << " " << stream.conn; - transit(stream); - } else { - VLOG(10) << "Open: Transition to HalfClosedRemote" - << " stream=" << stream.id << " " << stream.conn; - transit(stream); + VLOG(10) << "Open: Transition to Closed" + << " stream=" << stream.id << " " << stream.conn; + transit(state); + if (stream.inTerminalStates()) { + stream.conn.streamManager->addClosed(stream.id); } } stream.conn.streamManager->updateReadableStreams(stream); } inline void -Handler::handle( - QuicStreamState& stream, - StopSendingFrame frame) { - if (isBidirectionalStream(stream.id) || - isSendingStream(stream.conn.nodeType, stream.id)) { - stream.conn.streamManager->addStopSending(stream.id, frame.errorCode); - } else { - throw QuicTransportException( - "StopSendingFrame on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +Handler:: + handle( + QuicStreamState::Send& state, + StopSendingFrame frame, + QuicStreamState& stream) { + CHECK( + isBidirectionalStream(stream.id) || + isSendingStream(stream.conn.nodeType, stream.id)); + stream.conn.streamManager->addStopSending(stream.id, frame.errorCode); } inline void -Handler::handle( - QuicStreamState& stream, - RstStreamFrame rst) { - if (isSendingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "RstStreamFrame on unidirectional sending stream", - TransportErrorCode::STREAM_STATE_ERROR); - } - // We transit before invoking onResetQuicStream because it will check the - // state of the stream for flow control. - transit(stream); - onResetQuicStream(stream, std::move(rst)); - if (isBidirectionalStream(stream.id)) { +Handler:: + handle( + QuicStreamState::Recv& state, + RstStreamFrame rst, + QuicStreamState& stream) { + if (matchesStates( + stream.send.state)) { // TODO: remove. - appendPendingStreamReset( - stream.conn, stream, GenericApplicationErrorCode::NO_ERROR); - } else { - transit(stream); + invokeStreamSendStateMachine( + stream.conn, + stream, + StreamEvents::SendReset(GenericApplicationErrorCode::NO_ERROR)); } + // We transit the receive state machine to Closed before invoking + // onResetQuicStream because it will check the state of the stream for flow + // control. + transit(state); + if (stream.inTerminalStates()) { + stream.conn.streamManager->addClosed(stream.id); + } + onResetQuicStream(stream, std::move(rst)); } -inline void -Handler:: - handle(QuicStreamState& stream, StreamEvents::SendReset rst) { - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "SendReset on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +inline void Handler< + StreamSendStateMachine, + StreamSendStates::Open, + StreamEvents::SendReset>:: + handle( + QuicStreamState::Send& state, + StreamEvents::SendReset rst, + QuicStreamState& stream) { resetQuicStream(stream, rst.errorCode); appendPendingStreamReset(stream.conn, stream, rst.errorCode); // Move the state machine: - transit(stream); + transit(state); } -inline void -Handler:: - handle(QuicStreamState& stream, StreamEvents::AckStreamFrame ack) { - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "AckStreamFrame on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } +inline void Handler< + StreamSendStateMachine, + StreamSendStates::Open, + StreamEvents::AckStreamFrame>:: + handle( + QuicStreamState::Send& state, + StreamEvents::AckStreamFrame ack, + QuicStreamState& stream) { // Clean up the acked buffers from the retransmissionBuffer. auto ackedBuffer = std::lower_bound( @@ -129,10 +126,9 @@ Handler:: // Check for whether or not we have ACKed all bytes until our FIN. if (allBytesTillFinAcked(stream)) { - if (isUnidirectionalStream(stream.id)) { - transit(stream); - } else { - transit(stream); + transit(state); + if (stream.inTerminalStates()) { + stream.conn.streamManager->addClosed(stream.id); } } } diff --git a/quic/state/stream/StreamStateMachine.h b/quic/state/stream/StreamStateMachine.h index d556f35af..3de837a9f 100644 --- a/quic/state/stream/StreamStateMachine.h +++ b/quic/state/stream/StreamStateMachine.h @@ -40,219 +40,171 @@ struct StreamEvents { // Transition the stream to an error state if there is an invalid state // transition. -inline void StreamStateMachineInvalidHandler(const QuicStreamState& state) { +inline void StreamSendStateMachineInvalidHandler(const QuicStreamState& state) { throw QuicTransportException( folly::to( "Invalid transition from state=", folly::variant_match( - state.state, - [](const StreamStates::Open&) { return "Open"; }, - [](const StreamStates::HalfClosedLocal&) { - return "HalfClosedLocal"; - }, - [](const StreamStates::HalfClosedRemote&) { - return "HalfClosedRemote"; - }, - [](const StreamStates::WaitingForRstAck&) { - return "WaitingForRstAck"; - }, - [](const StreamStates::Closed&) { return "Closed"; })), + state.send.state, + [](const StreamSendStates::Open&) { return "Open"; }, + [](const StreamSendStates::ResetSent&) { return "ResetSent"; }, + [](const StreamSendStates::Closed&) { return "Closed"; }, + [](const StreamSendStates::Invalid&) { return "Invalid"; })), TransportErrorCode::STREAM_STATE_ERROR); } -struct StreamStateMachine { - using StateData = QuicStreamState; - static constexpr auto InvalidEventHandler = &StreamStateMachineInvalidHandler; +struct StreamSendStateMachine { + using StateData = QuicStreamState::Send; + using UserData = QuicStreamState; + static constexpr auto InvalidEventHandler = + &StreamSendStateMachineInvalidHandler; +}; + +// Transition the stream to an error state if there is an invalid state +// transition. +inline void StreamReceiveStateMachineInvalidHandler( + const QuicStreamState& state) { + throw QuicTransportException( + folly::to( + "Invalid transition from state=", + folly::variant_match( + state.recv.state, + [](const StreamReceiveStates::Open&) { return "Open"; }, + [](const StreamReceiveStates::Closed&) { return "Closed"; }, + [](const StreamReceiveStates::Invalid&) { return "Invalid"; })), + TransportErrorCode::STREAM_STATE_ERROR); +} + +struct StreamReceiveStateMachine { + using StateData = QuicStreamState::Recv; + using UserData = QuicStreamState; + static constexpr auto InvalidEventHandler = + &StreamReceiveStateMachineInvalidHandler; }; /** * Welcome to the stream state machine, we got fun and games. * - * ACK = Ack of stream frame. + * This is a simplified version of the state machines defined in the transport + * specification. The "Invalid" state is used for unidirectional streams that + * do not have that half (eg: an ingress uni stream is in send state Invalid) + * + * Send State Machine + * ================== + * + * [ Initial State ] + * | + * | Send Stream + * | + * v + * Send::Open ---------------+ + * | | + * | Ack all bytes | Send RST + * | ti FIN | + * v v + * Send::Closed <------ ResetSent + * RST + * Acked * * - * Stream / ACK Stream/ ACK - * |--| |----| - * | v All bytes till FIN | v All bytes till FIN - * Open ---------------------> HalfClosedRemote ------------------------| - * | | recv | acked | - * | | | | - * | | SendReset / Reset | | - * | ----------------------------| | | - * | | | | - * | All bytes till FIN acked | | | - * | | | | - * | | | | - * | Stream / ACK | | SendReset / | - * | |---| | | Reset | - * v | v SendReset v v RstAck v - * HalfClosedLocal --------------> WaitingForRstAck---------------------| - * | | ^ | - * | |---| | - * | Reset / Stream / ACK / Reset / SendReset | - * | All bytes till FIN recv | - * |--------------------------------------------------------------> Closed - * | ^ - * |---| - * Stream / - * Reset / - * RstAck / - * Ack / - * SendReset + * Receive State Machine + * ===================== + * + * [ Initial State ] + * | + * | Stream + * | + * v + * Receive::Open -----------+ + * | | + * | Receive all | Receive RST + * | bytes til FIN | + * v | + * Receive::Closed <---------+ + * */ QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Open, + StreamReceiveStateMachine, + StreamReceiveStates::Open, ReadStreamFrame, - StreamStates::HalfClosedRemote, - StreamStates::Closed); + StreamReceiveStates::Closed); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Open, + StreamReceiveStateMachine, + StreamReceiveStates::Open, RstStreamFrame, - StreamStates::WaitingForRstAck, - StreamStates::Closed); + StreamReceiveStates::Closed); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Open, + StreamSendStateMachine, + StreamSendStates::Open, StreamEvents::SendReset, - StreamStates::WaitingForRstAck); + StreamSendStates::ResetSent); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Open, + StreamSendStateMachine, + StreamSendStates::Open, StreamEvents::AckStreamFrame, - StreamStates::HalfClosedLocal, - StreamStates::Closed); + StreamSendStates::Closed); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Open, + StreamSendStateMachine, + StreamSendStates::Open, StopSendingFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedLocal, - RstStreamFrame, - StreamStates::Closed); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedLocal, - ReadStreamFrame, - StreamStates::Closed); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedLocal, + StreamSendStateMachine, + StreamSendStates::ResetSent, StreamEvents::AckStreamFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedLocal, + StreamSendStateMachine, + StreamSendStates::ResetSent, StopSendingFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedLocal, - StreamEvents::SendReset, - StreamStates::WaitingForRstAck); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedRemote, - ReadStreamFrame); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedRemote, - StreamEvents::AckStreamFrame, - StreamStates::Closed); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedRemote, - StopSendingFrame); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedRemote, - StreamEvents::SendReset, - StreamStates::WaitingForRstAck); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::HalfClosedRemote, - RstStreamFrame, - StreamStates::WaitingForRstAck); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::WaitingForRstAck, - ReadStreamFrame); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::WaitingForRstAck, - StreamEvents::AckStreamFrame); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::WaitingForRstAck, - StopSendingFrame); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::WaitingForRstAck, - RstStreamFrame); - -QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::WaitingForRstAck, + StreamSendStateMachine, + StreamSendStates::ResetSent, StreamEvents::RstAck, - StreamStates::Closed); + StreamSendStates::Closed); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::WaitingForRstAck, + StreamSendStateMachine, + StreamSendStates::ResetSent, StreamEvents::SendReset); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Closed, + StreamReceiveStateMachine, + StreamReceiveStates::Closed, ReadStreamFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Closed, + StreamSendStateMachine, + StreamSendStates::Closed, StreamEvents::RstAck); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Closed, + StreamSendStateMachine, + StreamSendStates::Closed, StreamEvents::AckStreamFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Closed, + StreamSendStateMachine, + StreamSendStates::Closed, StopSendingFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Closed, + StreamReceiveStateMachine, + StreamReceiveStates::Closed, RstStreamFrame); QUIC_DECLARE_STATE_HANDLER( - StreamStateMachine, - StreamStates::Closed, + StreamSendStateMachine, + StreamSendStates::Closed, StreamEvents::SendReset); } // namespace quic #include -#include -#include #include #include diff --git a/quic/state/stream/StreamWaitingForRstAckHandlers.h b/quic/state/stream/StreamWaitingForRstAckHandlers.h index 92656e216..8dbed1dc5 100644 --- a/quic/state/stream/StreamWaitingForRstAckHandlers.h +++ b/quic/state/stream/StreamWaitingForRstAckHandlers.h @@ -12,25 +12,20 @@ namespace quic { -inline void -Handler:: - handle(QuicStreamState& stream, ReadStreamFrame frame) { - if (isSendingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "ReadStreamFrame on unidirectional sending stream", - TransportErrorCode::STREAM_STATE_ERROR); - } - VLOG_IF(10, frame.fin) << "WaitingForRstAck: Received data with fin" - << " stream=" << stream.id << " " << stream.conn; - appendDataToReadBuffer( - stream, StreamBuffer(std::move(frame.data), frame.offset, frame.fin)); -} +template +void invokeStreamReceiveStateMachine( + QuicConnectionStateBase&, + QuicStreamState& stream, + Event event); inline void Handler< - StreamStateMachine, - StreamStates::WaitingForRstAck, + StreamSendStateMachine, + StreamSendStates::ResetSent, StreamEvents::AckStreamFrame>:: - handle(QuicStreamState& stream, StreamEvents::AckStreamFrame /*ack*/) { + handle( + QuicStreamState::Send& /*state*/, + StreamEvents::AckStreamFrame /*ack*/, + QuicStreamState& stream) { // do nothing here. We should have already dumped the stream state before // we got here. DCHECK(stream.retransmissionBuffer.empty()); @@ -38,43 +33,46 @@ inline void Handler< } inline void -Handler:: - handle(QuicStreamState& stream, StopSendingFrame /*frame*/) { - if (isReceivingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "StopSendingFrame on unidirectional receiving stream", - TransportErrorCode::STREAM_STATE_ERROR); - } -} - -inline void -Handler:: - handle(QuicStreamState& stream, RstStreamFrame rst) { - if (isSendingStream(stream.conn.nodeType, stream.id)) { - throw QuicTransportException( - "RstStreamFrame on unidirectional sending stream", - TransportErrorCode::STREAM_STATE_ERROR); - } - // This will make sure all the states are consistent between resets. - onResetQuicStream(stream, std::move(rst)); +Handler:: + handle( + QuicStreamState::Send& /*state*/, + StopSendingFrame /*frame*/, + QuicStreamState& /*stream*/) { + // no-op, we already sent a reset } inline void Handler< - StreamStateMachine, - StreamStates::WaitingForRstAck, + StreamSendStateMachine, + StreamSendStates::ResetSent, StreamEvents::RstAck>:: - handle(QuicStreamState& stream, StreamEvents::RstAck /*ack*/) { - stream.conn.streamManager->addClosed(stream.id); - VLOG(10) << "WaitingForRstAck: Transition to closed stream=" << stream.id - << " " << stream.conn; - transit(stream); + handle( + QuicStreamState::Send& state, + StreamEvents::RstAck /*ack*/, + QuicStreamState& stream) { + VLOG(10) << "ResetSent: Transition to closed stream=" << stream.id << " " + << stream.conn; + transit(state); + if (matchesStates( + stream.recv.state)) { + // Terminate the ingress state machine until we remove rst on rst + invokeStreamReceiveStateMachine( + stream.conn, + stream, + RstStreamFrame(stream.id, GenericApplicationErrorCode::NO_ERROR, 0)); + } + if (stream.inTerminalStates()) { + stream.conn.streamManager->addClosed(stream.id); + } } inline void Handler< - StreamStateMachine, - StreamStates::WaitingForRstAck, + StreamSendStateMachine, + StreamSendStates::ResetSent, StreamEvents::SendReset>:: - handle(QuicStreamState& /*stream*/, StreamEvents::SendReset) { + handle( + QuicStreamState::Send& /*state*/, + StreamEvents::SendReset, + QuicStreamState& /*stream*/) { // do nothing. } } // namespace quic diff --git a/quic/state/stream/test/StreamStateFunctionsTest.cpp b/quic/state/stream/test/StreamStateFunctionsTest.cpp index e35159784..983ac3968 100644 --- a/quic/state/stream/test/StreamStateFunctionsTest.cpp +++ b/quic/state/stream/test/StreamStateFunctionsTest.cpp @@ -36,8 +36,10 @@ TEST_F(StreamStateFunctionsTests, SanityTest) { auto currentReadOffset = stream.currentReadOffset; EXPECT_TRUE(stream.writable()); - invokeHandler( - stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream); // Something are cleared: EXPECT_TRUE(stream.writeBuffer.empty()); EXPECT_TRUE(stream.retransmissionBuffer.empty()); diff --git a/quic/state/stream/test/StreamStateMachineTest.cpp b/quic/state/stream/test/StreamStateMachineTest.cpp index 2c52f6b13..b9bc9f17f 100644 --- a/quic/state/stream/test/StreamStateMachineTest.cpp +++ b/quic/state/stream/test/StreamStateMachineTest.cpp @@ -27,10 +27,13 @@ void verifyStreamReset( const QuicStreamState& stream, uint64_t readOffsetExpected) { EXPECT_TRUE(stream.readBuffer.empty()); + // AHF EXPECT_TRUE(stream.retransmissionBuffer.empty()); EXPECT_TRUE(stream.writeBuffer.empty()); + EXPECT_TRUE(stream.finalReadOffset.hasValue()); EXPECT_EQ(readOffsetExpected, stream.finalReadOffset.value()); + // AHF EXPECT_FALSE(stream.writable()); } @@ -65,10 +68,11 @@ TEST_F(QuicOpenStateTest, ReadStreamDataNotFin) { bool fin = false; ReadStreamFrame frame(id, offset, fin); frame.data = IOBuf::copyBuffer("hey"); - invokeHandler(stream, std::move(frame)); + invokeHandler( + stream.recv, std::move(frame), stream); EXPECT_TRUE(stream.hasReadableData()); EXPECT_TRUE(stream.hasPeekableData()); - EXPECT_TRUE(isState(stream)); + EXPECT_TRUE(isState(stream.recv)); } TEST_F(QuicOpenStateTest, ReadInvalidData) { @@ -81,15 +85,17 @@ TEST_F(QuicOpenStateTest, ReadInvalidData) { // EOF in middle of stream ReadStreamFrame frame1(id, offset1, fin1); frame1.data = IOBuf::copyBuffer("hey"); - invokeHandler(stream, std::move(frame1)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.recv, std::move(frame1), stream); + EXPECT_TRUE(isState(stream.recv)); uint64_t offset2 = 1; bool fin2 = true; ReadStreamFrame frame2(id, offset2, fin2); frame2.data = IOBuf::copyBuffer("e"); EXPECT_THROW( - invokeHandler(stream, std::move(frame2)), + invokeHandler( + stream.recv, std::move(frame2), stream), QuicTransportException); } @@ -99,7 +105,8 @@ TEST_F(QuicOpenStateTest, InvalidEvent) { QuicStreamState stream(id, *conn); RstStreamFrame frame(1, GenericApplicationErrorCode::UNKNOWN, 0); EXPECT_THROW( - invokeHandler(stream, StreamEvents::RstAck(frame)), + invokeHandler( + stream.send, StreamEvents::RstAck(frame), stream), QuicTransportException); } @@ -113,8 +120,9 @@ TEST_F(QuicOpenStateTest, ReceiveStreamFrameWithFIN) { ReadStreamFrame receivedStreamFrame(stream->id, 100, true); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(*stream, std::move(receivedStreamFrame)); - ASSERT_TRUE(isState(*stream)); + invokeHandler( + stream->recv, std::move(receivedStreamFrame), *stream); + ASSERT_TRUE(isState(stream->recv)); } TEST_F(QuicOpenStateTest, ReceiveStreamFrameWithFINReadbuffHole) { @@ -127,8 +135,9 @@ TEST_F(QuicOpenStateTest, ReceiveStreamFrameWithFINReadbuffHole) { ReadStreamFrame receivedStreamFrame(stream->id, 200, true); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(*stream, std::move(receivedStreamFrame)); - ASSERT_TRUE(isState(*stream)); + invokeHandler( + stream->recv, std::move(receivedStreamFrame), *stream); + ASSERT_TRUE(isState(stream->recv)); } TEST_F(QuicOpenStateTest, ReceiveStreamFrameWithoutFIN) { @@ -141,28 +150,32 @@ TEST_F(QuicOpenStateTest, ReceiveStreamFrameWithoutFIN) { ReadStreamFrame receivedStreamFrame(stream->id, 100, false); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(*stream, std::move(receivedStreamFrame)); - ASSERT_TRUE(isState(*stream)); + invokeHandler( + stream->recv, std::move(receivedStreamFrame), *stream); + ASSERT_TRUE(isState(stream->recv)); } -class QuicWaitingForRstAckStateTest : public Test {}; +class QuicResetSentStateTest : public Test {}; -TEST_F(QuicWaitingForRstAckStateTest, RstAck) { +TEST_F(QuicResetSentStateTest, RstAck) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); + stream.send.state = StreamSendStates::ResetSent(); stream.currentReadOffset = 0xABCD; stream.finalWriteOffset = 0xACDC; stream.readBuffer.emplace_back( folly::IOBuf::copyBuffer("One more thing"), 0xABCD, false); RstStreamFrame frame(id, GenericApplicationErrorCode::UNKNOWN, 0); - invokeHandler(stream, StreamEvents::RstAck(frame)); + invokeHandler( + stream.send, StreamEvents::RstAck(frame), stream); - EXPECT_TRUE(isState(stream)); - EXPECT_FALSE(stream.finalReadOffset); - EXPECT_FALSE(stream.readBuffer.empty()); + EXPECT_TRUE(isState(stream.send)); + // AHF + EXPECT_TRUE(isState(stream.recv)); + // EXPECT_FALSE(stream.finalReadOffset); + // EXPECT_FALSE(stream.readBuffer.empty()); } class QuicClosedStateTest : public Test {}; @@ -171,10 +184,11 @@ TEST_F(QuicClosedStateTest, RstAck) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Closed(); RstStreamFrame frame(id, GenericApplicationErrorCode::UNKNOWN, 0); - invokeHandler(stream, StreamEvents::RstAck(frame)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.send, StreamEvents::RstAck(frame), stream); + EXPECT_TRUE(isState(stream.send)); } TEST_F(QuicOpenStateTest, AckStream) { @@ -204,11 +218,11 @@ TEST_F(QuicOpenStateTest, AckStream) { ->packet.frames.front()); StreamEvents::AckStreamFrame ack(streamFrame); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); } TEST_F(QuicOpenStateTest, AckStreamAfterSkip) { @@ -245,11 +259,13 @@ TEST_F(QuicOpenStateTest, AckStreamAfterSkip) { EXPECT_TRUE(stream->retransmissionBuffer.empty()); StreamEvents::AckStreamFrame ack(streamFrame); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); } class QuicHalfClosedLocalStateTest : public Test {}; @@ -258,45 +274,54 @@ TEST_F(QuicHalfClosedLocalStateTest, ReceiveStreamFrameWithFIN) { auto conn = createConn(); auto stream = conn->streamManager->createNextBidirectionalStream().value(); - stream->state = StreamStates::HalfClosedLocal(); + stream->send.state = StreamSendStates::Closed(); + stream->recv.state = StreamReceiveStates::Open(); stream->currentReadOffset = 100; // We received FIN and everything: ReadStreamFrame receivedStreamFrame(stream->id, 100, true); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(*stream, std::move(receivedStreamFrame)); - ASSERT_TRUE(isState(*stream)); + invokeHandler( + stream->recv, std::move(receivedStreamFrame), *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); } TEST_F(QuicHalfClosedLocalStateTest, ReceiveStreamFrameWithFINReadbuffHole) { auto conn = createConn(); auto stream = conn->streamManager->createNextBidirectionalStream().value(); - stream->state = StreamStates::HalfClosedLocal(); + stream->send.state = StreamSendStates::Closed(); + stream->recv.state = StreamReceiveStates::Open(); stream->currentReadOffset = 100; // We received FIN, but we havn't received anything between 100 and 200: ReadStreamFrame receivedStreamFrame(stream->id, 200, true); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(*stream, std::move(receivedStreamFrame)); - ASSERT_TRUE(isState(*stream)); + invokeHandler( + stream->recv, std::move(receivedStreamFrame), *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); } TEST_F(QuicHalfClosedLocalStateTest, ReceiveStreamFrameWithoutFIN) { auto conn = createConn(); auto stream = conn->streamManager->createNextBidirectionalStream().value(); - stream->state = StreamStates::HalfClosedLocal(); + stream->send.state = StreamSendStates::Closed(); + stream->recv.state = StreamReceiveStates::Open(); stream->currentReadOffset = 100; // We haven't received FIN: ReadStreamFrame receivedStreamFrame(stream->id, 100, false); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(*stream, std::move(receivedStreamFrame)); - ASSERT_TRUE(isState(*stream)); + invokeHandler( + stream->recv, std::move(receivedStreamFrame), *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); } class QuicHalfClosedRemoteStateTest : public Test {}; @@ -310,7 +335,8 @@ TEST_F(QuicHalfClosedRemoteStateTest, AckStream) { folly::Optional serverChosenConnId = connIdAlgo->encodeConnectionId(params); auto stream = conn->streamManager->createNextBidirectionalStream().value(); - stream->state = StreamStates::HalfClosedRemote(); + stream->send.state = StreamSendStates::Open(); + stream->recv.state = StreamReceiveStates::Closed(); EventBase evb; auto sock = std::make_unique(&evb); @@ -333,11 +359,11 @@ TEST_F(QuicHalfClosedRemoteStateTest, AckStream) { ->packet.frames.front()); StreamEvents::AckStreamFrame ack(streamFrame); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); } TEST_F(QuicHalfClosedRemoteStateTest, AckStreamAfterSkip) { @@ -349,7 +375,8 @@ TEST_F(QuicHalfClosedRemoteStateTest, AckStreamAfterSkip) { folly::Optional serverChosenConnId = connIdAlgo->encodeConnectionId(params); auto stream = conn->streamManager->createNextBidirectionalStream().value(); - stream->state = StreamStates::HalfClosedRemote(); + stream->send.state = StreamSendStates::Open(); + stream->recv.state = StreamReceiveStates::Closed(); EventBase evb; auto sock = std::make_unique(&evb); @@ -379,11 +406,13 @@ TEST_F(QuicHalfClosedRemoteStateTest, AckStreamAfterSkip) { EXPECT_TRUE(stream->retransmissionBuffer.empty()); StreamEvents::AckStreamFrame ack(streamFrame); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); - invokeHandler(*stream, ack); - ASSERT_TRUE(isState(*stream)); + invokeHandler(stream->send, ack, *stream); + ASSERT_TRUE(isState(stream->send)); + ASSERT_TRUE(isState(stream->recv)); } class QuicSendResetTest : public Test {}; @@ -392,47 +421,61 @@ TEST_F(QuicSendResetTest, FromOpen) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - invokeHandler( - stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream); + EXPECT_TRUE(isState(stream.send)); } TEST_F(QuicSendResetTest, FromHalfCloseRemote) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedRemote(); - invokeHandler( - stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Closed(); + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream); + EXPECT_TRUE(isState(stream.send)); } TEST_F(QuicSendResetTest, FromHalfCloseLocal) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedLocal(); - invokeHandler( - stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::Closed(); + stream.recv.state = StreamReceiveStates::Open(); + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream); + + // You cannot send a reset after FIN has been acked + EXPECT_TRUE(isState(stream.send)); } TEST_F(QuicSendResetTest, FromClosed) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); - invokeHandler( - stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); + stream.send.state = StreamSendStates::Closed(); + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream); } -TEST_F(QuicSendResetTest, FromWaitingForRstAck) { +TEST_F(QuicSendResetTest, FromResetSent) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); - invokeHandler( - stream, StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)); + stream.send.state = StreamSendStates::ResetSent(); + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream); } class QuicRecvResetTest : public Test {}; @@ -443,8 +486,9 @@ TEST_F(QuicRecvResetTest, FromOpen) { StreamId rstStream = 1; QuicStreamState stream(id, *conn); RstStreamFrame rst(rstStream, GenericApplicationErrorCode::UNKNOWN, 100); - invokeHandler(stream, std::move(rst)); - EXPECT_TRUE(isState(stream)); + invokeHandler(stream.recv, std::move(rst), stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 100); } @@ -456,7 +500,8 @@ TEST_F(QuicRecvResetTest, FromOpenReadEOFMismatch) { RstStreamFrame rst(1, GenericApplicationErrorCode::UNKNOWN, 100); stream.finalReadOffset = 1024; EXPECT_THROW( - invokeHandler(stream, std::move(rst)), + invokeHandler( + stream.recv, std::move(rst), stream), QuicTransportException); } @@ -464,10 +509,14 @@ TEST_F(QuicRecvResetTest, FromHalfClosedRemoteNoReadOffsetYet) { StreamId id = 5; auto conn = createConn(); QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedRemote(); - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 100)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Closed(); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 100), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 100); } @@ -475,11 +524,15 @@ TEST_F(QuicRecvResetTest, FromHalfClosedRemoteReadOffsetMatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedRemote(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Closed(); stream.finalReadOffset = 1024; - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1024)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1024), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 1024); } @@ -487,11 +540,14 @@ TEST_F(QuicRecvResetTest, FromHalfClosedRemoteReadOffsetMismatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedRemote(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Closed(); stream.finalReadOffset = 1024; EXPECT_THROW( - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 100)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 100), + stream), QuicTransportException); } @@ -499,10 +555,14 @@ TEST_F(QuicRecvResetTest, FromHalfClosedLocal) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedLocal(); - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::Closed(); + stream.recv.state = StreamReceiveStates::Open(); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 200); } @@ -510,46 +570,60 @@ TEST_F(QuicRecvResetTest, FromHalfClosedLocalReadEOFMismatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::HalfClosedLocal(); + stream.send.state = StreamSendStates::Closed(); + stream.recv.state = StreamReceiveStates::Open(); stream.finalReadOffset = 2014; EXPECT_THROW( - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200), + stream), QuicTransportException); } -TEST_F(QuicRecvResetTest, FromWaitingForRstAckNoReadOffsetYet) { +TEST_F(QuicRecvResetTest, FromResetSentNoReadOffsetYet) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::ResetSent(); + stream.recv.state = StreamReceiveStates::Open(); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 200); } -TEST_F(QuicRecvResetTest, FromWaitingForRstAckOffsetMatch) { +TEST_F(QuicRecvResetTest, FromResetSentOffsetMatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); + stream.send.state = StreamSendStates::ResetSent(); + stream.recv.state = StreamReceiveStates::Open(); stream.finalReadOffset = 200; - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 200); } -TEST_F(QuicRecvResetTest, FromWaitingForRstAckOffsetMismatch) { +TEST_F(QuicRecvResetTest, FromResetSentOffsetMismatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); + stream.send.state = StreamSendStates::ResetSent(); + stream.recv.state = StreamReceiveStates::Open(); stream.finalReadOffset = 300; EXPECT_THROW( - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200), + stream), QuicTransportException); } @@ -557,10 +631,14 @@ TEST_F(QuicRecvResetTest, FromClosedNoReadOffsetYet) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::Closed(); + stream.recv.state = StreamReceiveStates::Closed(); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 200), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 200); } @@ -568,11 +646,15 @@ TEST_F(QuicRecvResetTest, FromClosedOffsetMatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Closed(); + stream.recv.state = StreamReceiveStates::Closed(); stream.finalReadOffset = 1234; - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); verifyStreamReset(stream, 1234); } @@ -580,12 +662,14 @@ TEST_F(QuicRecvResetTest, FromClosedOffsetMismatch) { auto conn = createConn(); StreamId id = 5; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Closed(); + stream.recv.state = StreamReceiveStates::Closed(); stream.finalReadOffset = 123; EXPECT_THROW( - invokeHandler( - stream, - RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234), + stream), QuicTransportException); } @@ -595,9 +679,11 @@ TEST_F(QuicUnidirectionalStreamTest, OpenInvalidReadStream) { auto conn = createConn(); StreamId id = 0b111; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Invalid(); EXPECT_THROW( - invokeHandler(stream, ReadStreamFrame(id, 1, false)), + invokeHandler( + stream.recv, ReadStreamFrame(id, 1, false), stream), QuicTransportException); } @@ -605,11 +691,13 @@ TEST_F(QuicUnidirectionalStreamTest, OpenInvalidRstStream) { auto conn = createConn(); StreamId id = 0b111; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Invalid(); EXPECT_THROW( - invokeHandler( - stream, - RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234), + stream), QuicTransportException); } @@ -617,11 +705,13 @@ TEST_F(QuicUnidirectionalStreamTest, OpenInvalidSendReset) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Open(); EXPECT_THROW( - invokeHandler( - stream, - StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)), + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream), QuicTransportException); } @@ -629,20 +719,25 @@ TEST_F(QuicUnidirectionalStreamTest, OpenInvalidAckStreamFrame) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Open(); StreamEvents::AckStreamFrame ack(WriteStreamFrame(id, 0, 0, false)); EXPECT_THROW( - invokeHandler(stream, ack), QuicTransportException); + invokeHandler(stream.send, ack, stream), + QuicTransportException); } TEST_F(QuicUnidirectionalStreamTest, OpenInvalidStopSending) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Open(); EXPECT_THROW( - invokeHandler( - stream, StopSendingFrame(id, GenericApplicationErrorCode::UNKNOWN)), + invokeHandler( + stream.send, + StopSendingFrame(id, GenericApplicationErrorCode::UNKNOWN), + stream), QuicTransportException); } @@ -650,9 +745,11 @@ TEST_F(QuicUnidirectionalStreamTest, ClosedInvalidReadStream) { auto conn = createConn(); StreamId id = 0b111; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Invalid(); EXPECT_THROW( - invokeHandler(stream, ReadStreamFrame(id, 1, false)), + invokeHandler( + stream.recv, ReadStreamFrame(id, 1, false), stream), QuicTransportException); } @@ -660,11 +757,13 @@ TEST_F(QuicUnidirectionalStreamTest, ClosedInvalidRstStream) { auto conn = createConn(); StreamId id = 0b111; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Invalid(); EXPECT_THROW( - invokeHandler( - stream, - RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234), + stream), QuicTransportException); } @@ -672,11 +771,13 @@ TEST_F(QuicUnidirectionalStreamTest, ClosedInvalidSendReset) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Closed(); EXPECT_THROW( - invokeHandler( - stream, - StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN)), + invokeHandler( + stream.send, + StreamEvents::SendReset(GenericApplicationErrorCode::UNKNOWN), + stream), QuicTransportException); } @@ -684,20 +785,25 @@ TEST_F(QuicUnidirectionalStreamTest, ClosedInvalidAckStreamFrame) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Closed(); StreamEvents::AckStreamFrame ack(WriteStreamFrame(id, 0, 0, false)); EXPECT_THROW( - invokeHandler(stream, ack), QuicTransportException); + invokeHandler(stream.send, ack, stream), + QuicTransportException); } TEST_F(QuicUnidirectionalStreamTest, ClosedInvalidStopSending) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Closed(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Closed(); EXPECT_THROW( - invokeHandler( - stream, StopSendingFrame(id, GenericApplicationErrorCode::UNKNOWN)), + invokeHandler( + stream.send, + StopSendingFrame(id, GenericApplicationErrorCode::UNKNOWN), + stream), QuicTransportException); } @@ -705,23 +811,30 @@ TEST_F(QuicUnidirectionalStreamTest, OpenReadStreamFin) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Open(); stream.currentReadOffset = 100; ReadStreamFrame receivedStreamFrame(stream.id, 100, true); receivedStreamFrame.data = folly::IOBuf::create(10); receivedStreamFrame.data->append(10); - invokeHandler(stream, std::move(receivedStreamFrame)); - EXPECT_TRUE(isState(stream)); + invokeHandler( + stream.recv, std::move(receivedStreamFrame), stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); } TEST_F(QuicUnidirectionalStreamTest, OpenRstStream) { auto conn = createConn(); StreamId id = 0b110; QuicStreamState stream(id, *conn); - stream.state = StreamStates::Open(); - invokeHandler( - stream, RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234)); - EXPECT_TRUE(isState(stream)); + stream.send.state = StreamSendStates::Invalid(); + stream.recv.state = StreamReceiveStates::Open(); + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234), + stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); } TEST_F(QuicUnidirectionalStreamTest, OpenFinalAckStreamFrame) { @@ -729,48 +842,44 @@ TEST_F(QuicUnidirectionalStreamTest, OpenFinalAckStreamFrame) { StreamId id = 0b111; QuicStreamState stream(id, *conn); WriteStreamFrame streamFrame(id, 1, 1, false); - stream.state = StreamStates::Open(); + stream.send.state = StreamSendStates::Open(); + stream.recv.state = StreamReceiveStates::Invalid(); stream.finalWriteOffset = 1; stream.currentWriteOffset = 2; auto buf = folly::IOBuf::create(1); buf->append(1); stream.retransmissionBuffer.emplace_back(std::move(buf), 1, false); StreamEvents::AckStreamFrame ack(streamFrame); - invokeHandler(stream, ack); - EXPECT_TRUE(isState(stream)); + invokeHandler(stream.send, ack, stream); + EXPECT_TRUE(isState(stream.send)); + EXPECT_TRUE(isState(stream.recv)); } -TEST_F(QuicUnidirectionalStreamTest, WaitingForRstAckInvalidReadStream) { +TEST_F(QuicUnidirectionalStreamTest, ResetSentInvalidReadStream) { auto conn = createConn(); StreamId id = 0b111; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); + stream.send.state = StreamSendStates::ResetSent(); + stream.recv.state = StreamReceiveStates::Invalid(); EXPECT_THROW( - invokeHandler(stream, ReadStreamFrame(id, 1, false)), + invokeHandler( + stream.recv, ReadStreamFrame(id, 1, false), stream), QuicTransportException); } -TEST_F(QuicUnidirectionalStreamTest, WaitingForRstAckInvalidRstStream) { +TEST_F(QuicUnidirectionalStreamTest, ResetSentInvalidRstStream) { auto conn = createConn(); StreamId id = 0b111; QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); + stream.send.state = StreamSendStates::ResetSent(); + stream.recv.state = StreamReceiveStates::Invalid(); EXPECT_THROW( - invokeHandler( - stream, - RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234)), + invokeHandler( + stream.recv, + RstStreamFrame(1, GenericApplicationErrorCode::UNKNOWN, 1234), + stream), QuicTransportException); } -TEST_F(QuicUnidirectionalStreamTest, WaitingForRstAckInvalidStopSending) { - auto conn = createConn(); - StreamId id = 0b110; - QuicStreamState stream(id, *conn); - stream.state = StreamStates::WaitingForRstAck(); - EXPECT_THROW( - invokeHandler( - stream, StopSendingFrame(id, GenericApplicationErrorCode::UNKNOWN)), - QuicTransportException); -} } // namespace test } // namespace quic diff --git a/quic/state/test/QPRFunctionsTest.cpp b/quic/state/test/QPRFunctionsTest.cpp index 546ddba4b..46032051d 100644 --- a/quic/state/test/QPRFunctionsTest.cpp +++ b/quic/state/test/QPRFunctionsTest.cpp @@ -194,7 +194,8 @@ TEST_F(QPRFunctionsTest, RecvMinStreamDataFrameShrinkBuffer) { TEST_F(QPRFunctionsTest, RecvMinStreamDataFrameOnUnidirectionalStream) { auto stream = conn.streamManager->createNextUnidirectionalStream().value(); - stream->state = StreamStates::Closed{}; + stream->send.state = StreamSendStates::Closed{}; + stream->recv.state = StreamReceiveStates::Closed{}; PacketNum packetNum(10); MinStreamDataFrame frame( stream->id, stream->flowControlState.peerAdvertisedMaxOffset + 100, 100); diff --git a/quic/state/test/QuicStateFunctionsTest.cpp b/quic/state/test/QuicStateFunctionsTest.cpp index fef79998c..6b2c3c8f6 100644 --- a/quic/state/test/QuicStateFunctionsTest.cpp +++ b/quic/state/test/QuicStateFunctionsTest.cpp @@ -345,10 +345,11 @@ TEST_F(QuicStateFunctionsTest, TestInvokeStreamStateMachineConnectionError) { RstStreamFrame rst(1, GenericApplicationErrorCode::UNKNOWN, 100); stream.finalReadOffset = 1024; EXPECT_THROW( - invokeStreamStateMachine(conn, stream, std::move(rst)), + invokeStreamReceiveStateMachine(conn, stream, std::move(rst)), QuicTransportException); - bool matches = matchesStates( - stream.state); + bool matches = + matchesStates( + stream.send.state); EXPECT_TRUE(matches); } @@ -361,9 +362,10 @@ TEST_F(QuicStateFunctionsTest, InvokeResetDoesNotSendFlowControl) { stream.flowControlState.windowSize = 100; conn.flowControlState.advertisedMaxOffset = 100; conn.flowControlState.windowSize = 100; - invokeStreamStateMachine(conn, stream, std::move(rst)); - bool matches = matchesStates( - stream.state); + invokeStreamReceiveStateMachine(conn, stream, std::move(rst)); + bool matches = + matchesStates( + stream.send.state); EXPECT_TRUE(matches); EXPECT_FALSE(conn.streamManager->hasWindowUpdates()); EXPECT_TRUE(conn.pendingEvents.connWindowUpdate); @@ -376,13 +378,13 @@ TEST_F(QuicStateFunctionsTest, TestInvokeStreamStateMachineStreamError) { QuicStreamState stream(1, conn); RstStreamFrame rst(1, GenericApplicationErrorCode::UNKNOWN, 100); try { - invokeStreamStateMachine(conn, stream, StreamEvents::RstAck(rst)); + invokeStreamSendStateMachine(conn, stream, StreamEvents::RstAck(rst)); ADD_FAILURE(); } catch (QuicTransportException& ex) { EXPECT_EQ(ex.errorCode(), TransportErrorCode::STREAM_STATE_ERROR); } - bool matches = - matchesStates(stream.state); + bool matches = matchesStates( + stream.send.state); EXPECT_TRUE(matches); } diff --git a/quic/state/test/QuicStreamFunctionsTest.cpp b/quic/state/test/QuicStreamFunctionsTest.cpp index dd34b5108..f24cc09ca 100644 --- a/quic/state/test/QuicStreamFunctionsTest.cpp +++ b/quic/state/test/QuicStreamFunctionsTest.cpp @@ -1518,7 +1518,8 @@ TEST_F(QuicStreamFunctionsTest, RemovedClosedState) { conn.streamManager->queueWindowUpdate(streamId); conn.streamManager->addStopSending( streamId, GenericApplicationErrorCode::UNKNOWN); - stream->state = StreamStates::Closed{}; + stream->send.state = StreamSendStates::Closed{}; + stream->recv.state = StreamReceiveStates::Closed{}; conn.streamManager->removeClosedStream(streamId); EXPECT_FALSE(conn.streamManager->streamExists(streamId)); EXPECT_TRUE(conn.streamManager->readableStreams().empty()); @@ -1594,8 +1595,8 @@ TEST_F(QuicServerStreamFunctionsTest, ServerGetCloseBothDirections) { conn.streamManager->createStream(serverBiStream).value(); EXPECT_EQ(conn.streamManager->getStream(serverBiStream)->id, serverBiStream); StreamId serverUniStream = 0x0B; - conn.streamManager->createStream(serverUniStream).value()->state = - StreamStates::Closed{}; + auto stream = conn.streamManager->createStream(serverUniStream).value(); + stream->send.state = StreamSendStates::Closed{}; conn.streamManager->removeClosedStream(serverUniStream); EXPECT_TRUE( @@ -1679,7 +1680,10 @@ TEST_F(QuicStreamFunctionsTest, StreamExists) { EXPECT_FALSE(conn.streamManager->streamExists(peerStream)); EXPECT_FALSE(conn.streamManager->streamExists(peerAutoOpened)); - conn.streamManager->getStream(peerStream)->state = StreamStates::Closed{}; + conn.streamManager->getStream(peerStream)->send.state = + StreamSendStates::Closed{}; + conn.streamManager->getStream(peerStream)->recv.state = + StreamReceiveStates::Closed{}; EXPECT_TRUE(conn.streamManager->streamExists(localStream)); EXPECT_TRUE(conn.streamManager->streamExists(localAutoOpened)); EXPECT_FALSE(conn.streamManager->streamExists(notOpenedLocal)); diff --git a/quic/state/test/StateMachineTest.cpp b/quic/state/test/StateMachineTest.cpp index 48eee8c9a..f8d73373e 100644 --- a/quic/state/test/StateMachineTest.cpp +++ b/quic/state/test/StateMachineTest.cpp @@ -41,6 +41,7 @@ void ThrowExceptionHandler(const ConnectionState&) { struct TestMachine { using StateData = ConnectionState; + using UserData = ConnectionState; static auto constexpr InvalidEventHandler = &ThrowExceptionHandler; }; @@ -51,14 +52,16 @@ QUIC_DECLARE_STATE_HANDLER(TestMachine, State2, Event2, State1, State3); // The handlers void Handler::handle( ConnectionState& connState, - Event1 /*event*/) { + Event1 /*event*/, + TestMachine::UserData&) { connState.visitedState1Event1 = true; transit(connState); } void Handler::handle( ConnectionState& connState, - Event2 /*event*/) { + Event2 /*event*/, + TestMachine::UserData&) { connState.visitedState1Event2 = true; transit(connState); // transit(connState); This will fail to compile because it @@ -67,7 +70,8 @@ void Handler::handle( void Handler::handle( ConnectionState& connState, - Event2 /*event*/) { + Event2 /*event*/, + TestMachine::UserData&) { connState.visitedState2Event2 = true; transit(connState); } @@ -83,6 +87,7 @@ struct ConnectionStateT { template struct TestMachineT { using StateData = ConnectionStateT; + using UserData = ConnectionStateT; static void InvalidEventHandler(ConnectionStateT& /*s*/) { throw InvalidHandlerException("invalid state in template machine"); } @@ -95,7 +100,8 @@ QUIC_DECLARE_STATE_HANDLER_T(State2, Event2, State3); template void Handler::handle( typename Machine::StateData& s, - Event1) { + Event1, + typename Machine::UserData&) { s.visitedState1Event1 = true; transit(s); } @@ -103,7 +109,8 @@ void Handler::handle( template void Handler::handle( typename Machine::StateData& s, - Event2) { + Event2, + typename Machine::UserData&) { s.visitedState1Event2 = true; transit(s); } @@ -111,7 +118,8 @@ void Handler::handle( template void Handler::handle( typename Machine::StateData& s, - Event2) { + Event2, + typename Machine::UserData&) { s.visitedState2Event2 = true; transit(s); } @@ -126,7 +134,7 @@ class StateMachineTest : public Test { TEST_F(StateMachineTest, TestTransitions) { state.state = State1(); - invokeHandler(state, Event1()); + invokeHandler(state, Event1(), state); EXPECT_TRUE(state.visitedState1Event1); EXPECT_FALSE(state.visitedState1Event2); EXPECT_FALSE(state.visitedState2Event2); @@ -134,7 +142,7 @@ TEST_F(StateMachineTest, TestTransitions) { // check that the state is correct. boost::get(state.state); - invokeHandler(state, Event2()); + invokeHandler(state, Event2(), state); EXPECT_TRUE(state.visitedState1Event1); EXPECT_FALSE(state.visitedState1Event2); @@ -146,19 +154,20 @@ TEST_F(StateMachineTest, TestTransitions) { TEST_F(StateMachineTest, TestInvalid) { state.state = State2(); EXPECT_THROW( - invokeHandler(state, Event1()), InvalidHandlerException); + invokeHandler(state, Event1(), state), + InvalidHandlerException); } TEST_F(StateMachineTest, TestTemplateTransitions) { stateT.state = State1(); - invokeHandler>(stateT, Event1()); + invokeHandler>(stateT, Event1(), stateT); EXPECT_TRUE(stateT.visitedState1Event1); EXPECT_FALSE(stateT.visitedState1Event2); EXPECT_FALSE(stateT.visitedState2Event2); boost::get(stateT.state); - invokeHandler>(stateT, Event2()); + invokeHandler>(stateT, Event2(), stateT); EXPECT_TRUE(stateT.visitedState1Event1); EXPECT_FALSE(stateT.visitedState1Event2); @@ -170,7 +179,7 @@ TEST_F(StateMachineTest, TestTemplateTransitions) { TEST_F(StateMachineTest, TestTemplateInvalid) { stateT.state = State2(); EXPECT_THROW( - invokeHandler>(stateT, Event1()), + invokeHandler>(stateT, Event1(), stateT), InvalidHandlerException); } }