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 : #pragma once
19 :
20 : #include <app/CommandHandlerExchangeInterface.h>
21 : #include <app/CommandHandlerImpl.h>
22 : #include <app/StatusResponse.h>
23 : #include <messaging/ExchangeHolder.h>
24 : #include <system/SystemPacketBuffer.h>
25 :
26 : namespace chip {
27 : namespace app {
28 :
29 : // TODO(#30453): Rename CommandResponseSender to CommandResponder in follow up PR
30 : /**
31 : * Manages the process of sending InvokeResponseMessage(s) to the requester.
32 : *
33 : * Implements the CommandHandlerExchangeInterface. Uses a CommandHandler member to process
34 : * InvokeCommandRequest. The CommandHandler is provided a reference to this
35 : * CommandHandlerExchangeInterface implementation to enable sending InvokeResponseMessage(s).
36 : */
37 : class CommandResponseSender : public Messaging::ExchangeDelegate,
38 : public CommandHandlerImpl::Callback,
39 : public CommandHandlerExchangeInterface
40 : {
41 : public:
42 : class Callback
43 : {
44 : public:
45 60 : virtual ~Callback() = default;
46 : /*
47 : * Signals registered callback that this object has finished its work and can now be
48 : * safely destroyed/released.
49 : */
50 : virtual void OnDone(CommandResponseSender & apResponderObj) = 0;
51 : };
52 :
53 33 : CommandResponseSender(Callback * apCallback, CommandHandlerImpl::Callback * apDispatchCallback) :
54 33 : mpCallback(apCallback), mpCommandHandlerCallback(apDispatchCallback), mCommandHandler(this), mExchangeCtx(*this)
55 33 : {}
56 :
57 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
58 : System::PacketBufferHandle && payload) override;
59 :
60 : void OnResponseTimeout(Messaging::ExchangeContext * ec) override;
61 :
62 : void OnDone(CommandHandlerImpl & apCommandObj) override;
63 :
64 : void DispatchCommand(CommandHandlerImpl & apCommandObj, const ConcreteCommandPath & aCommandPath,
65 : TLV::TLVReader & apPayload) override;
66 :
67 : Protocols::InteractionModel::Status ValidateCommandCanBeDispatched(const DataModel::InvokeRequest & request) override;
68 :
69 : /**
70 : * Gets the inner exchange context object, without ownership.
71 : *
72 : * WARNING: This is dangerous, since it is directly interacting with the
73 : * exchange being managed automatically by mExchangeCtx and
74 : * if not done carefully, may end up with use-after-free errors.
75 : *
76 : * @return The inner exchange context, might be nullptr if no
77 : * exchange context has been assigned or the context
78 : * has been released.
79 : */
80 2 : Messaging::ExchangeContext * GetExchangeContext() const override { return mExchangeCtx.Get(); }
81 :
82 : /**
83 : * Gets subject descriptor of the exchange.
84 : *
85 : * WARNING: This method should only be called when the caller is certain the
86 : * session has not been evicted.
87 : */
88 99 : Access::SubjectDescriptor GetSubjectDescriptor() const override
89 : {
90 99 : VerifyOrDie(mExchangeCtx);
91 99 : return mExchangeCtx->GetSessionHandle()->GetSubjectDescriptor();
92 : }
93 :
94 0 : FabricIndex GetAccessingFabricIndex() const override
95 : {
96 0 : VerifyOrDie(mExchangeCtx);
97 0 : return mExchangeCtx->GetSessionHandle()->GetFabricIndex();
98 : }
99 :
100 32 : Optional<GroupId> GetGroupId() const override
101 : {
102 32 : VerifyOrDie(mExchangeCtx);
103 32 : auto sessionHandle = mExchangeCtx->GetSessionHandle();
104 32 : if (sessionHandle->GetSessionType() != Transport::Session::SessionType::kGroupIncoming)
105 : {
106 32 : return NullOptional;
107 : }
108 0 : return MakeOptional(sessionHandle->AsIncomingGroupSession()->GetGroupId());
109 32 : }
110 :
111 0 : void HandlingSlowCommand() override
112 : {
113 0 : VerifyOrReturn(mExchangeCtx);
114 0 : auto * msgContext = mExchangeCtx->GetReliableMessageContext();
115 0 : VerifyOrReturn(msgContext != nullptr);
116 0 : msgContext->FlushAcks();
117 : }
118 :
119 29 : void AddInvokeResponseToSend(System::PacketBufferHandle && aPacket) override
120 : {
121 29 : VerifyOrDie(mState == State::ReadyForInvokeResponses);
122 29 : mChunks.AddToEnd(std::move(aPacket));
123 29 : }
124 :
125 0 : void ResponseDropped() override { mReportResponseDropped = true; }
126 :
127 : size_t GetCommandResponseMaxBufferSize() override;
128 :
129 : /*
130 : * Main entrypoint for this class to handle an invoke request.
131 : *
132 : * isTimedInvoke is true if and only if this is part of a Timed Invoke
133 : * transaction (i.e. was preceded by a Timed Request). If we reach here,
134 : * the timer verification has already been done.
135 : */
136 : void OnInvokeCommandRequest(Messaging::ExchangeContext * ec, System::PacketBufferHandle && payload, bool isTimedInvoke);
137 :
138 : #if CHIP_WITH_NLFAULTINJECTION
139 : /**
140 : * @brief Sends InvokeResponseMessages with injected faults for certification testing.
141 : *
142 : * The Test Harness (TH) uses this to simulate various server response behaviors,
143 : * ensuring the Device Under Test (DUT) handles responses per specification.
144 : *
145 : * This function strictly validates the DUT's InvokeRequestMessage against the test plan.
146 : * If deviations occur, the TH terminates with a detailed error message.
147 : *
148 : * @param ec Exchange context for sending InvokeResponseMessages to the client.
149 : * @param payload Payload of the incoming InvokeRequestMessage from the client.
150 : * @param isTimedInvoke Indicates whether the interaction is timed.
151 : * @param faultType The specific type of fault to inject into the response.
152 : */
153 : void TestOnlyInvokeCommandRequestWithFaultsInjected(Messaging::ExchangeContext * ec, System::PacketBufferHandle && payload,
154 : bool isTimedInvoke, CommandHandlerImpl::NlFaultInjectionType faultType);
155 : #endif // CHIP_WITH_NLFAULTINJECTION
156 :
157 : private:
158 : enum class State : uint8_t
159 : {
160 : ReadyForInvokeResponses, ///< Accepting InvokeResponses to send back to requester.
161 : AwaitingStatusResponse, ///< Awaiting status response from requester, after sending InvokeResponse.
162 : AllInvokeResponsesSent, ///< All InvokeResponses have been sent out.
163 : ErrorSentDelayCloseUntilOnDone ///< We have sent an early error response, but still need to clean up.
164 : };
165 :
166 : void MoveToState(const State aTargetState);
167 : const char * GetStateStr() const;
168 :
169 : /**
170 : * @brief Initiates the sending of InvokeResponses previously queued using AddInvokeResponseToSend.
171 : */
172 : void StartSendingCommandResponses();
173 :
174 3 : void SendStatusResponse(Protocols::InteractionModel::Status aStatus)
175 : {
176 3 : StatusResponse::Send(aStatus, mExchangeCtx.Get(), /*aExpectResponse = */ false);
177 3 : }
178 :
179 : CHIP_ERROR SendCommandResponse();
180 87 : bool HasMoreToSend() { return !mChunks.IsNull() || mReportResponseDropped; }
181 : void Close();
182 :
183 : // A list of InvokeResponseMessages to be sent out by CommandResponseSender.
184 : System::PacketBufferHandle mChunks;
185 :
186 : Callback * mpCallback;
187 : CommandHandlerImpl::Callback * mpCommandHandlerCallback;
188 : CommandHandlerImpl mCommandHandler;
189 : Messaging::ExchangeHolder mExchangeCtx;
190 : State mState = State::ReadyForInvokeResponses;
191 :
192 : bool mReportResponseDropped = false;
193 : };
194 :
195 : } // namespace app
196 : } // namespace chip
|