bmcweb: Set Retry Policy Valid Response Codes

Allows individual retry policies to specify what HTTP response codes
are considered valid.  Sets functions for the EventService and
Redfish Aggregation retry policies.  Those functions expect a
response code and return an error code based on what the response
code is.

This change is needed because EventService only considers 2XX codes
to be valid.  Any code outside of that range would trigger a retry
attempt.  Redfish Aggregation by design will need to return
errors outside of that range such as 404.  It should not retry to
send a message when it receives a 404 from a satellite BMC.

Right now 404 is the only error code that is handled differently
between the services.  Going forward, Redfish Aggregation will
likely want to allow other error codes as its functionality is
expanded.

Tested:
Used Redfish-Event-Listener with ssh port forwarding to create 3
subscriptions.  I then closed the ssh connection and sent a test
event.  Bmcweb made 3 retry attempts for each subscription.  At
that point the max retry amount (as defined by EventService) was
reached and bmcweb stop attempting to resend the messages.

There were no errors when the Redfish-Event-Listener was correctly
connected.  Test events resulted in messages being sent for each
subscription.

Signed-off-by: Carson Labrado <clabrado@google.com>
Change-Id: Ifdfaf638d28982ed18998f3ca05280a288e0020a
diff --git a/http/http_client.hpp b/http/http_client.hpp
index d35ddd4..a28c1c4 100644
--- a/http/http_client.hpp
+++ b/http/http_client.hpp
@@ -60,6 +60,21 @@
     retry
 };
 
+static inline boost::system::error_code
+    defaultRetryHandler(unsigned int respCode)
+{
+    // As a default, assume 200X is alright
+    BMCWEB_LOG_DEBUG << "Using default check for response code validity";
+    if ((respCode < 200) || (respCode >= 300))
+    {
+        return boost::system::errc::make_error_code(
+            boost::system::errc::result_out_of_range);
+    }
+
+    // Return 0 if the response code is valid
+    return boost::system::errc::make_error_code(boost::system::errc::success);
+};
+
 // We need to allow retry information to be set before a message has been sent
 // and a connection pool has been created
 struct RetryPolicyData
@@ -67,7 +82,8 @@
     uint32_t maxRetryAttempts = 5;
     std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
     std::string retryPolicyAction = "TerminateAfterRetries";
-    std::string name;
+    std::function<boost::system::error_code(unsigned int respCode)>
+        invalidResp = defaultRetryHandler;
 };
 
 struct PendingRequest
@@ -231,8 +247,9 @@
             BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
                              << respCode;
 
-            // 2XX response is considered to be successful
-            if ((respCode < 200) || (respCode >= 300))
+            // Make sure the received response code is valid as defined by
+            // the associated retry policy
+            if (self->retryPolicy.invalidResp(respCode))
             {
                 // The listener failed to receive the Sent-Event
                 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
@@ -414,9 +431,7 @@
 
         BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
                          << ":" << std::to_string(conn.port)
-                         << ", id: " << std::to_string(conn.connId)
-                         << ", retry policy is \"" << conn.retryPolicy.name
-                         << "\"";
+                         << ", id: " << std::to_string(conn.connId);
 
         // We can remove the request from the queue at this point
         requestQueue.pop_front();
@@ -427,9 +442,7 @@
                                    const RetryPolicyData& retryPolicy)
     {
         BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
-                         << ", id: " << std::to_string(conn.connId)
-                         << " using retry policy \"" << retryPolicy.name
-                         << "\"";
+                         << ", id: " << std::to_string(conn.connId);
 
         conn.retryPolicy = retryPolicy;
     }
@@ -678,7 +691,6 @@
         {
             BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
                              << "\" with default values";
-            policy.first->second.name = retryPolicyName;
         }
 
         // Send the data using either the existing connection pool or the newly
@@ -687,9 +699,11 @@
                                        policy.first->second, resHandler);
     }
 
-    void setRetryConfig(const uint32_t retryAttempts,
-                        const uint32_t retryTimeoutInterval,
-                        const std::string& retryPolicyName)
+    void setRetryConfig(
+        const uint32_t retryAttempts, const uint32_t retryTimeoutInterval,
+        const std::function<boost::system::error_code(unsigned int respCode)>&
+            invalidResp,
+        const std::string& retryPolicyName)
     {
         // We need to create the retry policy if one does not already exist for
         // the given retryPolicyName
@@ -698,7 +712,6 @@
         {
             BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
                              << retryPolicyName << "\"";
-            result.first->second.name = retryPolicyName;
         }
         else
         {
@@ -709,6 +722,7 @@
         result.first->second.maxRetryAttempts = retryAttempts;
         result.first->second.retryIntervalSecs =
             std::chrono::seconds(retryTimeoutInterval);
+        result.first->second.invalidResp = invalidResp;
     }
 
     void setRetryPolicy(const std::string& retryPolicy,
@@ -721,7 +735,6 @@
         {
             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
                              << retryPolicyName << "\"";
-            result.first->second.name = retryPolicyName;
         }
         else
         {