text: /*
   *  Copyright (c) 2016, The OpenThread Authors.
   *  All rights reserved.
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions are met:
   *  1. Redistributions of source code must retain the above copyright
   *     notice, this list of conditions and the following disclaimer.
   *  2. Redistributions in binary form must reproduce the above copyright
   *     notice, this list of conditions and the following disclaimer in the
   *     documentation and/or other materials provided with the distribution.
   *  3. Neither the name of the copyright holder nor the
   *     names of its contributors may be used to endorse or promote products
   *     derived from this software without specific prior written permission.
   *
   *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   *  POSSIBILITY OF SUCH DAMAGE.
   */
  
  #ifndef COAP_HPP_
  #define COAP_HPP_
  
  #include "openthread-core-config.h"
  
  #include <openthread/coap.h>
  
  #include "coap/coap_message.hpp"
  #include "common/as_core_type.hpp"
  #include "common/debug.hpp"
  #include "common/linked_list.hpp"
  #include "common/locator.hpp"
  #include "common/message.hpp"
  #include "common/non_copyable.hpp"
  #include "common/timer.hpp"
  #include "net/ip6.hpp"
  #include "net/netif.hpp"
  #include "net/udp6.hpp"
  
  /**
   * @file
   *   This file includes definitions for CoAP client and server functionality.
   */
  
  namespace ot {
  
  namespace Coap {
  
  /**
   * @addtogroup core-coap
   *
   * @{
   *
   */
  
  /**
   * This type represents a function pointer which is called when a CoAP response is received or on the request timeout.
   *
   * Please see otCoapResponseHandler for details.
   *
   */
  typedef otCoapResponseHandler ResponseHandler;
  
  /**
   * This type represents a function pointer which is called when a CoAP request associated with a given URI path is
   * received.
   *
   * Please see otCoapRequestHandler for details.
   *
   */
  typedef otCoapRequestHandler RequestHandler;
  
  /**
   * This structure represents the CoAP transmission parameters.
   *
   */
  class TxParameters : public otCoapTxParameters
  {
      friend class CoapBase;
      friend class ResponsesQueue;
  
  public:
      /**
       * This static method coverts a pointer to `otCoapTxParameters` to `Coap::TxParamters`
       *
       * If the pointer is `nullptr`, the default parameters are used instead.
       *
       * @param[in] aTxParameters   A pointer to tx parameter.
       *
       * @returns A reference to corresponding `TxParamters` if  @p aTxParameters is not `nullptr`, otherwise the default
       *          tx parameters.
       *
       */
      static const TxParameters &From(const otCoapTxParameters *aTxParameters)
      {
          return aTxParameters ? *static_cast<const TxParameters *>(aTxParameters) : GetDefault();
      }
  
      /**
       * This method validates whether the CoAP transmission parameters are valid.
       *
       * @returns Whether the parameters are valid.
       *
       */
      bool IsValid(void) const;
  
      /**
       * This static method returns default CoAP tx parameters.
       *
       * @returns The default tx parameters.
       *
       */
      static const TxParameters &GetDefault(void) { return static_cast<const TxParameters &>(kDefaultTxParameters); }
  
  private:
      static constexpr uint32_t kDefaultAckTimeout                 = 2000; // in msec
      static constexpr uint8_t  kDefaultAckRandomFactorNumerator   = 3;
      static constexpr uint8_t  kDefaultAckRandomFactorDenominator = 2;
      static constexpr uint8_t  kDefaultMaxRetransmit              = 4;
      static constexpr uint32_t kDefaultMaxLatency                 = 100000; // in msec
  
      uint32_t CalculateInitialRetransmissionTimeout(void) const;
      uint32_t CalculateExchangeLifetime(void) const;
      uint32_t CalculateMaxTransmitWait(void) const;
      uint32_t CalculateSpan(uint8_t aMaxRetx) const;
  
      static const otCoapTxParameters kDefaultTxParameters;
  };
  
  /**
   * This class implements CoAP resource handling.
   *
   */
  class Resource : public otCoapResource, public LinkedListEntry<Resource>
  {
      friend class CoapBase;
  
  public:
      /**
       * This constructor initializes the resource.
       *
       * @param[in]  aUriPath  A pointer to a null-terminated string for the URI path.
       * @param[in]  aHandler  A function pointer that is called when receiving a CoAP message for @p aUriPath.
       * @param[in]  aContext  A pointer to arbitrary context information.
       */
      Resource(const char *aUriPath, RequestHandler aHandler, void *aContext)
      {
          mUriPath = aUriPath;
          mHandler = aHandler;
          mContext = aContext;
          mNext    = nullptr;
      }
  
      /**
       * This method returns a pointer to the URI path.
       *
       * @returns A pointer to the URI path.
       *
       */
      const char *GetUriPath(void) const { return mUriPath; }
  
  protected:
      void HandleRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
      {
          mHandler(mContext, &aMessage, &aMessageInfo);
      }
  };
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
  /**
   * This class implements CoAP block-wise resource handling.
   *
   */
  class ResourceBlockWise : public otCoapBlockwiseResource
  {
      friend class CoapBase;
  
  public:
      /**
       * This constructor initializes the resource.
       *
       * @param[in]  aUriPath         A pointer to a NULL-terminated string for the Uri-Path.
       * @param[in]  aHandler         A function pointer that is called when receiving a CoAP message for @p aUriPath.
       * @param[in]  aContext         A pointer to arbitrary context information.
       * @param[in]  aReceiveHook     A function pointer that is called when receiving a CoAP block message for @p
       *                              aUriPath.
       * @param[in]  aTransmitHook    A function pointer that is called when transmitting a CoAP block message from @p
       *                              aUriPath.
       */
      ResourceBlockWise(const char *                aUriPath,
                        otCoapRequestHandler        aHandler,
                        void *                      aContext,
                        otCoapBlockwiseReceiveHook  aReceiveHook,
                        otCoapBlockwiseTransmitHook aTransmitHook)
      {
          mUriPath      = aUriPath;
          mHandler      = aHandler;
          mContext      = aContext;
          mReceiveHook  = aReceiveHook;
          mTransmitHook = aTransmitHook;
          mNext         = nullptr;
      }
  
      Error HandleBlockReceive(const uint8_t *aBlock,
                               uint32_t       aPosition,
                               uint16_t       aBlockLength,
                               bool           aMore,
                               uint32_t       aTotalLength) const
      {
          return mReceiveHook(otCoapBlockwiseResource::mContext, aBlock, aPosition, aBlockLength, aMore, aTotalLength);
      }
  
      Error HandleBlockTransmit(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore) const
      {
          return mTransmitHook(otCoapBlockwiseResource::mContext, aBlock, aPosition, aBlockLength, aMore);
      }
  
      /**
       * This method gets the next entry in the linked list.
       *
       * @returns A pointer to the next entry in the linked list or `nullptr` if at the end of the list.
       *
       */
      const ResourceBlockWise *GetNext(void) const
      {
          return static_cast<const ResourceBlockWise *>(static_cast<const ResourceBlockWise *>(this)->mNext);
      }
  
      /**
       * This method gets the next entry in the linked list.
       *
       * @returns A pointer to the next entry in the linked list or `nullptr` if at the end of the list.
       *
       */
      ResourceBlockWise *GetNext(void)
      {
          return static_cast<ResourceBlockWise *>(static_cast<ResourceBlockWise *>(this)->mNext);
      }
  
      /**
       * This method sets the next pointer on the entry.
       *
       * @param[in] aNext  A pointer to the next entry.
       *
       */
      void SetNext(ResourceBlockWise *aNext) { static_cast<ResourceBlockWise *>(this)->mNext = aNext; }
  
      /**
       * This method returns a pointer to the URI path.
       *
       * @returns A pointer to the URI path.
       *
       */
      const char *GetUriPath(void) const { return mUriPath; }
  
  protected:
      void HandleRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
      {
          mHandler(mContext, &aMessage, &aMessageInfo);
      }
  };
  #endif
  
  /**
   * This class caches CoAP responses to implement message deduplication.
   *
   */
  class ResponsesQueue
  {
  public:
      /**
       * Default class constructor.
       *
       * @param[in]  aInstance  A reference to the OpenThread instance.
       *
       */
      explicit ResponsesQueue(Instance &aInstance);
  
      /**
       * This method adds a given response to the cache.
       *
       * If matching response (the same Message ID, source endpoint address and port) exists in the cache given
       * response is not added.
       *
       * The CoAP response is copied before it is added to the cache.
       *
       * @param[in]  aMessage      The CoAP response to add to the cache.
       * @param[in]  aMessageInfo  The message info corresponding to @p aMessage.
       * @param[in]  aTxParameters Transmission parameters.
       *
       */
      void EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const TxParameters &aTxParameters);
  
      /**
       * This method removes all responses from the cache.
       *
       */
      void DequeueAllResponses(void);
  
      /**
       * This method gets a copy of CoAP response from the cache that matches a given Message ID and source endpoint.
       *
       * @param[in]  aRequest      The CoAP message containing Message ID.
       * @param[in]  aMessageInfo  The message info containing source endpoint address and port.
       * @param[out] aResponse     A pointer to return a copy of a cached CoAP response matching given arguments.
       *
       * @retval kErrorNone      Matching response found and successfully created a copy.
       * @retval kErrorNoBufs    Matching response found but there is not sufficient buffer to create a copy.
       * @retval kErrorNotFound  Matching response not found.
       *
       */
      Error GetMatchedResponseCopy(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Message **aResponse);
  
      /**
       * This method gets a reference to the cached CoAP responses queue.
       *
       * @returns  A reference to the cached CoAP responses queue.
       *
       */
      const MessageQueue &GetResponses(void) const { return mQueue; }
  
  private:
      static constexpr uint16_t kMaxCachedResponses = OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES;
  
      struct ResponseMetadata
      {
          Error AppendTo(Message &aMessage) const { return aMessage.Append(*this); }
          void  ReadFrom(const Message &aMessage);
  
          TimeMilli        mDequeueTime;
          Ip6::MessageInfo mMessageInfo;
      };
  
      const Message *FindMatchedResponse(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) const;
      void           DequeueResponse(Message &aMessage);
      void           UpdateQueue(void);
  
      static void HandleTimer(Timer &aTimer);
      void        HandleTimer(void);
  
      MessageQueue      mQueue;
      TimerMilliContext mTimer;
  };
  
  /**
   * This class implements the CoAP client and server.
   *
   */
  class CoapBase : public InstanceLocator, private NonCopyable
  {
      friend class ResponsesQueue;
  
  public:
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
      static constexpr uint16_t kMaxBlockLength = OPENTHREAD_CONFIG_COAP_MAX_BLOCK_LENGTH;
  #endif
  
      /**
       * This function pointer is called before CoAP server processing a CoAP message.
       *
       * @param[in]   aMessage        A reference to the message.
       @ @param[in]   aMessageInfo    A reference to the message info associated with @p aMessage.
       * @param[in]   aContext        A pointer to arbitrary context information.
       *
       * @retval  kErrorNone      Server should continue processing this message, other return values indicates the
       *                          server should stop processing this message.
       * @retval  kErrorNotTmf    The message is not a TMF message.
       *
       */
      typedef Error (*Interceptor)(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext);
  
      /**
       * This method clears requests and responses used by this CoAP agent.
       *
       */
      void ClearRequestsAndResponses(void);
  
      /**
       * This method clears requests with specified source address used by this CoAP agent.
       *
       * @param[in]  aAddress A reference to the specified address.
       *
       */
      void ClearRequests(const Ip6::Address &aAddress);
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
  
      /**
       * This method adds a block-wise resource to the CoAP server.
       *
       * @param[in]  aResource  A reference to the resource.
       *
       */
      void AddBlockWiseResource(ResourceBlockWise &aResource);
  
      /**
       * This method removes a block-wise resource from the CoAP server.
       *
       * @param[in]  aResource  A reference to the resource.
       *
       */
      void RemoveBlockWiseResource(ResourceBlockWise &aResource);
  #endif
  
      /**
       * This method adds a resource to the CoAP server.
       *
       * @param[in]  aResource  A reference to the resource.
       *
       */
      void AddResource(Resource &aResource);
  
      /**
       * This method removes a resource from the CoAP server.
       *
       * @param[in]  aResource  A reference to the resource.
       *
       */
      void RemoveResource(Resource &aResource);
  
      /* This method sets the default handler for unhandled CoAP requests.
       *
       * @param[in]  aHandler   A function pointer that shall be called when an unhandled request arrives.
       * @param[in]  aContext   A pointer to arbitrary context information. May be `nullptr` if not used.
       *
       */
      void SetDefaultHandler(RequestHandler aHandler, void *aContext);
  
      /**
       * This method creates a new message with a CoAP header.
       *
       * @param[in]  aSettings  The message settings.
       *
       * @returns A pointer to the message or `nullptr` if failed to allocate message.
       *
       */
      Message *NewMessage(const Message::Settings &aSettings = Message::Settings::GetDefault());
  
      /**
       * This method creates a new message with a CoAP header that has Network Control priority level.
       *
       * @returns A pointer to the message or `nullptr` if failed to allocate message.
       *
       */
      Message *NewPriorityMessage(void)
      {
          return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet));
      }
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
      /**
       * This method sends a CoAP message block-wise with custom transmission parameters.
       *
       * If a response for a request is expected, respective function and context information should be provided.
       * If no response is expected, these arguments should be NULL pointers.
       * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
       *
       * @param[in]  aMessage      A reference to the message to send.
       * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
       * @param[in]  aTxParameters A reference to transmission parameters for this message.
       * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
       * @param[in]  aContext      A pointer to arbitrary context information.
       * @param[in]  aTransmitHook A pointer to a hook function for outgoing block-wise transfer.
       * @param[in]  aReceiveHook  A pointer to a hook function for incoming block-wise transfer.
       *
       * @retval kErrorNone     Successfully sent CoAP message.
       * @retval kErrorNoBufs   Failed to allocate retransmission data.
       *
       */
      Error SendMessage(Message &                   aMessage,
                        const Ip6::MessageInfo &    aMessageInfo,
                        const TxParameters &        aTxParameters,
                        otCoapResponseHandler       aHandler      = nullptr,
                        void *                      aContext      = nullptr,
                        otCoapBlockwiseTransmitHook aTransmitHook = nullptr,
                        otCoapBlockwiseReceiveHook  aReceiveHook  = nullptr);
  #else  // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
  
      /**
       * This method sends a CoAP message with custom transmission parameters.
       *
       * If a response for a request is expected, respective function and context information should be provided.
       * If no response is expected, these arguments should be `nullptr` pointers.
       * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
       *
       * @param[in]  aMessage      A reference to the message to send.
       * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
       * @param[in]  aTxParameters A reference to transmission parameters for this message.
       * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
       * @param[in]  aContext      A pointer to arbitrary context information.
       *
       * @retval kErrorNone    Successfully sent CoAP message.
       * @retval kErrorNoBufs  Insufficient buffers available to send the CoAP message.
       *
       */
      Error SendMessage(Message &               aMessage,
                        const Ip6::MessageInfo &aMessageInfo,
                        const TxParameters &    aTxParameters,
                        ResponseHandler         aHandler = nullptr,
                        void *                  aContext = nullptr);
  #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
  
      /**
       * This method sends a CoAP message with default transmission parameters.
       *
       * If a response for a request is expected, respective function and context information should be provided.
       * If no response is expected, these arguments should be `nullptr` pointers.
       * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
       *
       * @param[in]  aMessage      A reference to the message to send.
       * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
       * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
       * @param[in]  aContext      A pointer to arbitrary context information.
       *
       * @retval kErrorNone    Successfully sent CoAP message.
       * @retval kErrorNoBufs  Insufficient buffers available to send the CoAP response.
       *
       */
      Error SendMessage(Message &               aMessage,
                        const Ip6::MessageInfo &aMessageInfo,
                        ResponseHandler         aHandler = nullptr,
                        void *                  aContext = nullptr);
  
      /**
       * This method sends a CoAP reset message.
       *
       * @param[in]  aRequest        A reference to the CoAP Message that was used in CoAP request.
       * @param[in]  aMessageInfo    The message info corresponding to the CoAP request.
       *
       * @retval kErrorNone          Successfully enqueued the CoAP response message.
       * @retval kErrorNoBufs        Insufficient buffers available to send the CoAP response.
       * @retval kErrorInvalidArgs   The @p aRequest is not of confirmable type.
       *
       */
      Error SendReset(Message &aRequest, const Ip6::MessageInfo &aMessageInfo);
  
      /**
       * This method sends header-only CoAP response message.
       *
       * @param[in]  aCode           The CoAP code of this response.
       * @param[in]  aRequest        A reference to the CoAP Message that was used in CoAP request.
       * @param[in]  aMessageInfo    The message info corresponding to the CoAP request.
       *
       * @retval kErrorNone          Successfully enqueued the CoAP response message.
       * @retval kErrorNoBufs        Insufficient buffers available to send the CoAP response.
       * @retval kErrorInvalidArgs   The @p aRequest header is not of confirmable type.
       *
       */
      Error SendHeaderResponse(Message::Code aCode, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo);
  
      /**
       * This method sends a CoAP ACK empty message which is used in Separate Response for confirmable requests.
       *
       * @param[in]  aRequest        A reference to the CoAP Message that was used in CoAP request.
       * @param[in]  aMessageInfo    The message info corresponding to the CoAP request.
       *
       * @retval kErrorNone          Successfully enqueued the CoAP response message.
       * @retval kErrorNoBufs        Insufficient buffers available to send the CoAP response.
       * @retval kErrorInvalidArgs   The @p aRequest header is not of confirmable type.
       *
       */
      Error SendAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo);
  
      /**
       * This method sends a CoAP ACK message on which a dummy CoAP response is piggybacked.
       *
       * @param[in]  aRequest        A reference to the CoAP Message that was used in CoAP request.
       * @param[in]  aMessageInfo    The message info corresponding to the CoAP request.
       * @param[in]  aCode           The CoAP code of the dummy CoAP response.
       *
       * @retval kErrorNone          Successfully enqueued the CoAP response message.
       * @retval kErrorNoBufs        Insufficient buffers available to send the CoAP response.
       * @retval kErrorInvalidArgs   The @p aRequest header is not of confirmable type.
       *
       */
      Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Code aCode = kCodeChanged);
  
      /**
       * This method sends a header-only CoAP message to indicate no resource matched for the request.
       *
       * @param[in]  aRequest        A reference to the CoAP Message that was used in CoAP request.
       * @param[in]  aMessageInfo    The message info corresponding to the CoAP request.
       *
       * @retval kErrorNone          Successfully enqueued the CoAP response message.
       * @retval kErrorNoBufs        Insufficient buffers available to send the CoAP response.
       *
       */
      Error SendNotFound(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo);
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
      /**
       * This method sends a header-only CoAP message to indicate not all blocks have been sent or
       * were sent out of order.
       *
       * @param[in]  aRequest        A reference to the CoAP Message that was used in CoAP request.
       * @param[in]  aMessageInfo    The message info corresponding to the CoAP request.
       *
       * @retval kErrorNone          Successfully enqueued the CoAP response message.
       * @retval kErrorNoBufs        Insufficient buffers available to send the CoAP response.
       *
       */
      Error SendRequestEntityIncomplete(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
      {
          return SendHeaderResponse(kCodeRequestIncomplete, aRequest, aMessageInfo);
      }
  #endif
  
      /**
       * This method aborts CoAP transactions associated with given handler and context.
       *
       * The associated response handler will be called with kErrorAbort.
       *
       * @param[in]  aHandler  A function pointer that should be called when the transaction ends.
       * @param[in]  aContext  A pointer to arbitrary context information.
       *
       * @retval kErrorNone      Successfully aborted CoAP transactions.
       * @retval kErrorNotFound  CoAP transaction associated with given handler was not found.
       *
       */
      Error AbortTransaction(ResponseHandler aHandler, void *aContext);
  
      /**
       * This method sets interceptor to be called before processing a CoAP packet.
       *
       * @param[in]   aInterceptor    A pointer to the interceptor.
       * @param[in]   aContext        A pointer to arbitrary context information.
       *
       */
      void SetInterceptor(Interceptor aInterceptor, void *aContext);
  
      /**
       * This method returns a reference to the request message list.
       *
       * @returns A reference to the request message list.
       *
       */
      const MessageQueue &GetRequestMessages(void) const { return mPendingRequests; }
  
      /**
       * This method returns a reference to the cached response list.
       *
       * @returns A reference to the cached response list.
       *
       */
      const MessageQueue &GetCachedResponses(void) const { return mResponsesQueue.GetResponses(); }
  
  protected:
      /**
       * This function pointer is called to send a CoAP message.
       *
       * @param[in]  aCoapBase     A reference to the CoAP agent.
       * @param[in]  aMessage      A reference to the message to send.
       * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
       *
       * @retval kErrorNone    Successfully sent CoAP message.
       * @retval kErrorNoBufs  Failed to allocate retransmission data.
       *
       */
      typedef Error (*Sender)(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  
      /**
       * This constructor initializes the object.
       *
       * @param[in]  aInstance        A reference to the OpenThread instance.
       * @param[in]  aSender          A function pointer to send CoAP message, which SHOULD be a static
       *                              member method of a descendant of this class.
       *
       */
      CoapBase(Instance &aInstance, Sender aSender);
  
      /**
       * This method receives a CoAP message.
       *
       * @param[in]  aMessage      A reference to the received message.
       * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
       *
       */
      void Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  
  private:
      struct Metadata
      {
          Error AppendTo(Message &aMessage) const { return aMessage.Append(*this); }
          void  ReadFrom(const Message &aMessage);
          void  UpdateIn(Message &aMessage) const;
  
          Ip6::Address    mSourceAddress;            // IPv6 address of the message source.
          Ip6::Address    mDestinationAddress;       // IPv6 address of the message destination.
          uint16_t        mDestinationPort;          // UDP port of the message destination.
          ResponseHandler mResponseHandler;          // A function pointer that is called on response reception.
          void *          mResponseContext;          // A pointer to arbitrary context information.
          TimeMilli       mNextTimerShot;            // Time when the timer should shoot for this message.
          uint32_t        mRetransmissionTimeout;    // Delay that is applied to next retransmission.
          uint8_t         mRetransmissionsRemaining; // Number of retransmissions remaining.
  #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
          uint8_t mHopLimit; // The hop limit.
  #endif
          bool mAcknowledged : 1;  // Information that request was acknowledged.
          bool mConfirmable : 1;   // Information that message is confirmable.
          bool mMulticastLoop : 1; // Information that multicast loop is enabled.
  #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
          bool mIsHostInterface : 1; // TRUE if packets sent/received via host interface, FALSE otherwise.
  #endif
  #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
          bool mObserve : 1; // Information that this request involves Observations.
  #endif
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
          otCoapBlockwiseReceiveHook  mBlockwiseReceiveHook;  // Function pointer called on Block2 response reception.
          otCoapBlockwiseTransmitHook mBlockwiseTransmitHook; // Function pointer called on Block1 response reception.
  #endif
      };
  
      static void HandleRetransmissionTimer(Timer &aTimer);
      void        HandleRetransmissionTimer(void);
  
      void     ClearRequests(const Ip6::Address *aAddress);
      Message *CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata);
      void     DequeueMessage(Message &aMessage);
      Message *FindRelatedRequest(const Message &aResponse, const Ip6::MessageInfo &aMessageInfo, Metadata &aMetadata);
      void     FinalizeCoapTransaction(Message &               aRequest,
                                       const Metadata &        aMetadata,
                                       Message *               aResponse,
                                       const Ip6::MessageInfo *aMessageInfo,
                                       Error                   aResult);
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
      void  FreeLastBlockResponse(void);
      Error CacheLastBlockResponse(Message *aResponse);
  
      Error PrepareNextBlockRequest(Message::BlockType aType,
                                    bool               aMoreBlocks,
                                    Message &          aRequestOld,
                                    Message &          aRequest,
                                    Message &          aMessage);
      Error ProcessBlock1Request(Message &                aMessage,
                                 const Ip6::MessageInfo & aMessageInfo,
                                 const ResourceBlockWise &aResource,
                                 uint32_t                 aTotalLength);
      Error ProcessBlock2Request(Message &                aMessage,
                                 const Ip6::MessageInfo & aMessageInfo,
                                 const ResourceBlockWise &aResource);
  #endif
      void ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
      void ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
      Error SendNextBlock1Request(Message &               aRequest,
                                  Message &               aMessage,
                                  const Ip6::MessageInfo &aMessageInfo,
                                  const Metadata &        aCoapMetadata);
      Error SendNextBlock2Request(Message &               aRequest,
                                  Message &               aMessage,
                                  const Ip6::MessageInfo &aMessageInfo,
                                  const Metadata &        aCoapMetadata,
                                  uint32_t                aTotalLength,
                                  bool                    aBeginBlock1Transfer);
  #endif
      void  SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
      Error SendEmptyMessage(Type aType, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo);
  
      Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  
      MessageQueue      mPendingRequests;
      uint16_t          mMessageId;
      TimerMilliContext mRetransmissionTimer;
  
      LinkedList<Resource> mResources;
  
      void *         mContext;
      Interceptor    mInterceptor;
      ResponsesQueue mResponsesQueue;
  
      RequestHandler mDefaultHandler;
      void *         mDefaultHandlerContext;
  
      const Sender mSender;
  
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
      LinkedList<ResourceBlockWise> mBlockWiseResources;
      Message *                     mLastResponse;
  #endif
  };
  
  /**
   * This class implements the CoAP client and server.
   *
   */
  class Coap : public CoapBase
  {
  public:
      /**
       * This constructor initializes the object.
       *
       * @param[in] aInstance      A reference to the OpenThread instance.
       *
       */
      explicit Coap(Instance &aInstance);
  
      /**
       * This method starts the CoAP service.
       *
       * @param[in]  aPort             The local UDP port to bind to.
       * @param[in]  aNetifIdentifier  The network interface identifier to bind.
       *
       * @retval kErrorNone    Successfully started the CoAP service.
       * @retval kErrorFailed  Failed to start CoAP agent.
       *
       */
      Error Start(uint16_t aPort, otNetifIdentifier aNetifIdentifier = OT_NETIF_UNSPECIFIED);
  
      /**
       * This method stops the CoAP service.
       *
       * @retval kErrorNone    Successfully stopped the CoAP service.
       * @retval kErrorFailed  Failed to stop CoAP agent.
       *
       */
      Error Stop(void);
  
  protected:
      Ip6::Udp::Socket mSocket;
  
  private:
      static Error Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
      static void  HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
      Error        Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  };
  
  } // namespace Coap
  
  DefineCoreType(otCoapTxParameters, Coap::TxParameters);
  DefineCoreType(otCoapResource, Coap::Resource);
  #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
  DefineCoreType(otCoapBlockwiseResource, Coap::ResourceBlockWise);
  #endif
  
  } // namespace ot
  
  #endif // COAP_HPP_
  