requester: PLDM handler for async request/response

PLDM request handler provides APIs to register PLDM request message,
handle retries and instance ID expiration. Sending the PLDM request
and handling response is handled in an async manner. On receiving
the response the corresponding response handler registered for the
request is invoked.

Tested: Ran unit tests

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: I9f0a9dfcf0fbc9a84eefad375b92d40dd8b48d3d
diff --git a/requester/request.hpp b/requester/request.hpp
new file mode 100644
index 0000000..4097e10
--- /dev/null
+++ b/requester/request.hpp
@@ -0,0 +1,179 @@
+#pragma once
+
+#include "libpldm/base.h"
+#include "libpldm/requester/pldm.h"
+
+#include "common/types.hpp"
+
+#include <sdbusplus/timer.hpp>
+#include <sdeventplus/event.hpp>
+
+#include <chrono>
+#include <functional>
+#include <iostream>
+
+namespace pldm
+{
+
+namespace requester
+{
+
+using namespace std::chrono;
+
+/** @class RequestRetryTimer
+ *
+ *  The abstract base class for implementing the PLDM request retry logic. This
+ *  class handles number of times the PLDM request needs to be retried if the
+ *  response is not received and the time to wait between each retry. It
+ *  provides APIs to start and stop the request flow.
+ */
+class RequestRetryTimer
+{
+  public:
+    RequestRetryTimer() = delete;
+    RequestRetryTimer(const RequestRetryTimer&) = delete;
+    RequestRetryTimer(RequestRetryTimer&&) = default;
+    RequestRetryTimer& operator=(const RequestRetryTimer&) = delete;
+    RequestRetryTimer& operator=(RequestRetryTimer&&) = default;
+    virtual ~RequestRetryTimer() = default;
+
+    /** @brief Constructor
+     *
+     *  @param[in] event - reference to PLDM daemon's main event loop
+     *  @param[in] numRetries - number of request retries
+     *  @param[in] timeout - time to wait between each retry in milliseconds
+     */
+    explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries,
+                               milliseconds timeout) :
+
+        event(event),
+        numRetries(numRetries), timeout(timeout),
+        timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this))
+    {}
+
+    /** @brief Starts the request flow and arms the timer for request retries
+     *
+     *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
+     */
+    int start()
+    {
+        auto rc = send();
+        if (rc != PLDM_SUCCESS)
+        {
+            return rc;
+        }
+
+        try
+        {
+            if (numRetries)
+            {
+                timer.start(duration_cast<microseconds>(timeout), true);
+            }
+        }
+        catch (const std::runtime_error& e)
+        {
+            std::cerr << "Failed to start the request timer. RC = " << e.what()
+                      << "\n";
+            return PLDM_ERROR;
+        }
+
+        return PLDM_SUCCESS;
+    }
+
+    /** @brief Stops the timer and no further request retries happen */
+    void stop()
+    {
+        auto rc = timer.stop();
+        if (rc)
+        {
+            std::cerr << "Failed to stop the request timer. RC = " << rc
+                      << "\n";
+        }
+    }
+
+  protected:
+    sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
+    uint8_t numRetries;        //!< number of request retries
+    milliseconds timeout;  //!< time to wait between each retry in milliseconds
+    phosphor::Timer timer; //!< manages starting timers and handling timeouts
+
+    /** @brief Sends the PLDM request message
+     *
+     *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
+     */
+    virtual int send() const = 0;
+
+    /** @brief Callback function invoked when the timeout happens */
+    void callback()
+    {
+        if (numRetries--)
+        {
+            send();
+        }
+        else
+        {
+            stop();
+        }
+    }
+};
+
+/** @class Request
+ *
+ *  The concrete implementation of RequestIntf. This class implements the send()
+ *  to send the PLDM request message over MCTP socket.
+ *  This class encapsulates the PLDM request message, the number of times the
+ *  request needs to retried if the response is not received and the amount of
+ *  time to wait between each retry. It provides APIs to start and stop the
+ *  request flow.
+ */
+class Request final : public RequestRetryTimer
+{
+  public:
+    Request() = delete;
+    Request(const Request&) = delete;
+    Request(Request&&) = default;
+    Request& operator=(const Request&) = delete;
+    Request& operator=(Request&&) = default;
+    ~Request() = default;
+
+    /** @brief Constructor
+     *
+     *  @param[in] fd - fd of the MCTP communication socket
+     *  @param[in] eid - endpoint ID of the remote MCTP endpoint
+     *  @param[in] event - reference to PLDM daemon's main event loop
+     *  @param[in] requestMsg - PLDM request message
+     *  @param[in] numRetries - number of request retries
+     *  @param[in] timeout - time to wait between each retry in milliseconds
+     */
+    explicit Request(int fd, mctp_eid_t eid, sdeventplus::Event& event,
+                     pldm::Request&& requestMsg, uint8_t numRetries,
+                     milliseconds timeout) :
+        RequestRetryTimer(event, numRetries, timeout),
+        fd(fd), eid(eid), requestMsg(std::move(requestMsg))
+    {}
+
+  private:
+    int fd;         //!< file descriptor of MCTP communications socket
+    mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint
+    pldm::Request&& requestMsg; //!< PLDM request message
+
+    /** @brief Sends the PLDM request message on the socket
+     *
+     *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
+     */
+    int send() const
+    {
+        auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size());
+        if (rc < 0)
+        {
+            std::cerr << "Failed to send PLDM message. RC = " << rc
+                      << ", errno = " << errno << "\n";
+            return PLDM_ERROR;
+        }
+        return PLDM_SUCCESS;
+    }
+};
+
+} // namespace requester
+
+} // namespace pldm
\ No newline at end of file