Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
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 : /**
19 : * @file
20 : * This file defines the classes corresponding to CHIP Exchange Context.
21 : *
22 : */
23 :
24 : #pragma once
25 :
26 : #include <lib/core/ReferenceCounted.h>
27 : #include <lib/support/BitFlags.h>
28 : #include <lib/support/DLLUtil.h>
29 : #include <lib/support/ReferenceCountedHandle.h>
30 : #include <lib/support/TypeTraits.h>
31 : #include <messaging/ExchangeDelegate.h>
32 : #include <messaging/Flags.h>
33 : #include <messaging/ReliableMessageContext.h>
34 : #include <protocols/Protocols.h>
35 : #include <transport/SessionManager.h>
36 :
37 : namespace chip {
38 :
39 : namespace Messaging {
40 :
41 : class ExchangeManager;
42 : class ExchangeContext;
43 : class ExchangeMessageDispatch;
44 : using ExchangeHandle = ReferenceCountedHandle<ExchangeContext>;
45 :
46 : class ExchangeContextDeletor
47 : {
48 : public:
49 : static void Release(ExchangeContext * obj);
50 : };
51 :
52 : /**
53 : * @brief
54 : * This class represents an ongoing conversation (ExchangeContext) between two or more nodes.
55 : * It defines methods for encoding and communicating CHIP messages within an ExchangeContext
56 : * over various transport mechanisms, for example, TCP, UDP, or CHIP Reliable Messaging.
57 : *
58 : */
59 : class DLL_EXPORT ExchangeContext : public ReliableMessageContext,
60 : public ReferenceCounted<ExchangeContext, ExchangeContextDeletor>,
61 : public SessionDelegate
62 : {
63 : friend class ExchangeManager;
64 : friend class ExchangeContextDeletor;
65 :
66 : public:
67 : typedef System::Clock::Timeout Timeout; // Type used to express the timeout in this ExchangeContext
68 :
69 : ExchangeContext(ExchangeManager * em, uint16_t ExchangeId, const SessionHandle & session, bool Initiator,
70 : ExchangeDelegate * delegate, bool isEphemeralExchange = false);
71 :
72 : ~ExchangeContext() override;
73 :
74 : /**
75 : * Determine whether the context is the initiator of the exchange.
76 : *
77 : * @return Returns 'true' if it is the initiator, else 'false'.
78 : */
79 : bool IsInitiator() const;
80 :
81 14868 : bool IsEncryptionRequired() const { return mDispatch.IsEncryptionRequired(); }
82 :
83 58895 : bool IsGroupExchangeContext() const { return mSession && mSession->IsGroupSession(); }
84 :
85 : // Implement SessionDelegate
86 0 : NewSessionHandlingPolicy GetNewSessionHandlingPolicy() override { return NewSessionHandlingPolicy::kStayAtOldSession; }
87 : void OnSessionReleased() override;
88 :
89 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
90 : void OnSessionConnectionClosed(const Transport::ActiveTCPConnectionState & conn, CHIP_ERROR conErr) override;
91 : void OnConnectionAttemptComplete(Transport::ActiveTCPConnectionHolder & conn, CHIP_ERROR conErr) override;
92 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
93 : /**
94 : * Send a CHIP message on this exchange.
95 : *
96 : * If SendMessage returns success and the message was not expecting a
97 : * response, the exchange will close itself before returning, unless the
98 : * message being sent is a standalone ack. If SendMessage returns failure,
99 : * the caller is responsible for deciding what to do (e.g. closing the
100 : * exchange, trying to re-establish a secure session, etc).
101 : *
102 : * @param[in] protocolId The protocol identifier of the CHIP message to be sent.
103 : *
104 : * @param[in] msgType The message type of the corresponding protocol.
105 : *
106 : * @param[in] msgPayload A handle to the packet buffer holding the CHIP message.
107 : *
108 : * @param[in] sendFlags Flags set by the application for the CHIP message being sent.
109 : *
110 : * @retval #CHIP_ERROR_INVALID_ARGUMENT if an invalid argument was passed to this SendMessage API.
111 : * @retval #CHIP_ERROR_NOT_CONNECTED if the context was associated with a connection that is now
112 : * closed.
113 : * @retval #CHIP_ERROR_INCORRECT_STATE if the state of the exchange context is incorrect.
114 : * @retval #CHIP_NO_ERROR if the CHIP layer successfully sent the message down to the
115 : * network layer.
116 : */
117 : CHIP_ERROR SendMessage(Protocols::Id protocolId, uint8_t msgType, System::PacketBufferHandle && msgPayload,
118 : const SendFlags & sendFlags = SendFlags(SendMessageFlags::kNone));
119 :
120 : /**
121 : * A strongly-message-typed version of SendMessage.
122 : */
123 : template <typename MessageType, typename = std::enable_if_t<std::is_enum<MessageType>::value>>
124 15018 : CHIP_ERROR SendMessage(MessageType msgType, System::PacketBufferHandle && msgPayload,
125 : const SendFlags & sendFlags = SendFlags(SendMessageFlags::kNone))
126 : {
127 15018 : return SendMessage(Protocols::MessageTypeTraits<MessageType>::ProtocolId(), to_underlying(msgType), std::move(msgPayload),
128 15018 : sendFlags);
129 : }
130 :
131 : /**
132 : * A notification that we will have SendMessage called on us in the future
133 : * (and should stay open until that happens).
134 : */
135 4370 : void WillSendMessage() { mFlags.Set(Flags::kFlagWillSendMessage); }
136 :
137 : /**
138 : * Handle a received CHIP message on this exchange.
139 : *
140 : * @param[in] messageCounter The message counter of the packet.
141 : * @param[in] payloadHeader A reference to the PayloadHeader object.
142 : * @param[in] msgFlags The message flags corresponding to the received message
143 : * @param[in] msgBuf A handle to the packet buffer holding the CHIP message.
144 : *
145 : * @retval #CHIP_ERROR_INVALID_ARGUMENT if an invalid argument was passed to this HandleMessage API.
146 : * @retval #CHIP_ERROR_INCORRECT_STATE if the state of the exchange context is incorrect.
147 : * @retval #CHIP_NO_ERROR if the CHIP layer successfully delivered the message up to the
148 : * protocol layer.
149 : */
150 : CHIP_ERROR HandleMessage(uint32_t messageCounter, const PayloadHeader & payloadHeader, MessageFlags msgFlags,
151 : System::PacketBufferHandle && msgBuf);
152 :
153 14942 : ExchangeDelegate * GetDelegate() const { return mDelegate; }
154 8982 : void SetDelegate(ExchangeDelegate * delegate) { mDelegate = delegate; }
155 :
156 67936 : ExchangeManager * GetExchangeMgr() const { return mExchangeMgr; }
157 :
158 277634 : ReliableMessageContext * GetReliableMessageContext() { return static_cast<ReliableMessageContext *>(this); };
159 :
160 77883 : SessionHandle GetSessionHandle() const
161 : {
162 77883 : VerifyOrDieWithObject(mSession, this);
163 77883 : auto sessionHandle = mSession.Get();
164 77883 : return std::move(sessionHandle.Value());
165 77883 : }
166 :
167 186 : bool HasSessionHandle() const { return mSession; }
168 :
169 42790 : uint16_t GetExchangeId() const { return mExchangeId; }
170 :
171 : /*
172 : * In order to use reference counting (see refCount below) we use a hold/free paradigm where users of the exchange
173 : * can hold onto it while it's out of their direct control to make sure it isn't closed before everyone's ready.
174 : * A customized version of reference counting is used since there are some extra stuff to do within Release.
175 : */
176 : void Close();
177 : void Abort();
178 :
179 : // Applies a suggested response timeout value based on the session type and the given upper layer processing time for
180 : // the next message to the exchange. The exchange context must have a valid session when calling this function.
181 : //
182 : // This function is an equivalent of SetResponseTimeout(mSession->ComputeRoundTripTimeout(applicationProcessingTimeout))
183 : void UseSuggestedResponseTimeout(Timeout applicationProcessingTimeout);
184 :
185 : // Set the response timeout for the exchange context, regardless of the underlying session type. Using
186 : // UseSuggestedResponseTimeout to set a timeout based on the type of the session and the application processing time instead of
187 : // using this function is recommended.
188 : //
189 : // If a timeout of 0 is provided, it implies no response is expected. Consequently, ExchangeDelegate::OnResponseTimeout will not
190 : // be called.
191 : //
192 : void SetResponseTimeout(Timeout timeout);
193 :
194 : // This API is used by commands that need to shut down all existing
195 : // sessions/exchanges on a fabric but need to make sure the response to the
196 : // command still goes out on the exchange the command came in on. This API
197 : // will ensure that all secure sessions for the fabric this exchanges is on
198 : // are released except the one this exchange is using, and will release
199 : // that session once this exchange is done sending the response.
200 : //
201 : // This API is a no-op if called on an exchange that is not using a
202 : // SecureSession.
203 : void AbortAllOtherCommunicationOnFabric();
204 :
205 : /**
206 : * Determine whether a response is currently expected for a message that was sent over
207 : * this exchange. While this is true, attempts to send other messages that expect a response
208 : * will fail.
209 : *
210 : * @return Returns 'true' if response expected, else 'false'.
211 : */
212 : bool IsResponseExpected() const;
213 :
214 : /**
215 : * Determine whether we are expecting our consumer to send a message on
216 : * this exchange (i.e. WillSendMessage was called and the message has not
217 : * yet been sent).
218 : */
219 10850 : bool IsSendExpected() const { return mFlags.Has(Flags::kFlagWillSendMessage); }
220 :
221 : /**
222 : * Tracks whether we have received at least one application level message
223 : * during the life-time of this exchange
224 : *
225 : * @return Returns 'true' if we have received at least one message, else 'false'
226 : */
227 22166 : inline bool HasReceivedAtLeastOneMessage() { return mFlags.Has(Flags::kFlagReceivedAtLeastOneMessage); }
228 :
229 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
230 120 : SessionHolder & GetSessionHolder() { return mSession; }
231 :
232 : enum class InjectedFailureType : uint8_t
233 : {
234 : kFailOnSend = 0x01
235 : };
236 :
237 4 : void InjectFailure(InjectedFailureType failureType) { mInjectedFailures.Set(failureType); }
238 :
239 23 : void ClearInjectedFailures() { mInjectedFailures.ClearAll(); }
240 : #endif
241 :
242 0 : void DumpToLog() const
243 : {
244 0 : ChipLogError(ExchangeManager, "ExchangeContext: " ChipLogFormatExchangeId " delegate=" ChipLogFormatRtti,
245 : ChipLogValueExchangeId(GetExchangeId(), IsInitiator()), ChipLogValueRtti(mDelegate));
246 0 : }
247 :
248 : private:
249 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
250 : BitFlags<InjectedFailureType> mInjectedFailures;
251 : #endif
252 :
253 : class ExchangeSessionHolder : public SessionHolderWithDelegate
254 : {
255 : public:
256 4688 : ExchangeSessionHolder(ExchangeContext & exchange) : SessionHolderWithDelegate(exchange) {}
257 : void GrabExpiredSession(const SessionHandle & session);
258 : };
259 :
260 4688 : Timeout mResponseTimeout{ 0 }; // Maximum time to wait for response (in milliseconds); 0 disables response timeout.
261 : ExchangeDelegate * mDelegate = nullptr;
262 : ExchangeManager * mExchangeMgr = nullptr;
263 :
264 : ExchangeMessageDispatch & mDispatch;
265 :
266 : ExchangeSessionHolder mSession; // The connection state
267 : uint16_t mExchangeId; // Assigned exchange ID.
268 :
269 : /**
270 : * Track whether we are now expecting a response to a message sent via this exchange (because that
271 : * message had the kExpectResponse flag set in its sendFlags).
272 : *
273 : * @param[in] inResponseExpected A Boolean indicating whether (true) or not
274 : * (false) a response is currently expected on this
275 : * exchange.
276 : */
277 : void SetResponseExpected(bool inResponseExpected);
278 :
279 : /**
280 : * Search for an existing exchange that the message applies to.
281 : *
282 : * @param[in] session The secure session of the received message.
283 : *
284 : * @param[in] packetHeader A reference to the PacketHeader object.
285 : *
286 : * @param[in] payloadHeader A reference to the PayloadHeader object.
287 : *
288 : * @retval true If a match is found.
289 : * @retval false If a match is not found.
290 : */
291 : bool MatchExchange(const SessionHandle & session, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader);
292 :
293 : /**
294 : * Notify our delegate, if any, that we have timed out waiting for a
295 : * response. If aCloseIfNeeded is true, check whether the exchange needs to
296 : * be closed.
297 : */
298 : void NotifyResponseTimeout(bool aCloseIfNeeded);
299 :
300 : CHIP_ERROR StartResponseTimer();
301 :
302 : void CancelResponseTimer();
303 : static void HandleResponseTimeout(System::Layer * aSystemLayer, void * aAppState);
304 :
305 : void DoClose(bool clearRetransTable);
306 :
307 : /**
308 : * We have handled an application-level message in some way and should
309 : * re-evaluate out state to see whether we should still be open.
310 : */
311 : void MessageHandled();
312 :
313 : static ExchangeMessageDispatch & GetMessageDispatch(bool isEphemeralExchange, ExchangeDelegate * delegate);
314 :
315 : // If SetAutoReleaseSession() is called, this exchange must be using a SecureSession, and should
316 : // evict it when the exchange is done with all its work (including any MRP traffic).
317 4 : inline void SetIgnoreSessionRelease(bool ignore) { mFlags.Set(Flags::kFlagIgnoreSessionRelease, ignore); }
318 :
319 65 : inline bool ShouldIgnoreSessionRelease() { return mFlags.Has(Flags::kFlagIgnoreSessionRelease); }
320 :
321 12688 : inline void SetHasReceivedAtLeastOneMessage(bool hasReceivedMessage)
322 : {
323 12688 : mFlags.Set(Flags::kFlagReceivedAtLeastOneMessage, hasReceivedMessage);
324 12688 : }
325 : };
326 :
327 : } // namespace Messaging
328 : } // namespace chip
|