mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-08 09:42:06 +03:00
Improve transport close handling and observability
Summary: The current `close` observer event marks when `closeImpl` is called. However, the actual close of the socket may be delayed for some time while the socket drains. I've changed the existing event to `closeStarted` and added a new event `closing` that marks the close of the underlying `AsyncUDPSocket`. Adding the `closeStarted` event required two other changes which are difficult to separate from this diff: - When a transport is destroyed, `QuicTransportBase` was calling `closeImpl` and also closing the UDP socket. However, because the `folly::ObserverContainer` used to store observers is maintained in classes that derive from `QuicTransportBase`, the observers are gone by the time the UDP socket is closed in the base class destructor. Thus, the UDP socket should be closed by the derived classes in their respective destructors. This requirement is inline with the existing code: `closeImpl` is called by all derived classes in their destructors. Made this change and added `DCHECK` statements in the `QuicTransportBase` destructor to ensure that derived classes cleanup after themselves. - Writing tests with draining enabled and disabled required being able to set the transport settings. However, all of the existing `QuicTypedTransportTest` test cases were designed to operate after the connection was accepted (for the server impls) or established (for client impls), and transport settings cannot be updated at this state. Resolving this required adding new test classes in which the accept/connect operation is delayed until requested by the test. Reviewed By: mjoras Differential Revision: D39249604 fbshipit-source-id: 0ebf8b719c4d3b01d4f9509cf2b9a4fc72c2e737
This commit is contained in:
committed by
Facebook GitHub Bot
parent
ced772c63f
commit
5b8e0de5bd
@@ -145,20 +145,10 @@ const folly::SocketAddress& QuicTransportBase::getLocalAddress() const {
|
||||
QuicTransportBase::~QuicTransportBase() {
|
||||
resetConnectionCallbacks();
|
||||
|
||||
closeImpl(
|
||||
QuicError(
|
||||
QuicErrorCode(LocalErrorCode::SHUTTING_DOWN),
|
||||
std::string("Closing from base destructor")),
|
||||
false);
|
||||
// If a drainTimeout is already scheduled, then closeNow above
|
||||
// won't do anything. We have to manually clean up the socket. Timeout will be
|
||||
// canceled by timer's destructor.
|
||||
if (socket_) {
|
||||
auto sock = std::move(socket_);
|
||||
socket_ = nullptr;
|
||||
sock->pauseRead();
|
||||
sock->close();
|
||||
}
|
||||
// closeImpl and closeUdpSocket should have been triggered by destructor of
|
||||
// derived class to ensure that observers are properly notified
|
||||
DCHECK_NE(CloseState::OPEN, closeState_);
|
||||
DCHECK(!socket_.get()); // should be no socket
|
||||
}
|
||||
|
||||
bool QuicTransportBase::good() const {
|
||||
@@ -247,9 +237,11 @@ void QuicTransportBase::closeImpl(
|
||||
}
|
||||
|
||||
if (getSocketObserverContainer()) {
|
||||
SocketObserverInterface::CloseStartedEvent event;
|
||||
event.maybeCloseReason = errorCode;
|
||||
getSocketObserverContainer()->invokeInterfaceMethodAllObservers(
|
||||
[errorCode](auto observer, auto observed) {
|
||||
observer->close(observed, errorCode);
|
||||
[&event](auto observer, auto observed) {
|
||||
observer->closeStarted(observed, event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -437,6 +429,23 @@ void QuicTransportBase::closeImpl(
|
||||
}
|
||||
}
|
||||
|
||||
void QuicTransportBase::closeUdpSocket() {
|
||||
if (!socket_) {
|
||||
return;
|
||||
}
|
||||
if (getSocketObserverContainer()) {
|
||||
SocketObserverInterface::ClosingEvent event; // empty for now
|
||||
getSocketObserverContainer()->invokeInterfaceMethodAllObservers(
|
||||
[&event](auto observer, auto observed) {
|
||||
observer->closing(observed, event);
|
||||
});
|
||||
}
|
||||
auto sock = std::move(socket_);
|
||||
socket_ = nullptr;
|
||||
sock->pauseRead();
|
||||
sock->close();
|
||||
}
|
||||
|
||||
bool QuicTransportBase::processCancelCode(const QuicError& cancelCode) {
|
||||
bool noError = false;
|
||||
switch (cancelCode.code.type()) {
|
||||
@@ -494,12 +503,7 @@ void QuicTransportBase::processConnectionCallbacks(
|
||||
}
|
||||
|
||||
void QuicTransportBase::drainTimeoutExpired() noexcept {
|
||||
if (socket_) {
|
||||
auto sock = std::move(socket_);
|
||||
socket_ = nullptr;
|
||||
sock->pauseRead();
|
||||
sock->close();
|
||||
}
|
||||
closeUdpSocket();
|
||||
unbindConnection();
|
||||
}
|
||||
|
||||
|
@@ -738,6 +738,7 @@ class QuicTransportBase : public QuicSocket, QuicStreamPrioritiesObserver {
|
||||
folly::Optional<QuicError> error,
|
||||
bool drainConnection = true,
|
||||
bool sendCloseImmediately = true);
|
||||
void closeUdpSocket();
|
||||
folly::Expected<folly::Unit, LocalErrorCode> pauseOrResumeRead(
|
||||
StreamId id,
|
||||
bool resume);
|
||||
|
@@ -300,9 +300,10 @@ class MockObserver : public QuicSocket::ManagedObserver {
|
||||
(noexcept));
|
||||
MOCK_METHOD(
|
||||
(void),
|
||||
close,
|
||||
(QuicSocket*, const folly::Optional<QuicError>&),
|
||||
closeStarted,
|
||||
(QuicSocket*, const CloseStartedEvent&),
|
||||
(noexcept));
|
||||
MOCK_METHOD((void), closing, (QuicSocket*, const ClosingEvent&), (noexcept));
|
||||
};
|
||||
|
||||
class MockLegacyObserver : public LegacyObserver {
|
||||
@@ -311,13 +312,14 @@ class MockLegacyObserver : public LegacyObserver {
|
||||
MOCK_METHOD((void), observerAttach, (QuicSocket*), (noexcept));
|
||||
MOCK_METHOD((void), observerDetach, (QuicSocket*), (noexcept));
|
||||
MOCK_METHOD((void), destroy, (QuicSocket*), (noexcept));
|
||||
MOCK_METHOD((void), evbAttach, (QuicSocket*, folly::EventBase*), (noexcept));
|
||||
MOCK_METHOD((void), evbDetach, (QuicSocket*, folly::EventBase*), (noexcept));
|
||||
MOCK_METHOD(
|
||||
(void),
|
||||
close,
|
||||
(QuicSocket*, const folly::Optional<QuicError>&),
|
||||
closeStarted,
|
||||
(QuicSocket*, const CloseStartedEvent&),
|
||||
(noexcept));
|
||||
MOCK_METHOD((void), closing, (QuicSocket*, const ClosingEvent&), (noexcept));
|
||||
MOCK_METHOD((void), evbAttach, (QuicSocket*, folly::EventBase*), (noexcept));
|
||||
MOCK_METHOD((void), evbDetach, (QuicSocket*, folly::EventBase*), (noexcept));
|
||||
MOCK_METHOD(
|
||||
(void),
|
||||
startWritingFromAppLimited,
|
||||
|
@@ -247,11 +247,14 @@ class TestQuicTransport
|
||||
~TestQuicTransport() override {
|
||||
resetConnectionCallbacks();
|
||||
// we need to call close in the derived class.
|
||||
resetConnectionCallbacks();
|
||||
closeImpl(
|
||||
QuicError(
|
||||
QuicErrorCode(LocalErrorCode::SHUTTING_DOWN),
|
||||
std::string("shutdown")),
|
||||
false);
|
||||
false /* drainConnection */);
|
||||
// closeImpl may have been called earlier with drain = true, so force close.
|
||||
closeUdpSocket();
|
||||
}
|
||||
|
||||
std::chrono::milliseconds getLossTimeoutRemainingTime() const {
|
||||
@@ -3703,7 +3706,8 @@ TEST_P(QuicTransportImplTestBase, ObserverDestroy) {
|
||||
transport->addObserver(cb.get());
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cb, close(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closing(transport.get(), _));
|
||||
EXPECT_CALL(*cb, destroy(transport.get()));
|
||||
transport = nullptr;
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
@@ -3732,7 +3736,8 @@ TEST_P(QuicTransportImplTestBase, ObserverSharedPtrDestroy) {
|
||||
transport->addObserver(cb);
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cb, close(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closing(transport.get(), _));
|
||||
EXPECT_CALL(*cb, destroy(transport.get()));
|
||||
transport = nullptr;
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
@@ -3752,7 +3757,8 @@ TEST_P(QuicTransportImplTestBase, ObserverSharedPtrReleasedDestroy) {
|
||||
EXPECT_FALSE(dc.destroyed()); // should still exist
|
||||
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cbRaw, close(transport.get(), _));
|
||||
EXPECT_CALL(*cbRaw, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cbRaw, closing(transport.get(), _));
|
||||
EXPECT_CALL(*cbRaw, destroy(transport.get()));
|
||||
transport = nullptr;
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
@@ -3764,44 +3770,6 @@ TEST_P(QuicTransportImplTestBase, ObserverSharedPtrRemoveMissing) {
|
||||
EXPECT_THAT(transport->getObservers(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST_P(QuicTransportImplTestBase, ObserverCloseNoErrorThenDestroy) {
|
||||
auto cb = std::make_unique<StrictMock<MockLegacyObserver>>();
|
||||
EXPECT_CALL(*cb, observerAttach(transport.get()));
|
||||
transport->addObserver(cb.get());
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
|
||||
const QuicError defaultError = QuicError(
|
||||
GenericApplicationErrorCode::NO_ERROR,
|
||||
toString(GenericApplicationErrorCode::NO_ERROR));
|
||||
EXPECT_CALL(
|
||||
*cb, close(transport.get(), folly::Optional<QuicError>(defaultError)));
|
||||
transport->close(folly::none);
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cb, destroy(transport.get()));
|
||||
transport = nullptr;
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
}
|
||||
|
||||
TEST_P(QuicTransportImplTestBase, ObserverCloseWithErrorThenDestroy) {
|
||||
auto cb = std::make_unique<StrictMock<MockLegacyObserver>>();
|
||||
EXPECT_CALL(*cb, observerAttach(transport.get()));
|
||||
transport->addObserver(cb.get());
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
|
||||
const auto testError = QuicError(
|
||||
QuicErrorCode(LocalErrorCode::CONNECTION_RESET),
|
||||
std::string("testError"));
|
||||
EXPECT_CALL(
|
||||
*cb, close(transport.get(), folly::Optional<QuicError>(testError)));
|
||||
transport->close(testError);
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cb, destroy(transport.get()));
|
||||
transport = nullptr;
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
}
|
||||
|
||||
TEST_P(QuicTransportImplTestBase, ObserverDetachImmediately) {
|
||||
auto cb = std::make_unique<StrictMock<MockLegacyObserver>>();
|
||||
EXPECT_CALL(*cb, observerAttach(transport.get()));
|
||||
@@ -3815,12 +3783,20 @@ TEST_P(QuicTransportImplTestBase, ObserverDetachImmediately) {
|
||||
}
|
||||
|
||||
TEST_P(QuicTransportImplTestBase, ObserverDetachAfterClose) {
|
||||
// disable draining to ensure closing() event occurs immediately after close()
|
||||
{
|
||||
auto transportSettings = transport->getTransportSettings();
|
||||
transportSettings.shouldDrain = false;
|
||||
transport->setTransportSettings(transportSettings);
|
||||
}
|
||||
|
||||
auto cb = std::make_unique<StrictMock<MockLegacyObserver>>();
|
||||
EXPECT_CALL(*cb, observerAttach(transport.get()));
|
||||
transport->addObserver(cb.get());
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
|
||||
EXPECT_CALL(*cb, close(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closing(transport.get(), _));
|
||||
transport->close(folly::none);
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
|
||||
@@ -3830,14 +3806,33 @@ TEST_P(QuicTransportImplTestBase, ObserverDetachAfterClose) {
|
||||
EXPECT_THAT(transport->getObservers(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QuicTransportImplTest, ObserverDetachOnCloseDuringDestroy) {
|
||||
TEST_F(QuicTransportImplTest, ObserverDetachOnCloseStartedDuringDestroy) {
|
||||
auto cb = std::make_unique<StrictMock<MockLegacyObserver>>();
|
||||
EXPECT_CALL(*cb, observerAttach(transport.get()));
|
||||
transport->addObserver(cb.get());
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cb, close(transport.get(), _))
|
||||
|
||||
EXPECT_CALL(*cb, closeStarted(transport.get(), _))
|
||||
.WillOnce(Invoke([&cb](auto callbackTransport, auto /* errorOpt */) {
|
||||
EXPECT_TRUE(callbackTransport->removeObserver(cb.get()));
|
||||
}));
|
||||
EXPECT_CALL(*cb, observerDetach(transport.get()));
|
||||
transport = nullptr;
|
||||
Mock::VerifyAndClearExpectations(cb.get());
|
||||
}
|
||||
|
||||
TEST_F(QuicTransportImplTest, ObserverDetachOnClosingDuringDestroy) {
|
||||
auto cb = std::make_unique<StrictMock<MockLegacyObserver>>();
|
||||
EXPECT_CALL(*cb, observerAttach(transport.get()));
|
||||
transport->addObserver(cb.get());
|
||||
EXPECT_THAT(transport->getObservers(), UnorderedElementsAre(cb.get()));
|
||||
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(*cb, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cb, closing(transport.get(), _))
|
||||
.WillOnce(Invoke([&cb](auto callbackTransport, auto /* errorOpt */) {
|
||||
EXPECT_TRUE(callbackTransport->removeObserver(cb.get()));
|
||||
}));
|
||||
@@ -3938,8 +3933,10 @@ TEST_P(QuicTransportImplTestBase, ObserverMultipleAttachDestroy) {
|
||||
transport->getObservers(), UnorderedElementsAre(cb1.get(), cb2.get()));
|
||||
|
||||
InSequence s;
|
||||
EXPECT_CALL(*cb1, close(transport.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport.get()));
|
||||
transport = nullptr;
|
||||
|
@@ -405,9 +405,12 @@ TEST_F(QuicTransportTest, ObserverNotAppLimitedWithNoWritableBytes) {
|
||||
loopForWrites();
|
||||
Mock::VerifyAndClearExpectations(cb1.get());
|
||||
Mock::VerifyAndClearExpectations(cb2.get());
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb3, destroy(transport_.get()));
|
||||
@@ -451,9 +454,12 @@ TEST_F(QuicTransportTest, ObserverNotAppLimitedWithLargeBuffer) {
|
||||
loopForWrites();
|
||||
Mock::VerifyAndClearExpectations(cb1.get());
|
||||
Mock::VerifyAndClearExpectations(cb2.get());
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb3, destroy(transport_.get()));
|
||||
@@ -499,9 +505,12 @@ TEST_F(QuicTransportTest, ObserverAppLimited) {
|
||||
Mock::VerifyAndClearExpectations(cb1.get());
|
||||
Mock::VerifyAndClearExpectations(cb2.get());
|
||||
Mock::VerifyAndClearExpectations(cb3.get());
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb3, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb3, destroy(transport_.get()));
|
||||
@@ -885,7 +894,10 @@ TEST_F(QuicTransportTest, ObserverPacketsWrittenCycleCheckDetails) {
|
||||
loopForWrites();
|
||||
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, close(transport_.get(), _));
|
||||
EXPECT_CALL(observer, closeStarted(transport_.get(), _));
|
||||
}));
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, closing(transport_.get(), _));
|
||||
}));
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, destroy(transport_.get()));
|
||||
@@ -1097,7 +1109,10 @@ TEST_F(QuicTransportTest, ObserverPacketsWrittenCheckBytesSent) {
|
||||
}
|
||||
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, close(transport_.get(), _));
|
||||
EXPECT_CALL(observer, closeStarted(transport_.get(), _));
|
||||
}));
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, closing(transport_.get(), _));
|
||||
}));
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, destroy(transport_.get()));
|
||||
@@ -1378,7 +1393,10 @@ TEST_F(QuicTransportTest, ObserverWriteEventsCheckCwndPacketsWritable) {
|
||||
}
|
||||
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, close(transport_.get(), _));
|
||||
EXPECT_CALL(observer, closeStarted(transport_.get(), _));
|
||||
}));
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, closing(transport_.get(), _));
|
||||
}));
|
||||
invokeForAllObservers(([this](MockLegacyObserver& observer) {
|
||||
EXPECT_CALL(observer, destroy(transport_.get()));
|
||||
@@ -1422,8 +1440,10 @@ TEST_F(QuicTransportTest, ObserverStreamEventBidirectionalLocalOpenClose) {
|
||||
SocketAddress("::1", 10000),
|
||||
NetworkData(IOBuf::copyBuffer("fake data"), Clock::now()));
|
||||
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
transport_ = nullptr;
|
||||
@@ -1464,8 +1484,10 @@ TEST_F(QuicTransportTest, ObserverStreamEventBidirectionalRemoteOpenClose) {
|
||||
SocketAddress("::1", 10000),
|
||||
NetworkData(IOBuf::copyBuffer("fake data"), Clock::now()));
|
||||
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
transport_ = nullptr;
|
||||
@@ -1506,8 +1528,10 @@ TEST_F(QuicTransportTest, ObserverStreamEventUnidirectionalLocalOpenClose) {
|
||||
SocketAddress("::1", 10000),
|
||||
NetworkData(IOBuf::copyBuffer("fake data"), Clock::now()));
|
||||
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
transport_ = nullptr;
|
||||
@@ -1547,8 +1571,10 @@ TEST_F(QuicTransportTest, ObserverStreamEventUnidirectionalRemoteOpenClose) {
|
||||
SocketAddress("::1", 10000),
|
||||
NetworkData(IOBuf::copyBuffer("fake data"), Clock::now()));
|
||||
|
||||
EXPECT_CALL(*cb1, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, close(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closeStarted(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb2, closing(transport_.get(), _));
|
||||
EXPECT_CALL(*cb1, destroy(transport_.get()));
|
||||
EXPECT_CALL(*cb2, destroy(transport_.get()));
|
||||
transport_ = nullptr;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,9 @@ class TestQuicTransport
|
||||
QuicError(
|
||||
QuicErrorCode(LocalErrorCode::SHUTTING_DOWN),
|
||||
std::string("shutdown")),
|
||||
false);
|
||||
false /* drainConnection */);
|
||||
// closeImpl may have been called earlier with drain = true, so force close.
|
||||
closeUdpSocket();
|
||||
}
|
||||
|
||||
QuicVersion getVersion() {
|
||||
|
@@ -103,7 +103,9 @@ QuicClientTransport::~QuicClientTransport() {
|
||||
QuicError(
|
||||
QuicErrorCode(LocalErrorCode::SHUTTING_DOWN),
|
||||
std::string("Closing from client destructor")),
|
||||
false);
|
||||
false /* drainConnection */);
|
||||
// closeImpl may have been called earlier with drain = true, so force close.
|
||||
closeUdpSocket();
|
||||
|
||||
if (clientConn_->happyEyeballsState.secondSocket) {
|
||||
auto sock = std::move(clientConn_->happyEyeballsState.secondSocket);
|
||||
|
@@ -44,6 +44,38 @@ class SocketObserverInterface {
|
||||
};
|
||||
virtual ~SocketObserverInterface() = default;
|
||||
|
||||
/**
|
||||
* Event structures.
|
||||
*/
|
||||
|
||||
struct CloseStartedEvent {
|
||||
// Error code provided when close() or closeNow() called.
|
||||
//
|
||||
// The presence of an error code does NOT indicate that a "problem" caused
|
||||
// the socket to close, since an error code can be an application timeout.
|
||||
folly::Optional<QuicError> maybeCloseReason;
|
||||
|
||||
// Default equality comparator available in C++20.
|
||||
//
|
||||
// mvfst currently supports C++17 onwards. However, we can enable this for
|
||||
// unit tests and other code that we expect to run in C++20.
|
||||
#if FOLLY_CPLUSPLUS >= 202002L
|
||||
friend auto operator<=>(
|
||||
const CloseStartedEvent&,
|
||||
const CloseStartedEvent&) = default;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ClosingEvent {
|
||||
// Default equality comparator available in C++20.
|
||||
//
|
||||
// mvfst currently supports C++17 onwards. However, we can enable this for
|
||||
// unit tests and other code that we expect to run in C++20.
|
||||
#if FOLLY_CPLUSPLUS >= 202002L
|
||||
friend auto operator<=>(const ClosingEvent&, const ClosingEvent&) = default;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct WriteEvent {
|
||||
[[nodiscard]] const std::deque<OutstandingPacket>& getOutstandingPackets()
|
||||
const {
|
||||
@@ -408,19 +440,38 @@ class SocketObserverInterface {
|
||||
using StreamCloseEvent = StreamEvent;
|
||||
|
||||
/**
|
||||
* close() will be invoked when the socket is being closed.
|
||||
*
|
||||
* If the callback handler does not unsubscribe itself upon being called,
|
||||
* then it may be called multiple times (e.g., by a call to close() by
|
||||
* the application, and then again when closeNow() is called on
|
||||
* destruction).
|
||||
*
|
||||
* @param socket Socket being closed.
|
||||
* @param errorOpt Error information, if connection closed due to error.
|
||||
* Events.
|
||||
*/
|
||||
virtual void close(
|
||||
|
||||
/**
|
||||
* closeStarted() is invoked when socket close begins.
|
||||
*
|
||||
* The socket may stay open for some time after this event to drain.
|
||||
*
|
||||
* @param socket Socket being closed.
|
||||
* @param event CloseStartedEvent with details.
|
||||
*/
|
||||
virtual void closeStarted(
|
||||
QuicSocket* /* socket */,
|
||||
const folly::Optional<QuicError>& /* errorOpt */) noexcept {}
|
||||
const CloseStartedEvent& /* event */) noexcept {}
|
||||
|
||||
/**
|
||||
* closing() is invoked right before the transport is unbound from UDP socket.
|
||||
*
|
||||
* closeStarted() should have been invoked prior to this event as this event
|
||||
* marks the completion of the socket being closed and the last opportunity
|
||||
* to capture state from the socket.
|
||||
*
|
||||
* Called immediately BEFORE the transport is unbound from the UDP socket to
|
||||
* be consistent with TCP sockets, for which the closing() event would mark
|
||||
* the last opportunity to get information (such as TCP_INFO) from the socket.
|
||||
*
|
||||
* @param socket Socket being closed.
|
||||
* @param event ClosingEvent with details.
|
||||
*/
|
||||
virtual void closing(
|
||||
QuicSocket* /* socket */,
|
||||
const ClosingEvent& /* event */) noexcept {}
|
||||
|
||||
/**
|
||||
* evbAttach() will be invoked when a new event base is attached to this
|
||||
|
@@ -79,7 +79,9 @@ QuicServerTransport::~QuicServerTransport() {
|
||||
QuicError(
|
||||
QuicErrorCode(LocalErrorCode::SHUTTING_DOWN),
|
||||
std::string("Closing from server destructor")),
|
||||
false);
|
||||
false /* drainConnection */);
|
||||
// closeImpl may have been called earlier with drain = true, so force close.
|
||||
closeUdpSocket();
|
||||
}
|
||||
|
||||
QuicServerTransport::Ptr QuicServerTransport::make(
|
||||
|
Reference in New Issue
Block a user