Matter SDK Coverage Report
Current view: top level - app - CommandResponseSender.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 50.9 % 116 59
Test Date: 2025-01-17 19:00:11 Functions: 76.9 % 13 10

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2024 Project CHIP Authors
       3              :  *    All rights reserved.
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #include "CommandResponseSender.h"
      19              : #include "InteractionModelEngine.h"
      20              : #include "messaging/ExchangeContext.h"
      21              : 
      22              : namespace chip {
      23              : namespace app {
      24              : using Status = Protocols::InteractionModel::Status;
      25              : 
      26            0 : CHIP_ERROR CommandResponseSender::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext,
      27              :                                                     const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload)
      28              : {
      29            0 :     CHIP_ERROR err = CHIP_NO_ERROR;
      30            0 :     Optional<Status> failureStatusToSend;
      31              : 
      32            0 :     if (mState == State::AwaitingStatusResponse &&
      33            0 :         aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
      34              :     {
      35            0 :         CHIP_ERROR statusError = CHIP_NO_ERROR;
      36            0 :         err                    = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError);
      37            0 :         VerifyOrExit(err == CHIP_NO_ERROR, failureStatusToSend.SetValue(Status::InvalidAction));
      38            0 :         err = statusError;
      39            0 :         VerifyOrExit(err == CHIP_NO_ERROR, failureStatusToSend.SetValue(Status::InvalidAction));
      40              : 
      41            0 :         err = SendCommandResponse();
      42              :         // If SendCommandResponse() fails, we must close the exchange. We signal the failure to the
      43              :         // requester with a StatusResponse ('Failure'). Since we're in the middle of processing an
      44              :         // incoming message, we close the exchange by indicating that we don't expect a further response.
      45            0 :         VerifyOrExit(err == CHIP_NO_ERROR, failureStatusToSend.SetValue(Status::Failure));
      46              : 
      47            0 :         bool moreToSend = !mChunks.IsNull();
      48            0 :         if (!moreToSend)
      49              :         {
      50              :             // We are sending the final message and do not anticipate any further responses. We are
      51              :             // calling ExitNow() to immediately execute Close() and subsequently return from this function.
      52            0 :             ExitNow();
      53              :         }
      54            0 :         return CHIP_NO_ERROR;
      55              :     }
      56              : 
      57            0 :     ChipLogDetail(DataManagement, "CommandResponseSender: Unexpected message type %d", aPayloadHeader.GetMessageType());
      58            0 :     err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
      59            0 :     if (mState != State::AllInvokeResponsesSent)
      60              :     {
      61            0 :         failureStatusToSend.SetValue(Status::Failure);
      62            0 :         ExitNow();
      63              :     }
      64            0 :     StatusResponse::Send(Status::InvalidAction, mExchangeCtx.Get(), false /*aExpectResponse*/);
      65            0 :     return err;
      66            0 : exit:
      67            0 :     if (failureStatusToSend.HasValue())
      68              :     {
      69            0 :         StatusResponse::Send(failureStatusToSend.Value(), mExchangeCtx.Get(), false /*aExpectResponse*/);
      70              :     }
      71            0 :     Close();
      72            0 :     return err;
      73            0 : }
      74              : 
      75            0 : void CommandResponseSender::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
      76              : {
      77            0 :     ChipLogDetail(DataManagement, "CommandResponseSender: Timed out waiting for response from requester mState=[%10.10s]",
      78              :                   GetStateStr());
      79            0 :     Close();
      80            0 : }
      81              : 
      82           30 : void CommandResponseSender::StartSendingCommandResponses()
      83              : {
      84           30 :     VerifyOrDie(mState == State::ReadyForInvokeResponses);
      85           30 :     CHIP_ERROR err = SendCommandResponse();
      86           30 :     if (err != CHIP_NO_ERROR)
      87              :     {
      88            2 :         ChipLogError(DataManagement, "Failed to send InvokeResponseMessage");
      89              :         // TODO(#30453): It should be our responsibility to send a Failure StatusResponse to the requestor
      90              :         // if there is a SessionHandle, but legacy unit tests explicitly check the behavior where
      91              :         // we do not send any message. Changing this behavior should be done in a standalone
      92              :         // PR where only that specific change is made. Here is a possible solution that could
      93              :         // be used that fulfills our responsibility to send a Failure StatusResponse. This causes unit
      94              :         // tests to start failing.
      95              :         //   ```
      96              :         //   if (mExchangeCtx && mExchangeCtx->HasSessionHandle())
      97              :         //   {
      98              :         //       SendStatusResponse(Status::Failure);
      99              :         //   }
     100              :         //   ```
     101            2 :         Close();
     102            2 :         return;
     103              :     }
     104              : 
     105           28 :     if (HasMoreToSend())
     106              :     {
     107            0 :         MoveToState(State::AwaitingStatusResponse);
     108            0 :         mExchangeCtx->SetDelegate(this);
     109              :     }
     110              :     else
     111              :     {
     112           28 :         Close();
     113              :     }
     114              : }
     115              : 
     116           33 : void CommandResponseSender::OnDone(CommandHandlerImpl & apCommandObj)
     117              : {
     118           33 :     if (mState == State::ErrorSentDelayCloseUntilOnDone)
     119              :     {
     120              :         // We have already sent a message to the client indicating that we are not expecting
     121              :         // a response.
     122            3 :         Close();
     123            3 :         return;
     124              :     }
     125           30 :     StartSendingCommandResponses();
     126              : }
     127              : 
     128           23 : void CommandResponseSender::DispatchCommand(CommandHandlerImpl & apCommandObj, const ConcreteCommandPath & aCommandPath,
     129              :                                             TLV::TLVReader & apPayload)
     130              : {
     131           23 :     VerifyOrReturn(mpCommandHandlerCallback);
     132           23 :     mpCommandHandlerCallback->DispatchCommand(apCommandObj, aCommandPath, apPayload);
     133              : }
     134              : 
     135           30 : Protocols::InteractionModel::Status CommandResponseSender::ValidateCommandCanBeDispatched(const DataModel::InvokeRequest & request)
     136              : {
     137           30 :     VerifyOrReturnValue(mpCommandHandlerCallback, Protocols::InteractionModel::Status::Failure);
     138           30 :     return mpCommandHandlerCallback->ValidateCommandCanBeDispatched(request);
     139              : }
     140              : 
     141           30 : CHIP_ERROR CommandResponseSender::SendCommandResponse()
     142              : {
     143           30 :     VerifyOrReturnError(HasMoreToSend(), CHIP_ERROR_INCORRECT_STATE);
     144           29 :     if (mChunks.IsNull())
     145              :     {
     146            0 :         VerifyOrReturnError(mReportResponseDropped, CHIP_ERROR_INCORRECT_STATE);
     147            0 :         SendStatusResponse(Status::ResourceExhausted);
     148            0 :         mReportResponseDropped = false;
     149            0 :         return CHIP_NO_ERROR;
     150              :     }
     151           29 :     System::PacketBufferHandle commandResponsePayload = mChunks.PopHead();
     152              : 
     153           29 :     Messaging::SendFlags sendFlag = Messaging::SendMessageFlags::kNone;
     154           29 :     if (HasMoreToSend())
     155              :     {
     156            0 :         sendFlag = Messaging::SendMessageFlags::kExpectResponse;
     157            0 :         mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
     158              :     }
     159              : 
     160           29 :     ReturnErrorOnFailure(mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::InvokeCommandResponse,
     161              :                                                    std::move(commandResponsePayload), sendFlag));
     162              : 
     163           28 :     return CHIP_NO_ERROR;
     164           29 : }
     165              : 
     166           36 : const char * CommandResponseSender::GetStateStr() const
     167              : {
     168              : #if CHIP_DETAIL_LOGGING
     169           36 :     switch (mState)
     170              :     {
     171            0 :     case State::ReadyForInvokeResponses:
     172            0 :         return "ReadyForInvokeResponses";
     173              : 
     174            0 :     case State::AwaitingStatusResponse:
     175            0 :         return "AwaitingStatusResponse";
     176              : 
     177           33 :     case State::AllInvokeResponsesSent:
     178           33 :         return "AllInvokeResponsesSent";
     179              : 
     180            3 :     case State::ErrorSentDelayCloseUntilOnDone:
     181            3 :         return "ErrorSentDelayCloseUntilOnDone";
     182              :     }
     183              : #endif // CHIP_DETAIL_LOGGING
     184            0 :     return "N/A";
     185              : }
     186              : 
     187           36 : void CommandResponseSender::MoveToState(const State aTargetState)
     188              : {
     189           36 :     if (mState == aTargetState)
     190              :     {
     191            0 :         return;
     192              :     }
     193           36 :     mState = aTargetState;
     194           36 :     ChipLogDetail(DataManagement, "Command response sender moving to [%10.10s]", GetStateStr());
     195              : }
     196              : 
     197           33 : void CommandResponseSender::Close()
     198              : {
     199           33 :     MoveToState(State::AllInvokeResponsesSent);
     200           33 :     mpCallback->OnDone(*this);
     201           33 : }
     202              : 
     203           33 : void CommandResponseSender::OnInvokeCommandRequest(Messaging::ExchangeContext * ec, System::PacketBufferHandle && payload,
     204              :                                                    bool isTimedInvoke)
     205              : {
     206           33 :     VerifyOrDieWithMsg(ec != nullptr, DataManagement, "Incoming exchange context should not be null");
     207           33 :     VerifyOrDieWithMsg(mState == State::ReadyForInvokeResponses, DataManagement, "state should be ReadyForInvokeResponses");
     208              : 
     209              :     // NOTE: we already know this is an InvokeRequestMessage because we explicitly registered with the
     210              :     // Exchange Manager for unsolicited InvokeRequestMessages.
     211           33 :     mExchangeCtx.Grab(ec);
     212           33 :     mExchangeCtx->WillSendMessage();
     213              : 
     214              :     // Grabbing Handle to prevent mCommandHandler from calling OnDone before OnInvokeCommandRequest returns.
     215              :     // This allows us to send a StatusResponse error instead of any potentially queued up InvokeResponseMessages.
     216           33 :     CommandHandler::Handle workHandle(&mCommandHandler);
     217           33 :     Status status = mCommandHandler.OnInvokeCommandRequest(*this, std::move(payload), isTimedInvoke);
     218           33 :     if (status != Status::Success)
     219              :     {
     220            3 :         VerifyOrDie(mState == State::ReadyForInvokeResponses);
     221            3 :         SendStatusResponse(status);
     222              :         // The API contract of OnInvokeCommandRequest requires the CommandResponder instance to outlive
     223              :         // the CommandHandler. Therefore, we cannot safely call Close() here, even though we have
     224              :         // finished sending data. Closing must be deferred until the CommandHandler::OnDone callback.
     225            3 :         MoveToState(State::ErrorSentDelayCloseUntilOnDone);
     226              :     }
     227           33 : }
     228              : 
     229           29 : size_t CommandResponseSender::GetCommandResponseMaxBufferSize()
     230              : {
     231           29 :     if (!mExchangeCtx || !mExchangeCtx->HasSessionHandle())
     232              :     {
     233            0 :         ChipLogError(DataManagement, "Session not available. Unable to infer session-specific buffer capacities.");
     234            0 :         return kMaxSecureSduLengthBytes;
     235              :     }
     236              : 
     237           29 :     if (mExchangeCtx->GetSessionHandle()->AllowsLargePayload())
     238              :     {
     239            0 :         return kMaxLargeSecureSduLengthBytes;
     240              :     }
     241              : 
     242           29 :     return kMaxSecureSduLengthBytes;
     243              : }
     244              : 
     245              : #if CHIP_WITH_NLFAULTINJECTION
     246              : 
     247            0 : void CommandResponseSender::TestOnlyInvokeCommandRequestWithFaultsInjected(Messaging::ExchangeContext * ec,
     248              :                                                                            System::PacketBufferHandle && payload,
     249              :                                                                            bool isTimedInvoke,
     250              :                                                                            CommandHandlerImpl::NlFaultInjectionType faultType)
     251              : {
     252            0 :     VerifyOrDieWithMsg(ec != nullptr, DataManagement, "TH Failure: Incoming exchange context should not be null");
     253            0 :     VerifyOrDieWithMsg(mState == State::ReadyForInvokeResponses, DataManagement,
     254              :                        "TH Failure: state should be ReadyForInvokeResponses, issue with TH");
     255              : 
     256            0 :     mExchangeCtx.Grab(ec);
     257            0 :     mExchangeCtx->WillSendMessage();
     258              : 
     259            0 :     mCommandHandler.TestOnlyInvokeCommandRequestWithFaultsInjected(*this, std::move(payload), isTimedInvoke, faultType);
     260            0 : }
     261              : #endif // CHIP_WITH_NLFAULTINJECTION
     262              : 
     263              : } // namespace app
     264              : } // namespace chip
        

Generated by: LCOV version 2.0-1