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 9643 : bool IsEncryptionRequired() const { return mDispatch.IsEncryptionRequired(); }
82 :
83 34255 : 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(CHIP_ERROR conErr) override;
91 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
92 : /**
93 : * Send a CHIP message on this exchange.
94 : *
95 : * If SendMessage returns success and the message was not expecting a
96 : * response, the exchange will close itself before returning, unless the
97 : * message being sent is a standalone ack. If SendMessage returns failure,
98 : * the caller is responsible for deciding what to do (e.g. closing the
99 : * exchange, trying to re-establish a secure session, etc).
100 : *
101 : * @param[in] protocolId The protocol identifier of the CHIP message to be sent.
102 : *
103 : * @param[in] msgType The message type of the corresponding protocol.
104 : *
105 : * @param[in] msgPayload A handle to the packet buffer holding the CHIP message.
106 : *
107 : * @param[in] sendFlags Flags set by the application for the CHIP message being sent.
108 : *
109 : * @retval #CHIP_ERROR_INVALID_ARGUMENT if an invalid argument was passed to this SendMessage API.
110 : * @retval #CHIP_ERROR_NOT_CONNECTED if the context was associated with a connection that is now
111 : * closed.
112 : * @retval #CHIP_ERROR_INCORRECT_STATE if the state of the exchange context is incorrect.
113 : * @retval #CHIP_NO_ERROR if the CHIP layer successfully sent the message down to the
114 : * network layer.
115 : */
116 : CHIP_ERROR SendMessage(Protocols::Id protocolId, uint8_t msgType, System::PacketBufferHandle && msgPayload,
117 : const SendFlags & sendFlags = SendFlags(SendMessageFlags::kNone));
118 :
119 : /**
120 : * A strongly-message-typed version of SendMessage.
121 : */
122 : template <typename MessageType, typename = std::enable_if_t<std::is_enum<MessageType>::value>>
123 9096 : CHIP_ERROR SendMessage(MessageType msgType, System::PacketBufferHandle && msgPayload,
124 : const SendFlags & sendFlags = SendFlags(SendMessageFlags::kNone))
125 : {
126 9096 : return SendMessage(Protocols::MessageTypeTraits<MessageType>::ProtocolId(), to_underlying(msgType), std::move(msgPayload),
127 9096 : sendFlags);
128 : }
129 :
130 : /**
131 : * A notification that we will have SendMessage called on us in the future
132 : * (and should stay open until that happens).
133 : */
134 3530 : void WillSendMessage() { mFlags.Set(Flags::kFlagWillSendMessage); }
135 :
136 : /**
137 : * Handle a received CHIP message on this exchange.
138 : *
139 : * @param[in] messageCounter The message counter of the packet.
140 : * @param[in] payloadHeader A reference to the PayloadHeader object.
141 : * @param[in] msgFlags The message flags corresponding to the received message
142 : * @param[in] msgBuf A handle to the packet buffer holding the CHIP message.
143 : *
144 : * @retval #CHIP_ERROR_INVALID_ARGUMENT if an invalid argument was passed to this HandleMessage API.
145 : * @retval #CHIP_ERROR_INCORRECT_STATE if the state of the exchange context is incorrect.
146 : * @retval #CHIP_NO_ERROR if the CHIP layer successfully delivered the message up to the
147 : * protocol layer.
148 : */
149 : CHIP_ERROR HandleMessage(uint32_t messageCounter, const PayloadHeader & payloadHeader, MessageFlags msgFlags,
150 : System::PacketBufferHandle && msgBuf);
151 :
152 9704 : ExchangeDelegate * GetDelegate() const { return mDelegate; }
153 4120 : void SetDelegate(ExchangeDelegate * delegate) { mDelegate = delegate; }
154 :
155 43614 : ExchangeManager * GetExchangeMgr() const { return mExchangeMgr; }
156 :
157 183199 : ReliableMessageContext * GetReliableMessageContext() { return static_cast<ReliableMessageContext *>(this); };
158 :
159 34286 : SessionHandle GetSessionHandle() const
160 : {
161 34286 : VerifyOrDieWithObject(mSession, this);
162 34286 : auto sessionHandle = mSession.Get();
163 34286 : return std::move(sessionHandle.Value());
164 34286 : }
165 :
166 68 : bool HasSessionHandle() const { return mSession; }
167 :
168 0 : uint16_t GetExchangeId() const { return mExchangeId; }
169 :
170 : /*
171 : * In order to use reference counting (see refCount below) we use a hold/free paradigm where users of the exchange
172 : * can hold onto it while it's out of their direct control to make sure it isn't closed before everyone's ready.
173 : * A customized version of reference counting is used since there are some extra stuff to do within Release.
174 : */
175 : void Close();
176 : void Abort();
177 :
178 : // Applies a suggested response timeout value based on the session type and the given upper layer processing time for
179 : // the next message to the exchange. The exchange context must have a valid session when calling this function.
180 : //
181 : // This function is an equivalent of SetResponseTimeout(mSession->ComputeRoundTripTimeout(applicationProcessingTimeout))
182 : void UseSuggestedResponseTimeout(Timeout applicationProcessingTimeout);
183 :
184 : // Set the response timeout for the exchange context, regardless of the underlying session type. Using
185 : // UseSuggestedResponseTimeout to set a timeout based on the type of the session and the application processing time instead of
186 : // using this function is recommended.
187 : //
188 : // If a timeout of 0 is provided, it implies no response is expected. Consequently, ExchangeDelegate::OnResponseTimeout will not
189 : // be called.
190 : //
191 : void SetResponseTimeout(Timeout timeout);
192 :
193 : // This API is used by commands that need to shut down all existing
194 : // sessions/exchanges on a fabric but need to make sure the response to the
195 : // command still goes out on the exchange the command came in on. This API
196 : // will ensure that all secure sessions for the fabric this exchanges is on
197 : // are released except the one this exchange is using, and will release
198 : // that session once this exchange is done sending the response.
199 : //
200 : // This API is a no-op if called on an exchange that is not using a
201 : // SecureSession.
202 : void AbortAllOtherCommunicationOnFabric();
203 :
204 : /**
205 : * Determine whether a response is currently expected for a message that was sent over
206 : * this exchange. While this is true, attempts to send other messages that expect a response
207 : * will fail.
208 : *
209 : * @return Returns 'true' if response expected, else 'false'.
210 : */
211 : bool IsResponseExpected() const;
212 :
213 : /**
214 : * Determine whether we are expecting our consumer to send a message on
215 : * this exchange (i.e. WillSendMessage was called and the message has not
216 : * yet been sent).
217 : */
218 5854 : bool IsSendExpected() const { return mFlags.Has(Flags::kFlagWillSendMessage); }
219 :
220 : /**
221 : * Tracks whether we have received at least one application level message
222 : * during the life-time of this exchange
223 : *
224 : * @return Returns 'true' if we have received at least one message, else 'false'
225 : */
226 8086 : inline bool HasReceivedAtLeastOneMessage() { return mFlags.Has(Flags::kFlagReceivedAtLeastOneMessage); }
227 :
228 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
229 : SessionHolder & GetSessionHolder() { return mSession; }
230 :
231 : enum class InjectedFailureType : uint8_t
232 : {
233 : kFailOnSend = 0x01
234 : };
235 :
236 : void InjectFailure(InjectedFailureType failureType) { mInjectedFailures.Set(failureType); }
237 :
238 : void ClearInjectedFailures() { mInjectedFailures.ClearAll(); }
239 : #endif
240 :
241 0 : void DumpToLog() const
242 : {
243 0 : ChipLogError(ExchangeManager, "ExchangeContext: " ChipLogFormatExchangeId " delegate=" ChipLogFormatRtti,
244 : ChipLogValueExchangeId(GetExchangeId(), IsInitiator()), ChipLogValueRtti(mDelegate));
245 0 : }
246 :
247 : private:
248 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
249 : BitFlags<InjectedFailureType> mInjectedFailures;
250 : #endif
251 :
252 : class ExchangeSessionHolder : public SessionHolderWithDelegate
253 : {
254 : public:
255 3429 : ExchangeSessionHolder(ExchangeContext & exchange) : SessionHolderWithDelegate(exchange) {}
256 : void GrabExpiredSession(const SessionHandle & session);
257 : };
258 :
259 3429 : Timeout mResponseTimeout{ 0 }; // Maximum time to wait for response (in milliseconds); 0 disables response timeout.
260 : ExchangeDelegate * mDelegate = nullptr;
261 : ExchangeManager * mExchangeMgr = nullptr;
262 :
263 : ExchangeMessageDispatch & mDispatch;
264 :
265 : ExchangeSessionHolder mSession; // The connection state
266 : uint16_t mExchangeId; // Assigned exchange ID.
267 :
268 : /**
269 : * Track whether we are now expecting a response to a message sent via this exchange (because that
270 : * message had the kExpectResponse flag set in its sendFlags).
271 : *
272 : * @param[in] inResponseExpected A Boolean indicating whether (true) or not
273 : * (false) a response is currently expected on this
274 : * exchange.
275 : */
276 : void SetResponseExpected(bool inResponseExpected);
277 :
278 : /**
279 : * Search for an existing exchange that the message applies to.
280 : *
281 : * @param[in] session The secure session of the received message.
282 : *
283 : * @param[in] packetHeader A reference to the PacketHeader object.
284 : *
285 : * @param[in] payloadHeader A reference to the PayloadHeader object.
286 : *
287 : * @retval true If a match is found.
288 : * @retval false If a match is not found.
289 : */
290 : bool MatchExchange(const SessionHandle & session, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader);
291 :
292 : /**
293 : * Notify our delegate, if any, that we have timed out waiting for a
294 : * response. If aCloseIfNeeded is true, check whether the exchange needs to
295 : * be closed.
296 : */
297 : void NotifyResponseTimeout(bool aCloseIfNeeded);
298 :
299 : CHIP_ERROR StartResponseTimer();
300 :
301 : void CancelResponseTimer();
302 : static void HandleResponseTimeout(System::Layer * aSystemLayer, void * aAppState);
303 :
304 : void DoClose(bool clearRetransTable);
305 :
306 : /**
307 : * We have handled an application-level message in some way and should
308 : * re-evaluate out state to see whether we should still be open.
309 : */
310 : void MessageHandled();
311 :
312 : static ExchangeMessageDispatch & GetMessageDispatch(bool isEphemeralExchange, ExchangeDelegate * delegate);
313 :
314 : // If SetAutoReleaseSession() is called, this exchange must be using a SecureSession, and should
315 : // evict it when the exchange is done with all its work (including any MRP traffic).
316 4 : inline void SetIgnoreSessionRelease(bool ignore) { mFlags.Set(Flags::kFlagIgnoreSessionRelease, ignore); }
317 :
318 65 : inline bool ShouldIgnoreSessionRelease() { return mFlags.Has(Flags::kFlagIgnoreSessionRelease); }
319 :
320 8087 : inline void SetHasReceivedAtLeastOneMessage(bool hasReceivedMessage)
321 : {
322 8087 : mFlags.Set(Flags::kFlagReceivedAtLeastOneMessage, hasReceivedMessage);
323 8087 : }
324 : };
325 :
326 : } // namespace Messaging
327 : } // namespace chip
|