Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't automatically close exchanges if the underlying connection closes. #7114

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions src/messaging/ExchangeContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,27 @@ bool ExchangeContext::MatchExchange(SecureSessionHandle session, const PacketHea
&& (payloadHeader.IsInitiator() != IsInitiator());
}

void ExchangeContext::OnConnectionExpired()
{
// Reset our mSecureSession to a default-initialized (hence not matching any
// connection state) value, because it's still referencing the now-expired
// connection. This will mean that no more messages can be sent via this
// exchange, which seems fine given the semantics of connection expiration.
mSecureSession = SecureSessionHandle();
Copy link
Contributor

@yufengwangca yufengwangca May 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After we reset the SessionHandle, this exchange can not be used to send any message.

Let's go back the the original issue.

After we send AddRootCert command and receive the response to AddRootCert, the connection is closed.
Now we don't close the exchange, but reset the SessionHandle of the exchange.

On which exchange we send AddNetwork Command? My understand is we should continue to use the same exchange to send this command and close the exchange when we reach the end of conversation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On which exchange we send AddNetwork Command?

A different one. Each new Invoke is a new exchange in the spec.


if (!IsResponseExpected())
{
// Nothing to do in this case
return;
}

// If we're waiting on a response, we now know it's never going to show up
// and we should notify our delegate accordingly.
CancelResponseTimer();
SetResponseExpected(false);
NotifyResponseTimeout();
}

CHIP_ERROR ExchangeContext::StartResponseTimer()
{
System::Layer * lSystemLayer = mExchangeMgr->GetSessionMgr()->SystemLayer();
Expand Down Expand Up @@ -334,11 +355,18 @@ void ExchangeContext::HandleResponseTimeout(System::Layer * aSystemLayer, void *
// NOTE: we don't set mResponseExpected to false here because the response could still arrive. If the user
// wants to never receive the response, they must close the exchange context.

ExchangeDelegate * delegate = ec->GetDelegate();
ec->NotifyResponseTimeout();
}

void ExchangeContext::NotifyResponseTimeout()
{
ExchangeDelegate * delegate = GetDelegate();

// Call the user's timeout handler.
if (delegate != nullptr)
delegate->OnResponseTimeout(ec);
{
delegate->OnResponseTimeout(this);
}
}

CHIP_ERROR ExchangeContext::HandleMessage(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
Expand Down
11 changes: 11 additions & 0 deletions src/messaging/ExchangeContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,17 @@ class DLL_EXPORT ExchangeContext : public ReliableMessageContext, public Referen
*/
bool MatchExchange(SecureSessionHandle session, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader);

/**
* Notify the exchange that its connection has expired.
*/
void OnConnectionExpired();

/**
* Notify our delegate, if any, that we have timed out waiting for a
* response.
*/
void NotifyResponseTimeout();

CHIP_ERROR StartResponseTimer();

void CancelResponseTimer();
Expand Down
5 changes: 3 additions & 2 deletions src/messaging/ExchangeMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,9 @@ void ExchangeManager::OnConnectionExpired(SecureSessionHandle session, SecureSes
mContextPool.ForEachActiveObject([&](auto * ec) {
if (ec->mSecureSession == session)
{
ec->Close();
// Continue iterate because there can be multiple contexts associated with the connection.
ec->OnConnectionExpired();
// Continue to iterate because there can be multiple exchanges
// associated with the connection.
}
return true;
});
Expand Down
4 changes: 4 additions & 0 deletions src/messaging/ExchangeMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,13 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate, public Trans
SecureSessionMgr * msgLayer) override;

void OnNewConnection(SecureSessionHandle session, SecureSessionMgr * mgr) override;
#if CHIP_CONFIG_TEST
public: // Allow OnConnectionExpired to be called directly from tests.
#endif // CHIP_CONFIG_TEST
void OnConnectionExpired(SecureSessionHandle session, SecureSessionMgr * mgr) override;

// TransportMgrDelegate interface for rendezvous sessions
private:
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
void OnMessageReceived(const Transport::PeerAddress & source, System::PacketBufferHandle && msgBuf) override;
};

Expand Down
62 changes: 62 additions & 0 deletions src/messaging/tests/TestExchangeMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ class MockAppDelegate : public ExchangeDelegate
bool IsOnMessageReceivedCalled = false;
};

class WaitForTimeoutDelegate : public ExchangeDelegate
{
public:
void OnMessageReceived(ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
System::PacketBufferHandle && buffer) override
{}

void OnResponseTimeout(ExchangeContext * ec) override
{
IsOnResponseTimeoutCalled = true;
ec->Close();
}

bool IsOnResponseTimeoutCalled = false;
};

void CheckNewContextTest(nlTestSuite * inSuite, void * inContext)
{
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
Expand All @@ -114,6 +130,48 @@ void CheckNewContextTest(nlTestSuite * inSuite, void * inContext)
ec2->Close();
}

void CheckSessionExpirationBasics(nlTestSuite * inSuite, void * inContext)
{
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);

MockAppDelegate sendDelegate;
ExchangeContext * ec1 = ctx.NewExchangeToLocal(&sendDelegate);

// Expire the session this exchange is supposedly on.
ctx.GetExchangeManager().OnConnectionExpired(ec1->GetSecureSession(), &ctx.GetSecureSessionManager());

MockAppDelegate receiveDelegate;
CHIP_ERROR err =
ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1, &receiveDelegate);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

err = ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck));
NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, !receiveDelegate.IsOnMessageReceivedCalled);

ec1->Close();
err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
}

void CheckSessionExpirationTimeout(nlTestSuite * inSuite, void * inContext)
{
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);

WaitForTimeoutDelegate sendDelegate;
ExchangeContext * ec1 = ctx.NewExchangeToLocal(&sendDelegate);

ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(Messaging::SendMessageFlags::kExpectResponse).Set(Messaging::SendMessageFlags::kNoAutoRequestAck));
NL_TEST_ASSERT(inSuite, !sendDelegate.IsOnResponseTimeoutCalled);

// Expire the session this exchange is supposedly on. This should close the
// exchange.
ctx.GetExchangeManager().OnConnectionExpired(ec1->GetSecureSession(), &ctx.GetSecureSessionManager());
NL_TEST_ASSERT(inSuite, sendDelegate.IsOnResponseTimeoutCalled);
}

void CheckUmhRegistrationTest(nlTestSuite * inSuite, void * inContext)
{
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
Expand Down Expand Up @@ -167,6 +225,8 @@ void CheckExchangeMessages(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled);

ec1->Close();
err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
}

// Test Suite
Expand All @@ -180,6 +240,8 @@ const nlTest sTests[] =
NL_TEST_DEF("Test ExchangeMgr::NewContext", CheckNewContextTest),
NL_TEST_DEF("Test ExchangeMgr::CheckUmhRegistrationTest", CheckUmhRegistrationTest),
NL_TEST_DEF("Test ExchangeMgr::CheckExchangeMessages", CheckExchangeMessages),
NL_TEST_DEF("Test OnConnectionExpired basics", CheckSessionExpirationBasics),
NL_TEST_DEF("Test OnConnectionExpired timeout handling", CheckSessionExpirationTimeout),

NL_TEST_SENTINEL()
};
Expand Down