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

Support for TX timestamping

Summary:
Adds support for timestamping on TX (TX byte events). This allows the application to determine when a byte that it previously wrote to the transport was put onto the wire.

Callbacks are processed within a new function `QuicTransportBase::processCallbacksAfterWriteData`, which is invoked by `writeSocketDataAndCatch`.

Reviewed By: mjoras

Differential Revision: D22008855

fbshipit-source-id: 99c1697cb74bb2387dbad231611be58f9392c99f
This commit is contained in:
Brandon Schlinker
2020-07-16 22:44:15 -07:00
committed by Facebook GitHub Bot
parent d332cc4e0c
commit b4df09831b
16 changed files with 1446 additions and 38 deletions

View File

@@ -28,6 +28,7 @@ namespace quic {
namespace test {
constexpr uint8_t kStreamIncrement = 0x04;
using ByteEvent = QuicTransportBase::ByteEvent;
enum class TestFrameType : uint8_t {
STREAM,
@@ -365,6 +366,22 @@ class TestQuicTransport
}
deliveryCallbacks_.erase(deliveryCb);
}
auto txCallbacksForStream = txCallbacks_.find(id);
if (txCallbacksForStream != txCallbacks_.end()) {
for (auto& cbs : txCallbacksForStream->second) {
ByteEvent event = {};
event.id = id;
event.offset = cbs.first;
event.type = ByteEvent::Type::TX;
cbs.second->onByteEvent(event);
if (closeState_ != CloseState::OPEN) {
break;
}
}
txCallbacks_.erase(txCallbacksForStream);
}
SocketAddress addr("127.0.0.1", 1000);
// some fake data to trigger close behavior.
auto buf = encodeStreamBuffer(
@@ -436,6 +453,10 @@ class QuicTransportImplTest : public Test {
kDefaultMaxStreamsUnidirectional);
}
auto getTxMatcher(StreamId id, uint64_t offset) {
return MockByteEventCallback::getTxMatcher(id, offset);
}
protected:
std::unique_ptr<folly::EventBase> evb;
NiceMock<MockConnectionCallback> connCallback;
@@ -1365,91 +1386,621 @@ TEST_F(QuicTransportImplTest, DeliveryCallbackUnsetOne) {
transport->close(folly::none);
}
TEST_F(QuicTransportImplTest, DeliveryCallbackOnSendDataExpire) {
TEST_F(QuicTransportImplTest, TxDeliveryCallbackOnSendDataExpire) {
transport->transportConn->partialReliabilityEnabled = true;
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream2, 10, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
transport->registerDeliveryCallback(stream2, 10, &dcb2);
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(_, _));
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
auto res = transport->sendDataExpired(stream1, 11);
EXPECT_EQ(res.hasError(), false);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_CALL(txcb1, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 10)));
EXPECT_CALL(dcb1, onCanceled(_, _)).Times(0);
EXPECT_CALL(dcb2, onCanceled(_, _));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
}
TEST_F(QuicTransportImplTest, DeliveryCallbackOnSendDataExpireCallbacksLeft) {
TEST_F(QuicTransportImplTest, TxDeliveryCallbackOnSendDataExpireCallbacksLeft) {
transport->transportConn->partialReliabilityEnabled = true;
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream1, 20, &txcb1);
transport->registerTxCallback(stream2, 10, &txcb2);
transport->registerTxCallback(stream2, 20, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream1, 20, &dcb1);
transport->registerDeliveryCallback(stream2, 10, &dcb2);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(_, _));
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
auto res = transport->sendDataExpired(stream1, 11);
EXPECT_EQ(res.hasError(), false);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_CALL(dcb2, onCanceled(_, _));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 20)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 20)));
EXPECT_CALL(dcb1, onCanceled(_, _)).Times(1);
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(2);
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
}
TEST_F(QuicTransportImplTest, RegisterDeliveryCallbackLowerThanExpected) {
TEST_F(QuicTransportImplTest, RegisterTxDeliveryCallbackLowerThanExpected) {
auto stream = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
StrictMock<MockByteEventCallback> txcb3;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
NiceMock<MockDeliveryCallback> dcb3;
transport->registerTxCallback(stream, 10, &txcb1);
transport->registerTxCallback(stream, 20, &txcb2);
transport->registerDeliveryCallback(stream, 10, &dcb1);
transport->registerDeliveryCallback(stream, 20, &dcb2);
auto streamState = transport->transportConn->streamManager->getStream(stream);
streamState->currentWriteOffset = 7;
streamState->ackedIntervals.insert(0, 6);
EXPECT_CALL(dcb3, onDeliveryAck(_, _, _))
.WillOnce(Invoke(
[](auto /* id */, auto offset, auto) { EXPECT_EQ(offset, 2); }));
EXPECT_CALL(txcb3, onByteEvent(getTxMatcher(stream, 2)));
EXPECT_CALL(dcb3, onDeliveryAck(stream, 2, _));
transport->registerTxCallback(stream, 2, &txcb3);
transport->registerDeliveryCallback(stream, 2, &dcb3);
evb->loopOnce();
Mock::VerifyAndClearExpectations(&txcb3);
Mock::VerifyAndClearExpectations(&dcb3);
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream, 20)));
EXPECT_CALL(dcb1, onCanceled(_, _));
EXPECT_CALL(dcb2, onCanceled(_, _));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&txcb3);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
Mock::VerifyAndClearExpectations(&dcb3);
}
TEST_F(QuicTransportImplTest, RegisterDeliveryCallbackLowerThanExpectedClose) {
TEST_F(
QuicTransportImplTest,
RegisterTxDeliveryCallbackLowerThanExpectedClose) {
auto stream = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb;
NiceMock<MockDeliveryCallback> dcb;
auto streamState = transport->transportConn->streamManager->getStream(stream);
streamState->currentWriteOffset = 7;
EXPECT_CALL(txcb, onByteEventCanceled(getTxMatcher(stream, 2)));
EXPECT_CALL(dcb, onCanceled(_, _));
transport->registerTxCallback(stream, 2, &txcb);
transport->registerDeliveryCallback(stream, 2, &dcb);
transport->close(folly::none);
evb->loopOnce();
Mock::VerifyAndClearExpectations(&txcb);
Mock::VerifyAndClearExpectations(&dcb);
}
TEST_F(QuicTransportImplTest, CancelAllByteEventCallbacks) {
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
NiceMock<MockByteEventCallback> txcb1;
NiceMock<MockByteEventCallback> txcb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream2, 20, &txcb2);
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 20)));
EXPECT_CALL(dcb1, onCanceled(_, _));
EXPECT_CALL(dcb2, onCanceled(_, _));
transport->cancelAllByteEventCallbacks();
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(_, _)).Times(0);
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
}
TEST_F(QuicTransportImplTest, CancelByteEventCallbacksForStream) {
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream2, 20, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(stream1, 10));
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
transport->cancelByteEventCallbacksForStream(stream1);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
1,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 20)));
EXPECT_CALL(dcb1, onCanceled(stream1, _)).Times(0);
EXPECT_CALL(dcb2, onCanceled(_, 20));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
}
TEST_F(QuicTransportImplTest, CancelByteEventCallbacksForStreamWithOffset) {
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream1, 15, &txcb1);
transport->registerTxCallback(stream1, 20, &txcb1);
transport->registerTxCallback(stream2, 10, &txcb2);
transport->registerTxCallback(stream2, 15, &txcb2);
transport->registerTxCallback(stream2, 20, &txcb2);
EXPECT_EQ(3, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(3, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream1, 15, &dcb1);
transport->registerDeliveryCallback(stream1, 20, &dcb1);
transport->registerDeliveryCallback(stream2, 10, &dcb2);
transport->registerDeliveryCallback(stream2, 15, &dcb2);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
EXPECT_EQ(6, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(6, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(dcb1, onCanceled(stream1, 10));
// cancels if offset is < (not <=) offset provided
transport->cancelByteEventCallbacksForStream(stream1, 15);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(4, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(6, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 15)));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 20)));
EXPECT_CALL(dcb1, onCanceled(stream1, 15));
EXPECT_CALL(dcb1, onCanceled(stream1, 20));
// cancels if offset is < (not <=) offset provided
transport->cancelByteEventCallbacksForStream(stream1, 21);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(6, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
3,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 15)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 20)));
EXPECT_CALL(dcb2, onCanceled(stream2, 10));
EXPECT_CALL(dcb2, onCanceled(stream2, 15));
EXPECT_CALL(dcb2, onCanceled(stream2, 20));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(0, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
}
TEST_F(QuicTransportImplTest, CancelByteEventCallbacksTx) {
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream1, 15, &txcb1);
transport->registerTxCallback(stream2, 10, &txcb2);
transport->registerTxCallback(stream2, 15, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream1, 15, &dcb1);
transport->registerDeliveryCallback(stream2, 10, &dcb2);
transport->registerDeliveryCallback(stream2, 15, &dcb2);
EXPECT_EQ(4, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(4, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 15)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 15)));
transport->cancelByteEventCallbacks(ByteEvent::Type::TX);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(dcb1, onCanceled(stream1, 10));
EXPECT_CALL(dcb1, onCanceled(stream1, 15));
EXPECT_CALL(dcb2, onCanceled(stream2, 10));
EXPECT_CALL(dcb2, onCanceled(stream2, 15));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
}
TEST_F(QuicTransportImplTest, CancelByteEventCallbacksDelivery) {
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream1, 15, &txcb1);
transport->registerTxCallback(stream2, 10, &txcb2);
transport->registerTxCallback(stream2, 15, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream1, 15, &dcb1);
transport->registerDeliveryCallback(stream2, 10, &dcb2);
transport->registerDeliveryCallback(stream2, 15, &dcb2);
EXPECT_EQ(4, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(4, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(dcb1, onCanceled(stream1, 10));
EXPECT_CALL(dcb1, onCanceled(stream1, 15));
EXPECT_CALL(dcb2, onCanceled(stream2, 10));
EXPECT_CALL(dcb2, onCanceled(stream2, 15));
transport->cancelByteEventCallbacks(ByteEvent::Type::ACK);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream1));
EXPECT_EQ(2, transport->getNumByteEventCallbacksForStream(stream2));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream1));
EXPECT_EQ(
2,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::TX, stream2));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream1));
EXPECT_EQ(
0,
transport->getNumByteEventCallbacksForStream(
ByteEvent::Type::ACK, stream2));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10)));
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 15)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 10)));
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 15)));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
}
TEST_F(QuicTransportImplTest, TestNotifyPendingConnWriteOnCloseWithoutError) {
@@ -1519,6 +2070,7 @@ TEST_P(QuicTransportImplTestClose, TestNotifyPendingWriteOnCloseWithError) {
}
evb->loopOnce();
}
TEST_F(QuicTransportImplTest, TestTransportCloseWithMaxPacketNumber) {
transport->setServerConnectionId();
transport->transportConn->pendingEvents.closeTransport = false;
@@ -1527,6 +2079,7 @@ TEST_F(QuicTransportImplTest, TestTransportCloseWithMaxPacketNumber) {
transport->transportConn->pendingEvents.closeTransport = true;
EXPECT_THROW(transport->invokeWriteSocketData(), QuicTransportException);
}
TEST_F(QuicTransportImplTest, TestGracefulCloseWithActiveStream) {
EXPECT_CALL(connCallback, onConnectionEnd()).Times(0);
EXPECT_CALL(connCallback, onConnectionError(_)).Times(0);
@@ -1535,13 +2088,16 @@ TEST_F(QuicTransportImplTest, TestGracefulCloseWithActiveStream) {
NiceMock<MockWriteCallback> wcb;
NiceMock<MockWriteCallback> wcbConn;
NiceMock<MockReadCallback> rcb;
NiceMock<MockDeliveryCallback> deliveryCb;
StrictMock<MockByteEventCallback> txCb;
StrictMock<MockDeliveryCallback> deliveryCb;
EXPECT_CALL(
wcb, onStreamWriteError(stream, IsError(LocalErrorCode::NO_ERROR)));
EXPECT_CALL(
wcbConn, onConnectionWriteError(IsError(LocalErrorCode::NO_ERROR)));
EXPECT_CALL(rcb, readError(stream, IsError(LocalErrorCode::NO_ERROR)));
EXPECT_CALL(deliveryCb, onCanceled(stream, _));
EXPECT_CALL(txCb, onByteEventCanceled(getTxMatcher(stream, 0)));
EXPECT_CALL(txCb, onByteEventCanceled(getTxMatcher(stream, 4)));
transport->notifyPendingWriteOnConnection(&wcbConn);
transport->notifyPendingWriteOnStream(stream, &wcb);
@@ -1550,6 +2106,8 @@ TEST_F(QuicTransportImplTest, TestGracefulCloseWithActiveStream) {
.WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1));
transport->writeChain(
stream, IOBuf::copyBuffer("hello"), true, false, &deliveryCb);
EXPECT_FALSE(transport->registerTxCallback(stream, 0, &txCb).hasError());
EXPECT_FALSE(transport->registerTxCallback(stream, 4, &txCb).hasError());
transport->closeGracefully();
ASSERT_FALSE(transport->transportClosed);
@@ -1558,6 +2116,7 @@ TEST_F(QuicTransportImplTest, TestGracefulCloseWithActiveStream) {
EXPECT_TRUE(transport->setReadCallback(stream, &rcb).hasError());
EXPECT_TRUE(transport->notifyPendingWriteOnStream(stream, &wcb).hasError());
EXPECT_TRUE(transport->notifyPendingWriteOnConnection(&wcbConn).hasError());
EXPECT_TRUE(transport->registerTxCallback(stream, 2, &txCb).hasError());
EXPECT_TRUE(
transport->registerDeliveryCallback(stream, 2, &deliveryCb).hasError());
EXPECT_TRUE(
@@ -1584,9 +2143,12 @@ TEST_F(QuicTransportImplTest, TestGracefulCloseWithNoActiveStream) {
NiceMock<MockWriteCallback> wcbConn;
NiceMock<MockReadCallback> rcb;
NiceMock<MockDeliveryCallback> deliveryCb;
NiceMock<MockByteEventCallback> txCb;
EXPECT_CALL(
rcb, readError(stream, IsError(GenericApplicationErrorCode::NO_ERROR)));
EXPECT_CALL(deliveryCb, onDeliveryAck(stream, _, _));
EXPECT_CALL(txCb, onByteEvent(getTxMatcher(stream, 0)));
EXPECT_CALL(txCb, onByteEvent(getTxMatcher(stream, 4)));
EXPECT_CALL(connCallback, onConnectionEnd()).Times(0);
EXPECT_CALL(connCallback, onConnectionError(_)).Times(0);
@@ -1596,11 +2158,14 @@ TEST_F(QuicTransportImplTest, TestGracefulCloseWithNoActiveStream) {
.WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1));
transport->writeChain(
stream, IOBuf::copyBuffer("hello"), true, false, &deliveryCb);
EXPECT_FALSE(transport->registerTxCallback(stream, 0, &txCb).hasError());
EXPECT_FALSE(transport->registerTxCallback(stream, 4, &txCb).hasError());
// Close the last stream.
auto streamState = transport->transportConn->streamManager->getStream(stream);
// Fake that the data was delivered to keep all the state consistent.
// Fake that the data was TXed and delivered to keep all the state consistent.
streamState->currentWriteOffset = 7;
transport->transportConn->streamManager->addTx(stream);
transport->transportConn->streamManager->addDeliverable(stream);
transport->closeStream(stream);
transport->close(folly::none);
@@ -1611,6 +2176,7 @@ TEST_F(QuicTransportImplTest, TestGracefulCloseWithNoActiveStream) {
EXPECT_TRUE(transport->setReadCallback(stream, &rcb).hasError());
EXPECT_TRUE(transport->notifyPendingWriteOnStream(stream, &wcb).hasError());
EXPECT_TRUE(transport->notifyPendingWriteOnConnection(&wcbConn).hasError());
EXPECT_TRUE(transport->registerTxCallback(stream, 2, &txCb).hasError());
EXPECT_TRUE(
transport->registerDeliveryCallback(stream, 2, &deliveryCb).hasError());
EXPECT_TRUE(
@@ -1624,6 +2190,7 @@ TEST_F(QuicTransportImplTest, TestImmediateClose) {
NiceMock<MockWriteCallback> wcbConn;
NiceMock<MockReadCallback> rcb;
NiceMock<MockDeliveryCallback> deliveryCb;
NiceMock<MockByteEventCallback> txCb;
EXPECT_CALL(
wcb,
onStreamWriteError(
@@ -1634,6 +2201,8 @@ TEST_F(QuicTransportImplTest, TestImmediateClose) {
EXPECT_CALL(
rcb, readError(stream, IsAppError(GenericApplicationErrorCode::UNKNOWN)));
EXPECT_CALL(deliveryCb, onCanceled(stream, _));
EXPECT_CALL(txCb, onByteEventCanceled(getTxMatcher(stream, 0)));
EXPECT_CALL(txCb, onByteEventCanceled(getTxMatcher(stream, 4)));
EXPECT_CALL(connCallback, onConnectionError(_)).Times(0);
@@ -1644,6 +2213,8 @@ TEST_F(QuicTransportImplTest, TestImmediateClose) {
.WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1));
transport->writeChain(
stream, IOBuf::copyBuffer("hello"), true, false, &deliveryCb);
EXPECT_FALSE(transport->registerTxCallback(stream, 0, &txCb).hasError());
EXPECT_FALSE(transport->registerTxCallback(stream, 4, &txCb).hasError());
transport->close(std::make_pair(
QuicErrorCode(GenericApplicationErrorCode::UNKNOWN),
std::string("Error")));
@@ -1654,6 +2225,7 @@ TEST_F(QuicTransportImplTest, TestImmediateClose) {
EXPECT_TRUE(transport->setReadCallback(stream, &rcb).hasError());
EXPECT_TRUE(transport->notifyPendingWriteOnStream(stream, &wcb).hasError());
EXPECT_TRUE(transport->notifyPendingWriteOnConnection(&wcbConn).hasError());
EXPECT_TRUE(transport->registerTxCallback(stream, 2, &txCb).hasError());
EXPECT_TRUE(
transport->registerDeliveryCallback(stream, 2, &deliveryCb).hasError());
EXPECT_TRUE(
@@ -1863,6 +2435,20 @@ TEST_F(QuicTransportImplTest, UnidirectionalInvalidWriteFuncs) {
transport->registerDeliveryCallback(stream, 0, nullptr)
.thenOrThrow([&](auto) {}),
folly::Unexpected<LocalErrorCode>::BadExpectedAccess);
EXPECT_THROW(
transport->registerTxCallback(stream, 0, nullptr).thenOrThrow([&](auto) {
}),
folly::Unexpected<LocalErrorCode>::BadExpectedAccess);
EXPECT_THROW(
transport
->registerByteEventCallback(ByteEvent::Type::ACK, stream, 0, nullptr)
.thenOrThrow([&](auto) {}),
folly::Unexpected<LocalErrorCode>::BadExpectedAccess);
EXPECT_THROW(
transport
->registerByteEventCallback(ByteEvent::Type::TX, stream, 0, nullptr)
.thenOrThrow([&](auto) {}),
folly::Unexpected<LocalErrorCode>::BadExpectedAccess);
EXPECT_THROW(
transport->resetStream(stream, GenericApplicationErrorCode::UNKNOWN)
.thenOrThrow([&](auto) {}),
@@ -2524,43 +3110,58 @@ TEST_F(QuicTransportImplTest, DataRejecteddCallbackDataAvailable) {
transport.reset();
}
TEST_F(QuicTransportImplTest, DataRejecteddCallbackWithDeliveryCallbacks) {
TEST_F(QuicTransportImplTest, DataRejecteddCallbackWithTxAndDeliveryCallbacks) {
transport->transportConn->partialReliabilityEnabled = true;
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
NiceMock<MockDataRejectedCallback> dataRejectedCb1;
NiceMock<MockDataRejectedCallback> dataRejectedCb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream2, 20, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
transport->setDataRejectedCallback(stream1, &dataRejectedCb1);
transport->setDataRejectedCallback(stream2, &dataRejectedCb2);
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10))).Times(1);
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(stream1, 10)).Times(1);
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
EXPECT_CALL(dataRejectedCb1, onDataRejected(stream1, 15));
transport->addMinStreamDataFrameToStream(
MinStreamDataFrame(stream1, kDefaultStreamWindowSize, 15));
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
Mock::VerifyAndClearExpectations(&dataRejectedCb1);
EXPECT_CALL(txcb1, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 20))).Times(1);
EXPECT_CALL(dcb1, onCanceled(_, _)).Times(0);
EXPECT_CALL(dcb2, onCanceled(stream2, 20)).Times(1);
EXPECT_CALL(dataRejectedCb2, onDataRejected(stream2, 23));
transport->addMinStreamDataFrameToStream(
MinStreamDataFrame(stream2, kDefaultStreamWindowSize, 23));
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
Mock::VerifyAndClearExpectations(&dataRejectedCb2);
EXPECT_CALL(txcb1, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(_, _)).Times(0);
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
transport->close(folly::none);
@@ -2568,17 +3169,24 @@ TEST_F(QuicTransportImplTest, DataRejecteddCallbackWithDeliveryCallbacks) {
TEST_F(
QuicTransportImplTest,
DataRejecteddCallbackWithDeliveryCallbacksSomeLeft) {
DataRejecteddCallbackWithTxAndDeliveryCallbacksSomeLeft) {
transport->transportConn->partialReliabilityEnabled = true;
auto stream1 = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
StrictMock<MockByteEventCallback> txcb1;
StrictMock<MockByteEventCallback> txcb2;
NiceMock<MockDeliveryCallback> dcb1;
NiceMock<MockDeliveryCallback> dcb2;
NiceMock<MockDataRejectedCallback> dataRejectedCb1;
NiceMock<MockDataRejectedCallback> dataRejectedCb2;
transport->registerTxCallback(stream1, 10, &txcb1);
transport->registerTxCallback(stream1, 25, &txcb1);
transport->registerTxCallback(stream2, 20, &txcb2);
transport->registerTxCallback(stream2, 29, &txcb2);
transport->registerDeliveryCallback(stream1, 10, &dcb1);
transport->registerDeliveryCallback(stream1, 25, &dcb1);
transport->registerDeliveryCallback(stream2, 20, &dcb2);
@@ -2587,26 +3195,36 @@ TEST_F(
transport->setDataRejectedCallback(stream1, &dataRejectedCb1);
transport->setDataRejectedCallback(stream2, &dataRejectedCb2);
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 10))).Times(1);
EXPECT_CALL(txcb2, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(dcb1, onCanceled(stream1, 10)).Times(1);
EXPECT_CALL(dcb2, onCanceled(_, _)).Times(0);
EXPECT_CALL(dataRejectedCb1, onDataRejected(stream1, 15));
transport->addMinStreamDataFrameToStream(
MinStreamDataFrame(stream1, kDefaultStreamWindowSize, 15));
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
Mock::VerifyAndClearExpectations(&dataRejectedCb1);
EXPECT_CALL(txcb1, onByteEventCanceled(_)).Times(0);
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 20))).Times(1);
EXPECT_CALL(dcb1, onCanceled(_, _)).Times(0);
EXPECT_CALL(dcb2, onCanceled(stream2, 20)).Times(1);
EXPECT_CALL(dataRejectedCb2, onDataRejected(stream2, 23));
transport->addMinStreamDataFrameToStream(
MinStreamDataFrame(stream2, kDefaultStreamWindowSize, 23));
Mock::VerifyAndClearExpectations(&txcb1);
Mock::VerifyAndClearExpectations(&txcb2);
Mock::VerifyAndClearExpectations(&dcb1);
Mock::VerifyAndClearExpectations(&dcb2);
Mock::VerifyAndClearExpectations(&dataRejectedCb2);
EXPECT_CALL(txcb1, onByteEventCanceled(getTxMatcher(stream1, 25))).Times(1);
EXPECT_CALL(txcb2, onByteEventCanceled(getTxMatcher(stream2, 29))).Times(1);
EXPECT_CALL(dcb1, onCanceled(stream1, 25)).Times(1);
EXPECT_CALL(dcb2, onCanceled(stream2, 29)).Times(1);
transport->close(folly::none);