EventService: Add retry configuration support

This commit is to pass configuration parameters: retry attempts,
retry interval secs and retry policy to http client and take
required delivery retry policy action.

Also, perform async wait for retryTimeoutInterval before each
retry attempts.

Tested:
- Set and verified config properties by sending PATCH req on
  EventService and EventDestination uri.
- Verified the appropriate delivery retry policy action block reached.
- Verified the async_wait logic by triggering retry case depending
  failed state of connection.
    - could see a wait for timeout interval before next retry.

Signed-off-by: Ayushi Smriti <smriti.ayushi@linux.intel.com>
Change-Id: Id1366fca59dc9e6543c553bfe5df95a59f468bc7
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/http/http_client.hpp b/http/http_client.hpp
index 8b1fb5c..58086dd 100644
--- a/http/http_client.hpp
+++ b/http/http_client.hpp
@@ -42,13 +42,15 @@
     recvFailed,
     idle,
     suspended,
-    closed
+    closed,
+    terminated
 };
 
 class HttpClient : public std::enable_shared_from_this<HttpClient>
 {
   private:
     boost::beast::tcp_stream conn;
+    boost::asio::steady_timer timer;
     boost::beast::flat_buffer buffer;
     boost::beast::http::request<boost::beast::http::string_body> req;
     boost::beast::http::response<boost::beast::http::string_body> res;
@@ -56,11 +58,15 @@
     std::vector<std::pair<std::string, std::string>> headers;
     std::queue<std::string> requestDataQueue;
     ConnState state;
+    std::string subId;
     std::string host;
     std::string port;
     std::string uri;
-    int retryCount;
-    int maxRetryAttempts;
+    uint32_t retryCount;
+    uint32_t maxRetryAttempts;
+    uint32_t retryIntervalSecs;
+    std::string retryPolicyAction;
+    bool runningTimer;
 
     void doConnect()
     {
@@ -208,10 +214,23 @@
                 requestDataQueue.pop();
             }
 
-            // TODO: Take 'DeliveryRetryPolicy' action.
-            // For now, doing 'SuspendRetries' action.
-            state = ConnState::suspended;
-            return;
+            BMCWEB_LOG_DEBUG << "Retry policy is set to " << retryPolicyAction;
+            if (retryPolicyAction == "TerminateAfterRetries")
+            {
+                // TODO: delete subscription
+                state = ConnState::terminated;
+                return;
+            }
+            else if (retryPolicyAction == "SuspendRetries")
+            {
+                state = ConnState::suspended;
+                return;
+            }
+            else
+            {
+                // keep retrying, reset count and continue.
+                retryCount = 0;
+            }
         }
 
         if ((state == ConnState::connectFailed) ||
@@ -226,20 +245,43 @@
                 return;
             }
 
+            if (runningTimer)
+            {
+                BMCWEB_LOG_DEBUG << "Retry timer is already running.";
+                return;
+            }
+            runningTimer = true;
+
             retryCount++;
-            // TODO: Perform async wait for retryTimeoutInterval before proceed.
+
+            BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs
+                             << " seconds. RetryCount = " << retryCount;
+            timer.expires_after(std::chrono::seconds(retryIntervalSecs));
+            timer.async_wait([self = shared_from_this()](
+                                 const boost::system::error_code& ec) {
+                self->runningTimer = false;
+                self->connStateCheck();
+            });
+            return;
         }
         else
         {
             // reset retry count.
             retryCount = 0;
         }
+        connStateCheck();
 
+        return;
+    }
+
+    void connStateCheck()
+    {
         switch (state)
         {
             case ConnState::connectInProgress:
             case ConnState::sendInProgress:
             case ConnState::suspended:
+            case ConnState::terminated:
                 // do nothing
                 break;
             case ConnState::initialized:
@@ -263,22 +305,20 @@
             default:
                 break;
         }
-
-        return;
     }
 
   public:
-    explicit HttpClient(boost::asio::io_context& ioc, const std::string& destIP,
-                        const std::string& destPort,
+    explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
+                        const std::string& destIP, const std::string& destPort,
                         const std::string& destUri) :
         conn(ioc),
-        host(destIP), port(destPort), uri(destUri)
+        timer(ioc), subId(id), host(destIP), port(destPort), uri(destUri),
+        retryCount(0), maxRetryAttempts(5),
+        retryPolicyAction("TerminateAfterRetries"), runningTimer(false)
     {
         boost::asio::ip::tcp::resolver resolver(ioc);
         endpoint = resolver.resolve(host, port);
         state = ConnState::initialized;
-        retryCount = 0;
-        maxRetryAttempts = 5;
     }
 
     void sendData(const std::string& data)
@@ -306,6 +346,18 @@
     {
         headers = httpHeaders;
     }
+
+    void setRetryConfig(const uint32_t retryAttempts,
+                        const uint32_t retryTimeoutInterval)
+    {
+        maxRetryAttempts = retryAttempts;
+        retryIntervalSecs = retryTimeoutInterval;
+    }
+
+    void setRetryPolicy(const std::string& retryPolicy)
+    {
+        retryPolicyAction = retryPolicy;
+    }
 };
 
 } // namespace crow
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
index c11e31b..7c96420 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -292,7 +292,8 @@
         host(inHost), port(inPort), path(inPath), uriProto(inUriProto)
     {
         conn = std::make_shared<crow::HttpClient>(
-            crow::connections::systemBus->get_io_context(), host, port, path);
+            crow::connections::systemBus->get_io_context(), id, host, port,
+            path);
     }
     ~Subscription()
     {}
@@ -444,6 +445,17 @@
         this->sendEvent(msg.dump());
     }
 
+    void updateRetryConfig(const uint32_t retryAttempts,
+                           const uint32_t retryTimeoutInterval)
+    {
+        conn->setRetryConfig(retryAttempts, retryTimeoutInterval);
+    }
+
+    void updateRetryPolicy()
+    {
+        conn->setRetryPolicy(retryPolicy);
+    }
+
   private:
     uint64_t eventSeqNum;
     std::string host;
@@ -677,6 +689,7 @@
     void setEventServiceConfig(const EventServiceConfig& cfg)
     {
         bool updateConfig = false;
+        bool updateRetryCfg = false;
 
         if (serviceEnabled != std::get<0>(cfg))
         {
@@ -696,18 +709,31 @@
         {
             retryAttempts = std::get<1>(cfg);
             updateConfig = true;
+            updateRetryCfg = true;
         }
 
         if (retryTimeoutInterval != std::get<2>(cfg))
         {
             retryTimeoutInterval = std::get<2>(cfg);
             updateConfig = true;
+            updateRetryCfg = true;
         }
 
         if (updateConfig)
         {
             updateSubscriptionData();
         }
+
+        if (updateRetryCfg)
+        {
+            // Update the changed retry config to all subscriptions
+            for (const auto& it :
+                 EventServiceManager::getInstance().subscriptionsMap)
+            {
+                std::shared_ptr<Subscription> entry = it.second;
+                entry->updateRetryConfig(retryAttempts, retryTimeoutInterval);
+            }
+        }
     }
 
     void updateNoOfSubscribersCount()
@@ -791,6 +817,10 @@
             cacheLastEventTimestamp();
         }
 #endif
+        // Update retry configuration.
+        subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
+        subValue->updateRetryPolicy();
+
         return id;
     }
 
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index b88cb43..ba1ea19 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -507,6 +507,7 @@
                 return;
             }
             subValue->retryPolicy = *retryPolicy;
+            subValue->updateRetryPolicy();
         }
 
         EventServiceManager::getInstance().updateSubscriptionData();