Matter SDK Coverage Report
Current view: top level - transport - Session.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 79.7 % 64 51
Test Date: 2025-01-17 19:00:11 Functions: 76.5 % 34 26

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2021 Project CHIP Authors
       3              :  *
       4              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       5              :  *    you may not use this file except in compliance with the License.
       6              :  *    You may obtain a copy of the License at
       7              :  *
       8              :  *        http://www.apache.org/licenses/LICENSE-2.0
       9              :  *
      10              :  *    Unless required by applicable law or agreed to in writing, software
      11              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      12              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13              :  *    See the License for the specific language governing permissions and
      14              :  *    limitations under the License.
      15              :  */
      16              : 
      17              : #pragma once
      18              : 
      19              : #include <access/SubjectDescriptor.h>
      20              : #include <credentials/FabricTable.h>
      21              : #include <lib/core/CHIPConfig.h>
      22              : #include <lib/core/Optional.h>
      23              : #include <lib/core/PeerId.h>
      24              : #include <lib/core/ScopedNodeId.h>
      25              : #include <lib/support/IntrusiveList.h>
      26              : #include <lib/support/ReferenceCountedHandle.h>
      27              : #include <messaging/ReliableMessageProtocolConfig.h>
      28              : #include <messaging/SessionParameters.h>
      29              : #include <platform/LockTracker.h>
      30              : #include <transport/SessionDelegate.h>
      31              : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
      32              : #include <transport/raw/TCP.h>
      33              : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
      34              : 
      35              : namespace chip {
      36              : namespace Transport {
      37              : class Session;
      38              : } // namespace Transport
      39              : 
      40              : /** @brief
      41              :  *    Non-copyable session reference. All SessionHandles are created within SessionManager. It is not allowed to store SessionHandle
      42              :  *    anywhere except for function arguments and return values.
      43              :  *
      44              :  *    SessionHandle is reference counted such that it is never dangling, but there can be a gray period when the session is marked
      45              :  *    as pending removal but not actually removed yet. During this period, the handle is functional, but the underlying session
      46              :  *    won't be able to be grabbed by any SessionHolder. SessionHandle->IsActiveSession can be used to check if the session is
      47              :  *    active.
      48              :  *
      49              :  *    A `SessionHandle` exists to guarantee that the object it points to will not be released while allowing passing
      50              :  *    the handle as a reference, to not incur extra reference-counted Retain/Release calls.
      51              :  *
      52              :  *    Example code that breaks assumptions (hard to reason about/maintain):
      53              :  *
      54              :  *       void Process(ReferenceCountedHandle<Session> &handle);
      55              :  *       class Foo {
      56              :  *          ReferenceCountedHandle<Session> mSession;
      57              :  *          void ResetSession() { mSession = createNewSession(); }
      58              :  *          void DoProcessing() {
      59              :  *             Process(mSession);
      60              :  *          }
      61              :  *
      62              :  *          static Foo& GetInstance();
      63              :  *       };
      64              :  *
      65              :  *       void Process(ReferenceCountedHandle<Session> &handle) {
      66              :  *          Foo::GetInstance()->ResetSession(); // this changes the passed in handle
      67              :  *          // trying to use "&handle" here may point to something else altogether.
      68              :  *       }
      69              :  *
      70              :  *   Above would be fixed if we would pass in the handles by value, however that adds extra code
      71              :  *   to call Retain/Release every time. We could also by design say that passed in references will
      72              :  *   not change, however historically the codebase is complex enough that this could not be ensured.
      73              :  *
      74              :  *   The end result is the existence of SessionHandle which is NOT allowed to be held and it serves
      75              :  *   as a marker of "Retain has been called and stays valid". The code above becomes:
      76              :  *
      77              :  *      void Process(SessionHandle &handle);
      78              :  *
      79              :  *      ....
      80              :  *      void Foo::DoProcessing() {
      81              :  *         SessionHandle handle(mSession);  // retains the session and mSession can be independently changed
      82              :  *         Process(&handle);  // reference is now safe to use.
      83              :  *      }
      84              :  *
      85              :  *   To meet the requirements of "you should not store this", the Handle has additional restrictions
      86              :  *   preventing modification (no assignment or copy constructor) and allows only move.
      87              :  *   NOTE: `move` should likely also not be allowed, however we need to have the ability to
      88              :  *         return such objects from method calls, so it is currently allowed.
      89              :  *
      90              :  */
      91              : class SessionHandle
      92              : {
      93              : public:
      94          458 :     SessionHandle(Transport::Session & session) : mSession(session) {}
      95           24 :     ~SessionHandle() {}
      96              : 
      97              :     SessionHandle(const SessionHandle &)           = delete;
      98              :     SessionHandle operator=(const SessionHandle &) = delete;
      99         9863 :     SessionHandle(SessionHandle &&)                = default;
     100              :     SessionHandle & operator=(SessionHandle &&)    = delete;
     101              : 
     102            0 :     bool operator==(const SessionHandle & that) const { return &mSession.Get() == &that.mSession.Get(); }
     103              : 
     104        75648 :     Transport::Session * operator->() const { return mSession.operator->(); }
     105              : 
     106              : private:
     107              :     friend class SessionHolder;
     108              :     ReferenceCountedHandle<Transport::Session> mSession;
     109              : };
     110              : 
     111              : /** @brief
     112              :  *    Managed session reference. The object is used to store a session, the stored session will be automatically
     113              :  *    released when the underlying session is released. One must verify it is available before use. The object can be
     114              :  *    created using SessionHandle.Grab()
     115              :  */
     116              : class SessionHolder : public IntrusiveListNodeBase<>
     117              : {
     118              : public:
     119            3 :     SessionHolder() {}
     120            0 :     SessionHolder(const SessionHandle & handle) { Grab(handle); }
     121              :     virtual ~SessionHolder();
     122              : 
     123              :     SessionHolder(const SessionHolder &);
     124              :     SessionHolder(SessionHolder && that);
     125              :     SessionHolder & operator=(const SessionHolder &);
     126              :     SessionHolder & operator=(SessionHolder && that);
     127              : 
     128         1465 :     virtual void SessionReleased() { Release(); }
     129            0 :     virtual void ShiftToSession(const SessionHandle & session)
     130              :     {
     131            0 :         Release();
     132            0 :         Grab(session);
     133            0 :     }
     134              : 
     135        12098 :     bool Contains(const SessionHandle & session) const
     136              :     {
     137        12098 :         return mSession.HasValue() && &mSession.Value().Get() == &session.mSession.Get();
     138              :     }
     139              : 
     140              :     bool GrabPairingSession(const SessionHandle & session); // Should be only used inside CASE/PASE pairing.
     141              :     bool Grab(const SessionHandle & session);
     142              :     void Release();
     143              : 
     144        24982 :     explicit operator bool() const { return mSession.HasValue(); }
     145         7674 :     Optional<SessionHandle> Get() const
     146              :     {
     147              :         //
     148              :         // We cannot return mSession directly even if Optional<SessionHandle> is internally composed of the same bits,
     149              :         // since they are not actually equivalent type-wise, and SessionHandle does not permit copy-construction.
     150              :         //
     151              :         // So, construct a new Optional<SessionHandle> from the underlying Transport::Session reference.
     152              :         //
     153        15348 :         return mSession.HasValue() ? chip::MakeOptional<SessionHandle>(mSession.Value().Get())
     154        15348 :                                    : chip::Optional<SessionHandle>::Missing();
     155              :     }
     156              : 
     157        16748 :     Transport::Session * operator->() const { return &mSession.Value().Get(); }
     158              : 
     159              :     // There is no delegate, nothing to do here
     160           17 :     virtual void OnSessionHang() {}
     161              : 
     162              : protected:
     163              :     // Helper for use by the Grab methods.
     164              :     void GrabUnchecked(const SessionHandle & session);
     165              : 
     166              :     Optional<ReferenceCountedHandle<Transport::Session>> mSession;
     167              : };
     168              : 
     169              : /// @brief Extends SessionHolder to allow propagate SessionDelegate::* events to a given destination
     170              : class SessionHolderWithDelegate : public SessionHolder
     171              : {
     172              : public:
     173         3392 :     SessionHolderWithDelegate(SessionDelegate & delegate) : mDelegate(delegate) {}
     174              :     SessionHolderWithDelegate(const SessionHandle & handle, SessionDelegate & delegate) : SessionHolder(handle), mDelegate(delegate)
     175              :     {}
     176        87811 :     operator bool() const { return SessionHolder::operator bool(); }
     177              : 
     178           57 :     void SessionReleased() override
     179              :     {
     180           57 :         Release();
     181              : 
     182              :         // Note, the session is already cleared during mDelegate.OnSessionReleased
     183           57 :         mDelegate.OnSessionReleased();
     184           57 :     }
     185              : 
     186            0 :     void ShiftToSession(const SessionHandle & session) override
     187              :     {
     188            0 :         if (mDelegate.GetNewSessionHandlingPolicy() == SessionDelegate::NewSessionHandlingPolicy::kShiftToNewSession)
     189            0 :             SessionHolder::ShiftToSession(session);
     190            0 :     }
     191              : 
     192           11 :     void OnSessionHang() override { mDelegate.OnSessionHang(); }
     193              : 
     194              : private:
     195              :     SessionDelegate & mDelegate;
     196              : };
     197              : 
     198              : namespace Transport {
     199              : 
     200              : class SecureSession;
     201              : class UnauthenticatedSession;
     202              : class IncomingGroupSession;
     203              : class OutgoingGroupSession;
     204              : 
     205              : class Session
     206              : {
     207              : public:
     208       133775 :     virtual ~Session() {}
     209              : 
     210              :     enum class SessionType : uint8_t
     211              :     {
     212              :         kUndefined       = 0,
     213              :         kUnauthenticated = 1,
     214              :         kSecure          = 2,
     215              :         kGroupIncoming   = 3,
     216              :         kGroupOutgoing   = 4,
     217              :     };
     218              : 
     219              :     virtual SessionType GetSessionType() const = 0;
     220              : 
     221         8658 :     void AddHolder(SessionHolder & holder)
     222              :     {
     223         8658 :         assertChipStackLockedByCurrentThread();
     224         8658 :         VerifyOrDie(!holder.IsInList());
     225         8658 :         mHolders.PushBack(&holder);
     226         8658 :     }
     227              : 
     228         8658 :     void RemoveHolder(SessionHolder & holder)
     229              :     {
     230         8658 :         assertChipStackLockedByCurrentThread();
     231         8658 :         VerifyOrDie(mHolders.Contains(&holder));
     232         8658 :         mHolders.Remove(&holder);
     233         8658 :     }
     234              : 
     235              :     virtual void Retain()  = 0;
     236              :     virtual void Release() = 0;
     237              : 
     238              :     virtual bool IsActiveSession() const = 0;
     239              : 
     240              :     virtual ScopedNodeId GetPeer() const                                 = 0;
     241              :     virtual ScopedNodeId GetLocalScopedNodeId() const                    = 0;
     242              :     virtual Access::SubjectDescriptor GetSubjectDescriptor() const       = 0;
     243              :     virtual bool AllowsMRP() const                                       = 0;
     244              :     virtual bool AllowsLargePayload() const                              = 0;
     245              :     virtual const SessionParameters & GetRemoteSessionParameters() const = 0;
     246              :     virtual System::Clock::Timestamp GetMRPBaseTimeout() const           = 0;
     247              : 
     248              :     // Returns true if `subjectDescriptor.IsCommissioning` (based on Core Specification
     249              :     // pseudocode in ACL Architecture chapter) should be true when computing a
     250              :     // subject descriptor for that session. This is only valid to call during
     251              :     // synchronous processing of a message received on the session.
     252            0 :     virtual bool IsCommissioningSession() const { return false; }
     253              : 
     254              :     // GetAckTimeout is the estimate for how long it could take for the other
     255              :     // side to receive our message (accounting for our MRP retransmits if it
     256              :     // gets lost) and send a response.
     257              :     virtual System::Clock::Milliseconds32 GetAckTimeout() const = 0;
     258              : 
     259              :     // GetReceiptTimeout is the estimate for how long it could take for us to
     260              :     // receive a message after the other side sends it, accounting for the MRP
     261              :     // retransmits the other side might do if the message gets lost.
     262              :     //
     263              :     // The caller is expected to provide an estimate for when the peer would
     264              :     // last have heard from us.  The most likely values to pass are
     265              :     // System::SystemClock().GetMonotonicTimestamp() (to indicate "peer is
     266              :     // responding to a message it just received") and System::Clock::kZero (to
     267              :     // indicate "peer is reaching out to us, not in response to anything").
     268              :     virtual System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const = 0;
     269              : 
     270         2133 :     const ReliableMessageProtocolConfig & GetRemoteMRPConfig() const { return GetRemoteSessionParameters().GetMRPConfig(); }
     271              : 
     272              :     // Returns a suggested timeout value based on the round-trip time it takes for the peer at the other end of the session to
     273              :     // receive a message, process it and send it back. This is computed based on the session type, the type of transport, sleepy
     274              :     // characteristics of the target and a caller-provided value for the time it takes to process a message at the upper layer on
     275              :     // the target For group sessions, this function will always return 0.
     276              :     System::Clock::Timeout ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout);
     277              : 
     278        79015 :     FabricIndex GetFabricIndex() const { return mFabricIndex; }
     279              : 
     280              :     SecureSession * AsSecureSession();
     281              :     UnauthenticatedSession * AsUnauthenticatedSession();
     282              :     IncomingGroupSession * AsIncomingGroupSession();
     283              :     OutgoingGroupSession * AsOutgoingGroupSession();
     284              : 
     285        40887 :     bool IsGroupSession() const
     286              :     {
     287        40887 :         return GetSessionType() == SessionType::kGroupIncoming || GetSessionType() == SessionType::kGroupOutgoing;
     288              :     }
     289              : 
     290        27423 :     bool IsSecureSession() const { return GetSessionType() == SessionType::kSecure; }
     291              : 
     292          120 :     bool IsUnauthenticatedSession() const { return GetSessionType() == SessionType::kUnauthenticated; }
     293              : 
     294              : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
     295              :     // This API is used to associate the connection with the session when the
     296              :     // latter is about to be marked active. It is also used to reset the
     297              :     // connection to a nullptr when the connection is lost and the session
     298              :     // is marked as Defunct.
     299            0 :     ActiveTCPConnectionState * GetTCPConnection() const { return mTCPConnection; }
     300            0 :     void SetTCPConnection(ActiveTCPConnectionState * conn) { mTCPConnection = conn; }
     301              : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
     302              : 
     303           11 :     void NotifySessionHang()
     304              :     {
     305              :         // Holders might remove themselves when notified.
     306           11 :         auto holder = mHolders.begin();
     307           39 :         while (holder != mHolders.end())
     308              :         {
     309           28 :             auto cur = holder;
     310           28 :             ++holder;
     311           28 :             cur->OnSessionHang();
     312              :         }
     313           11 :     }
     314              : 
     315              :     // Return a session id that is usable for logging. This is the local session
     316              :     // id for secure unicast sessions, 0 for non-secure unicast sessions, and
     317              :     // the group id for group sessions.
     318              :     uint16_t SessionIdForLogging() const;
     319              : 
     320              : protected:
     321              :     // This should be called by sub-classes at the very beginning of the destructor, before any data field is disposed, such that
     322              :     // the session is still functional during the callback.
     323       133621 :     void NotifySessionReleased()
     324              :     {
     325       133621 :         SessionHandle session(*this);
     326       133652 :         while (!mHolders.Empty())
     327              :         {
     328           31 :             mHolders.begin()->SessionReleased(); // SessionReleased must remove the item from the linked list
     329              :         }
     330       133621 :     }
     331              : 
     332         1247 :     void SetFabricIndex(FabricIndex index) { mFabricIndex = index; }
     333              : 
     334              :     const SecureSession * AsConstSecureSession() const;
     335              :     const IncomingGroupSession * AsConstIncomingGroupSession() const;
     336              :     const OutgoingGroupSession * AsConstOutgoingGroupSession() const;
     337              : 
     338              :     IntrusiveList<SessionHolder> mHolders;
     339              : 
     340              : private:
     341              :     FabricIndex mFabricIndex = kUndefinedFabricIndex;
     342              : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
     343              :     // The underlying TCP connection object over which the session is
     344              :     // established.
     345              :     // The lifetime of this member connection pointer is, essentially, the same
     346              :     // as that of the underlying connection with the peer.
     347              :     // It would remain as a nullptr for all sessions that are not set up over
     348              :     // a TCP connection.
     349              :     ActiveTCPConnectionState * mTCPConnection = nullptr;
     350              : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
     351              : };
     352              : 
     353              : //
     354              : // Return a string representation of the underlying session.
     355              : //
     356              : // Always returns a non-null pointer.
     357              : //
     358              : const char * GetSessionTypeString(const SessionHandle & session);
     359              : 
     360              : } // namespace Transport
     361              : } // namespace chip
        

Generated by: LCOV version 2.0-1