From 631efe9ac4c57eb0f83d7a26a189ef47e77cf045 Mon Sep 17 00:00:00 2001 From: Yunhan Wang Date: Wed, 10 Nov 2021 20:32:19 -0800 Subject: [PATCH] Send status report in IM server side when server encounter the error When server don't have readhandler/subscribe handler, server would send status report with RESOURCE_EXHAUSTED. When server don't have writehandler/commandhandler, server would send status report with BUSY. For other error geneared when recieving incoming request in server, server would send status report with FAILURE --- src/app/BUILD.gn | 2 + src/app/CommandSender.cpp | 30 +++++-- src/app/InteractionModelEngine.cpp | 118 ++++++++++++-------------- src/app/InteractionModelEngine.h | 15 ++-- src/app/ReadClient.cpp | 74 +++++++--------- src/app/ReadClient.h | 1 - src/app/ReadHandler.cpp | 23 +---- src/app/StatusResponse.cpp | 63 ++++++++++++++ src/app/StatusResponse.h | 38 +++++++++ src/app/WriteClient.cpp | 33 +++++-- src/app/WriteHandler.cpp | 2 +- src/app/tests/TestReadInteraction.cpp | 72 +++++++++++++++- src/app/util/basic-types.h | 1 - 13 files changed, 319 insertions(+), 153 deletions(-) create mode 100644 src/app/StatusResponse.cpp create mode 100644 src/app/StatusResponse.h diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 05c460035c7125..fbf2732f7bd897 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -112,6 +112,8 @@ static_library("app") { "OperationalDeviceProxy.h", "ReadClient.cpp", "ReadHandler.cpp", + "StatusResponse.cpp", + "StatusResponse.h", "WriteClient.cpp", "WriteHandler.cpp", "decoder.cpp", diff --git a/src/app/CommandSender.cpp b/src/app/CommandSender.cpp index b99ce2e7ae01bd..fd22c96462e8e0 100644 --- a/src/app/CommandSender.cpp +++ b/src/app/CommandSender.cpp @@ -26,6 +26,7 @@ #include "Command.h" #include "CommandHandler.h" #include "InteractionModelEngine.h" +#include "StatusResponse.h" #include #include @@ -91,12 +92,31 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha System::PacketBufferHandle && aPayload) { CHIP_ERROR err = CHIP_NO_ERROR; - VerifyOrExit(apExchangeContext == mpExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrExit(aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::InvokeCommandResponse), - err = CHIP_ERROR_INVALID_MESSAGE_TYPE); - - err = ProcessInvokeResponse(std::move(aPayload)); + if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::InvokeCommandResponse)) + { + err = ProcessInvokeResponse(std::move(aPayload)); + SuccessOrExit(err); + } + else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) + { + Protocols::InteractionModel::Status status; + err = StatusResponse::ProcessStatusResponse(apExchangeContext, std::move(aPayload), status); + mpExchangeCtx = nullptr; + SuccessOrExit(err); + if (status == Protocols::InteractionModel::Status::ResourceExhausted) + { + err = CHIP_ERROR_NO_MEMORY; + } + else + { + err = CHIP_ERROR_INCORRECT_STATE; + } + } + else + { + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; + } exit: if (mpCallback != nullptr) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index b48978ee13d0e2..71f0f5e8cbe62d 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -155,7 +155,6 @@ CHIP_ERROR InteractionModelEngine::NewReadClient(ReadClient ** const apReadClien return err; } } - return CHIP_ERROR_NO_MEMORY; } @@ -262,7 +261,6 @@ CHIP_ERROR InteractionModelEngine::NewWriteClient(WriteClientHandle & apWriteCli { continue; } - ReturnLogErrorOnFailure(writeClient.Init(mpExchangeMgr, apCallback)); apWriteClient.SetWriteClient(&writeClient); return CHIP_NO_ERROR; @@ -271,27 +269,6 @@ CHIP_ERROR InteractionModelEngine::NewWriteClient(WriteClientHandle & apWriteCli return CHIP_ERROR_NO_MEMORY; } -CHIP_ERROR InteractionModelEngine::OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, - const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - ChipLogDetail(InteractionModel, "Msg type %d not supported", aPayloadHeader.GetMessageType()); - - // Todo: Add status report - // err = SendStatusReport(ec, kChipProfile_Common, kStatus_UnsupportedMessage); - // SuccessOrExit(err); - - apExchangeContext = nullptr; - - // Todo: Fix the below check after the above status report is implemented. - if (nullptr != apExchangeContext) - { - apExchangeContext->Abort(); - } - return err; -} - void InteractionModelEngine::OnDone(CommandHandler & apCommandObj) { mCommandHandlerObjs.ReleaseObject(&apCommandObj); @@ -299,23 +276,27 @@ void InteractionModelEngine::OnDone(CommandHandler & apCommandObj) CHIP_ERROR InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload) + System::PacketBufferHandle && aPayload, + Protocols::InteractionModel::Status & aStatus) { CommandHandler * commandHandler = mCommandHandlerObjs.CreateObject(this); if (commandHandler == nullptr) { + ChipLogProgress(InteractionModel, "no resource for Invoke interaction"); + aStatus = Protocols::InteractionModel::Status::Busy; return CHIP_ERROR_NO_MEMORY; } - return commandHandler->OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload)); + ReturnErrorOnFailure(commandHandler->OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload))); + aStatus = Protocols::InteractionModel::Status::Success; + return CHIP_NO_ERROR; } CHIP_ERROR InteractionModelEngine::OnReadInitialRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload, - ReadHandler::InteractionType aInteractionType) + ReadHandler::InteractionType aInteractionType, + Protocols::InteractionModel::Status & aStatus) { - CHIP_ERROR err = CHIP_NO_ERROR; - ChipLogDetail(InteractionModel, "Received %s request", aInteractionType == ReadHandler::InteractionType::Subscribe ? "Subscribe" : "Read"); @@ -328,10 +309,10 @@ CHIP_ERROR InteractionModelEngine::OnReadInitialRequest(Messaging::ExchangeConte bool keepSubscriptions = true; System::PacketBufferTLVReader reader; reader.Init(aPayload.Retain()); - SuccessOrExit(err = reader.Next()); + ReturnErrorOnFailure(reader.Next()); SubscribeRequestMessage::Parser subscribeRequestParser; - SuccessOrExit(err = subscribeRequestParser.Init(reader)); - err = subscribeRequestParser.GetKeepSubscriptions(&keepSubscriptions); + ReturnErrorOnFailure(subscribeRequestParser.Init(reader)); + CHIP_ERROR err = subscribeRequestParser.GetKeepSubscriptions(&keepSubscriptions); if (err == CHIP_NO_ERROR && !keepSubscriptions) { readHandler.Shutdown(ReadHandler::ShutdownOptions::AbortCurrentExchange); @@ -343,54 +324,45 @@ CHIP_ERROR InteractionModelEngine::OnReadInitialRequest(Messaging::ExchangeConte { if (readHandler.IsFree()) { - err = readHandler.Init(mpExchangeMgr, mpDelegate, apExchangeContext, aInteractionType); - SuccessOrExit(err); - err = readHandler.OnReadInitialRequest(std::move(aPayload)); - apExchangeContext = nullptr; - break; + ReturnErrorOnFailure(readHandler.Init(mpExchangeMgr, mpDelegate, apExchangeContext, aInteractionType)); + ReturnErrorOnFailure(readHandler.OnReadInitialRequest(std::move(aPayload))); + aStatus = Protocols::InteractionModel::Status::Success; + return CHIP_NO_ERROR; } } -exit: + ChipLogProgress(InteractionModel, "no resource for %s interaction", + aInteractionType == ReadHandler::InteractionType::Subscribe ? "Subscribe" : "Read"); + aStatus = Protocols::InteractionModel::Status::ResourceExhausted; - if (nullptr != apExchangeContext) - { - apExchangeContext->Abort(); - } - return err; + return CHIP_NO_ERROR; } CHIP_ERROR InteractionModelEngine::OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, - const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload, + Protocols::InteractionModel::Status & aStatus) { - CHIP_ERROR err = CHIP_NO_ERROR; - ChipLogDetail(InteractionModel, "Received Write request"); for (auto & writeHandler : mWriteHandlers) { if (writeHandler.IsFree()) { - err = writeHandler.Init(mpDelegate); - SuccessOrExit(err); - err = writeHandler.OnWriteRequest(apExchangeContext, std::move(aPayload)); - apExchangeContext = nullptr; - break; + ReturnErrorOnFailure(writeHandler.Init(mpDelegate)); + ReturnErrorOnFailure(writeHandler.OnWriteRequest(apExchangeContext, std::move(aPayload))); + aStatus = Protocols::InteractionModel::Status::Success; + return CHIP_NO_ERROR; } } - -exit: - - if (nullptr != apExchangeContext) - { - apExchangeContext->Abort(); - } - return err; + ChipLogProgress(InteractionModel, "no resource for write interaction"); + aStatus = Protocols::InteractionModel::Status::Busy; + return CHIP_NO_ERROR; } CHIP_ERROR InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload) + System::PacketBufferHandle && aPayload, + Protocols::InteractionModel::Status & aStatus) { System::PacketBufferTLVReader reader; reader.Init(aPayload.Retain()); @@ -421,31 +393,45 @@ CHIP_ERROR InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeCo CHIP_ERROR InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) { + CHIP_ERROR err = CHIP_NO_ERROR; + Protocols::InteractionModel::Status status = Protocols::InteractionModel::Status::Failure; + if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::InvokeCommandRequest)) { - return OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload)); + ReturnErrorOnFailure(OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), status)); } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReadRequest)) { - return OnReadInitialRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), ReadHandler::InteractionType::Read); + ReturnErrorOnFailure(OnReadInitialRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), + ReadHandler::InteractionType::Read, status)); } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::WriteRequest)) { - return OnWriteRequest(apExchangeContext, aPayloadHeader, std::move(aPayload)); + ReturnErrorOnFailure(OnWriteRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), status)); } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::SubscribeRequest)) { - return OnReadInitialRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), - ReadHandler::InteractionType::Subscribe); + ReturnErrorOnFailure(OnReadInitialRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), + ReadHandler::InteractionType::Subscribe, status)); } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReportData)) { - return OnUnsolicitedReportData(apExchangeContext, aPayloadHeader, std::move(aPayload)); + ReturnErrorOnFailure(OnUnsolicitedReportData(apExchangeContext, aPayloadHeader, std::move(aPayload), status)); } else { - return OnUnknownMsgType(apExchangeContext, aPayloadHeader, std::move(aPayload)); + ChipLogProgress(InteractionModel, "Msg type %d not supported", aPayloadHeader.GetMessageType()); + } + + if (status != Protocols::InteractionModel::Status::Success) + { + err = StatusResponse::SendStatusResponse(status, apExchangeContext, false /*aExpectResponse*/); + if (err != CHIP_NO_ERROR) + { + apExchangeContext->Abort(); + } } + return err; } void InteractionModelEngine::OnResponseTimeout(Messaging::ExchangeContext * ec) diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index d5fce192f6e56a..176b6b9cb98fa5 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -54,9 +55,6 @@ namespace chip { namespace app { - -static constexpr size_t kMaxSecureSduLengthBytes = 1024; - /** * @class InteractionModelEngine * @@ -199,10 +197,8 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate, public Comman void OnDone(CommandHandler & apCommandObj) override; - CHIP_ERROR OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload); CHIP_ERROR OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload); + System::PacketBufferHandle && aPayload, Protocols::InteractionModel::Status & aStatus); CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) override; void OnResponseTimeout(Messaging::ExchangeContext * ec) override; @@ -213,20 +209,21 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate, public Comman */ CHIP_ERROR OnReadInitialRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload, ReadHandler::InteractionType aInteractionType); + System::PacketBufferHandle && aPayload, ReadHandler::InteractionType aInteractionType, + Protocols::InteractionModel::Status & aStatus); /** * Called when Interaction Model receives a Write Request message. Errors processing * the Write Request are handled entirely within this function. */ CHIP_ERROR OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload); + System::PacketBufferHandle && aPayload, Protocols::InteractionModel::Status & aStatus); /**This function handles processing of un-solicited ReportData messages on the client, which can * only occur post subscription establishment */ CHIP_ERROR OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload); + System::PacketBufferHandle && aPayload, Protocols::InteractionModel::Status & aStatus); void DispatchCommand(CommandHandler & apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & apPayload) override; diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index c57c3417331ec4..33ceea9995c6a4 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace chip { namespace app { @@ -189,46 +190,6 @@ CHIP_ERROR ReadClient::SendReadRequest(ReadPrepareParams & aReadPrepareParams) return err; } -CHIP_ERROR ReadClient::SendStatusResponse(CHIP_ERROR aError) -{ - using Protocols::InteractionModel::Status; - - System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes); - VerifyOrReturnLogError(!msgBuf.IsNull(), CHIP_ERROR_NO_MEMORY); - - System::PacketBufferTLVWriter writer; - writer.Init(std::move(msgBuf)); - - StatusResponseMessage::Builder response; - ReturnLogErrorOnFailure(response.Init(&writer)); - Status statusCode = Status::Success; - if (aError != CHIP_NO_ERROR) - { - statusCode = Status::InvalidSubscription; - } - response.Status(statusCode); - ReturnLogErrorOnFailure(response.GetError()); - ReturnLogErrorOnFailure(writer.Finalize(&msgBuf)); - VerifyOrReturnLogError(mpExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE); - - if (IsSubscriptionType()) - { - if (IsAwaitingInitialReport()) - { - MoveToState(ClientState::AwaitingSubscribeResponse); - } - else - { - RefreshLivenessCheckTimer(); - } - } - ReturnLogErrorOnFailure( - mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::StatusResponse, std::move(msgBuf), - Messaging::SendFlags(IsAwaitingSubscribeResponse() ? Messaging::SendMessageFlags::kExpectResponse - : Messaging::SendMessageFlags::kNone))); - return CHIP_NO_ERROR; -} - CHIP_ERROR ReadClient::GenerateEventPaths(EventPaths::Builder & aEventPathsBuilder, EventPathParams * apEventPathParamsList, size_t aEventPathParamsListSize) { @@ -287,6 +248,22 @@ CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchange mpExchangeCtx = nullptr; SuccessOrExit(err); } + else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) + { + Protocols::InteractionModel::Status status; + VerifyOrExit(apExchangeContext == mpExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE); + err = StatusResponse::ProcessStatusResponse(apExchangeContext, std::move(aPayload), status); + mpExchangeCtx = nullptr; + SuccessOrExit(err); + if (status == Protocols::InteractionModel::Status::ResourceExhausted) + { + err = CHIP_ERROR_NO_MEMORY; + } + else + { + err = CHIP_ERROR_INCORRECT_STATE; + } + } else { err = CHIP_ERROR_INVALID_MESSAGE_TYPE; @@ -425,7 +402,22 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle && aPayload) } exit: - SendStatusResponse(err); + if (IsSubscriptionType()) + { + if (IsAwaitingInitialReport()) + { + MoveToState(ClientState::AwaitingSubscribeResponse); + } + else + { + RefreshLivenessCheckTimer(); + } + } + + StatusResponse::SendStatusResponse(err == CHIP_NO_ERROR ? Protocols::InteractionModel::Status::Success + : Protocols::InteractionModel::Status::InvalidSubscription, + mpExchangeCtx, IsAwaitingSubscribeResponse()); + if (!mInitialReport) { mpExchangeCtx = nullptr; diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h index 3e678746b0df30..e024c8be69e402 100644 --- a/src/app/ReadClient.h +++ b/src/app/ReadClient.h @@ -179,7 +179,6 @@ class ReadClient : public Messaging::ExchangeDelegate NodeId GetPeerNodeId() const { return mPeerNodeId; } bool IsReadType() { return mInteractionType == InteractionType::Read; } bool IsSubscriptionType() const { return mInteractionType == InteractionType::Subscribe; }; - CHIP_ERROR SendStatusResponse(CHIP_ERROR aError); private: friend class TestReadInteraction; diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 506f6a8e28eeb1..e7814b0faf9283 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -134,25 +134,10 @@ CHIP_ERROR ReadHandler::OnReadInitialRequest(System::PacketBufferHandle && aPayl CHIP_ERROR ReadHandler::OnStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload) { CHIP_ERROR err = CHIP_NO_ERROR; - Protocols::InteractionModel::Status statusCode; - StatusResponseMessage::Parser response; - System::PacketBufferTLVReader reader; - reader.Init(std::move(aPayload)); - reader.Next(); - err = response.Init(reader); - SuccessOrExit(err); - -#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK - err = response.CheckSchemaValidity(); - SuccessOrExit(err); -#endif - - err = response.GetStatus(statusCode); - SuccessOrExit(err); - - ChipLogProgress(DataManagement, "In state %s, receive status response, status code is %" PRIu16, GetStateStr(), - to_underlying(statusCode)); - VerifyOrExit((statusCode == Protocols::InteractionModel::Status::Success), err = CHIP_ERROR_INVALID_ARGUMENT); + Protocols::InteractionModel::Status status; + err = StatusResponse::ProcessStatusResponse(apExchangeContext, std::move(aPayload), status); + VerifyOrExit((err == CHIP_NO_ERROR) && (status == Protocols::InteractionModel::Status::Success), + err = CHIP_ERROR_INVALID_ARGUMENT); switch (mState) { case HandlerState::AwaitingReportResponse: diff --git a/src/app/StatusResponse.cpp b/src/app/StatusResponse.cpp new file mode 100644 index 00000000000000..f35bc20292a08c --- /dev/null +++ b/src/app/StatusResponse.cpp @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace chip { +namespace app { +CHIP_ERROR StatusResponse::SendStatusResponse(Protocols::InteractionModel::Status aStatus, + Messaging::ExchangeContext * apExchangeContext, bool aExpectResponse) +{ + using Protocols::InteractionModel::Status; + VerifyOrReturnError(apExchangeContext != nullptr, CHIP_ERROR_INCORRECT_STATE); + System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes); + VerifyOrReturnError(!msgBuf.IsNull(), CHIP_ERROR_NO_MEMORY); + + System::PacketBufferTLVWriter writer; + writer.Init(std::move(msgBuf)); + + StatusResponseMessage::Builder response; + ReturnErrorOnFailure(response.Init(&writer)); + response.Status(aStatus); + ReturnErrorOnFailure(response.GetError()); + ReturnErrorOnFailure(writer.Finalize(&msgBuf)); + ReturnErrorOnFailure(apExchangeContext->SendMessage(Protocols::InteractionModel::MsgType::StatusResponse, std::move(msgBuf), + aExpectResponse ? Messaging::SendMessageFlags::kExpectResponse + : Messaging::SendMessageFlags::kNone)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR StatusResponse::ProcessStatusResponse(Messaging::ExchangeContext * apExchangeContext, + System::PacketBufferHandle && aPayload, + Protocols::InteractionModel::Status & aStatus) +{ + StatusResponseMessage::Parser response; + System::PacketBufferTLVReader reader; + reader.Init(std::move(aPayload)); + ReturnErrorOnFailure(reader.Next()); + ReturnErrorOnFailure(response.Init(reader)); +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + ReturnErrorOnFailure(response.CheckSchemaValidity()); +#endif + ReturnErrorOnFailure(response.GetStatus(aStatus)); + ChipLogProgress(InteractionModel, "Receive status response, status is %" PRIu16, to_underlying(aStatus)); + return CHIP_NO_ERROR; +} +} // namespace app +} // namespace chip diff --git a/src/app/StatusResponse.h b/src/app/StatusResponse.h new file mode 100644 index 00000000000000..dde4270bcc0f62 --- /dev/null +++ b/src/app/StatusResponse.h @@ -0,0 +1,38 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace app { +static constexpr size_t kMaxSecureSduLengthBytes = 1024; + +class StatusResponse +{ +public: + static CHIP_ERROR SendStatusResponse(Protocols::InteractionModel::Status aStatus, + Messaging::ExchangeContext * apExchangeContext, bool aExpectResponse); + static CHIP_ERROR ProcessStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload, + Protocols::InteractionModel::Status & aStatus); +}; +} // namespace app +} // namespace chip diff --git a/src/app/WriteClient.cpp b/src/app/WriteClient.cpp index 26699e402b76a3..f6241bde189121 100644 --- a/src/app/WriteClient.cpp +++ b/src/app/WriteClient.cpp @@ -277,15 +277,32 @@ CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchang // This should never fail because even if SendWriteRequest is called // back-to-back, the second call will call Close() on the first exchange, // which clears the OnMessageReceived callback. + VerifyOrExit(apExchangeContext == mpExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrDie(apExchangeContext == mpExchangeCtx); - - // Verify that the message is an Write Response. If not, this is an unexpected message. - // Signal the error through the error callback and shutdown the client. - VerifyOrExit(aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::WriteResponse), - err = CHIP_ERROR_INVALID_MESSAGE_TYPE); - - err = ProcessWriteResponseMessage(std::move(aPayload)); + if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::WriteResponse)) + { + err = ProcessWriteResponseMessage(std::move(aPayload)); + SuccessOrExit(err); + } + else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) + { + Protocols::InteractionModel::Status status; + err = StatusResponse::ProcessStatusResponse(apExchangeContext, std::move(aPayload), status); + mpExchangeCtx = nullptr; + SuccessOrExit(err); + if (status == Protocols::InteractionModel::Status::ResourceExhausted) + { + err = CHIP_ERROR_NO_MEMORY; + } + else + { + err = CHIP_ERROR_INCORRECT_STATE; + } + } + else + { + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; + } exit: if (mpCallback != nullptr) diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp index 200946f9a18d11..c6533f28844969 100644 --- a/src/app/WriteHandler.cpp +++ b/src/app/WriteHandler.cpp @@ -269,7 +269,7 @@ const char * WriteHandler::GetStateStr() const void WriteHandler::MoveToState(const State aTargetState) { mState = aTargetState; - ChipLogDetail(DataManagement, "IM RH moving to [%s]", GetStateStr()); + ChipLogDetail(DataManagement, "IM WH moving to [%s]", GetStateStr()); } void WriteHandler::ClearState() diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index b5079798c3d6c6..7486b7680f6b7d 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -878,7 +878,7 @@ void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * a readPrepareParams.mMinIntervalFloorSeconds = 2; readPrepareParams.mMaxIntervalCeilingSeconds = 5; - printf("\nSend subscribe request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); + printf("\nSend first subscribe request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); err = engine->SendSubscribeRequest(readPrepareParams, &delegate); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); @@ -993,7 +993,7 @@ void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * a readPrepareParams1.mMinIntervalFloorSeconds = 2; readPrepareParams1.mMaxIntervalCeilingSeconds = 5; - printf("\nSend another subscribe request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); + printf("\nSend 2nd subscribe request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); err = engine->SendSubscribeRequest(readPrepareParams1, &delegate); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); @@ -1015,6 +1015,74 @@ void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * a engine->GetReportingEngine().Run(); NL_TEST_ASSERT(apSuite, delegate.mGotReport); NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 1); + + delegate.mNumAttributeResponse = 0; + delegate.mGotReport = false; + ReadPrepareParams readPrepareParams2(ctx.GetSessionBobToAlice()); + chip::app::AttributePathParams attributePathParams2[1]; + readPrepareParams2.mpAttributePathParamsList = attributePathParams2; + readPrepareParams2.mpAttributePathParamsList[0].mEndpointId = kTestEndpointId; + readPrepareParams2.mpAttributePathParamsList[0].mClusterId = kTestClusterId; + readPrepareParams2.mpAttributePathParamsList[0].mAttributeId = 1; + readPrepareParams2.mAttributePathParamsListSize = 1; + readPrepareParams2.mMinIntervalFloorSeconds = 2; + readPrepareParams2.mMaxIntervalCeilingSeconds = 5; + + printf("\nSend 3rd subscribe request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); + + err = engine->SendSubscribeRequest(readPrepareParams2, &delegate); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + engine->GetReportingEngine().Run(); + NL_TEST_ASSERT(apSuite, delegate.mGotReport); + NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 1); + NL_TEST_ASSERT(apSuite, !delegate.mReadError); + + delegate.mNumAttributeResponse = 0; + delegate.mGotReport = false; + ReadPrepareParams readPrepareParams3(ctx.GetSessionBobToAlice()); + chip::app::AttributePathParams attributePathParams3[1]; + readPrepareParams3.mpAttributePathParamsList = attributePathParams3; + readPrepareParams3.mpAttributePathParamsList[0].mEndpointId = kTestEndpointId; + readPrepareParams3.mpAttributePathParamsList[0].mClusterId = kTestClusterId; + readPrepareParams3.mpAttributePathParamsList[0].mAttributeId = 1; + readPrepareParams3.mAttributePathParamsListSize = 1; + readPrepareParams3.mMinIntervalFloorSeconds = 2; + readPrepareParams3.mMaxIntervalCeilingSeconds = 5; + + printf("\nSend 4th subscribe request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); + + ReadClient readClient3; + readClient3.Init(&ctx.GetExchangeManager(), &delegate, ReadClient::InteractionType::Subscribe); + err = readClient3.SendSubscribeRequest(readPrepareParams3); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + engine->GetReportingEngine().Run(); + NL_TEST_ASSERT(apSuite, delegate.mGotReport); + NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 1); + NL_TEST_ASSERT(apSuite, !delegate.mReadError); + + delegate.mNumAttributeResponse = 0; + delegate.mGotReport = false; + ReadPrepareParams readPrepareParams4(ctx.GetSessionBobToAlice()); + chip::app::AttributePathParams attributePathParams4[1]; + readPrepareParams4.mpAttributePathParamsList = attributePathParams4; + readPrepareParams4.mpAttributePathParamsList[0].mEndpointId = kTestEndpointId; + readPrepareParams4.mpAttributePathParamsList[0].mClusterId = kTestClusterId; + readPrepareParams4.mpAttributePathParamsList[0].mAttributeId = 1; + readPrepareParams4.mAttributePathParamsListSize = 1; + readPrepareParams4.mMinIntervalFloorSeconds = 2; + readPrepareParams4.mMaxIntervalCeilingSeconds = 5; + + printf("\nSend 5th subscribe request message to Node: %" PRIu64 ", resource exhausted\n", chip::kTestDeviceNodeId); + + ReadClient readClient4; + readClient4.Init(&ctx.GetExchangeManager(), &delegate, ReadClient::InteractionType::Subscribe); + err = readClient4.SendSubscribeRequest(readPrepareParams4); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + engine->GetReportingEngine().Run(); + NL_TEST_ASSERT(apSuite, !delegate.mGotReport); + NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 0); + NL_TEST_ASSERT(apSuite, delegate.mReadError); + // By now we should have closed all exchanges and sent all pending acks, so // there should be no queued-up things in the retransmit table. NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); diff --git a/src/app/util/basic-types.h b/src/app/util/basic-types.h index 97aebe690423c0..53256533ef0a3d 100644 --- a/src/app/util/basic-types.h +++ b/src/app/util/basic-types.h @@ -29,7 +29,6 @@ #include namespace chip { - typedef uint8_t Percent; typedef uint16_t Percent100ths;