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