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