summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--http/http_client.hpp590
-rw-r--r--redfish-core/include/event_service_manager.hpp55
2 files changed, 457 insertions, 188 deletions
diff --git a/http/http_client.hpp b/http/http_client.hpp
index 342ed1b9df..16378232cb 100644
--- a/http/http_client.hpp
+++ b/http/http_client.hpp
@@ -21,7 +21,7 @@
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/version.hpp>
-#include <boost/circular_buffer.hpp>
+#include <boost/container/devector.hpp>
#include <include/async_resolve.hpp>
#include <cstdlib>
@@ -34,8 +34,10 @@
namespace crow
{
-static constexpr uint8_t maxRequestQueueSize = 50;
-static constexpr unsigned int httpReadBodyLimit = 8192;
+// It is assumed that the BMC should be able to handle 4 parallel connections
+constexpr uint8_t maxPoolSize = 4;
+constexpr uint8_t maxRequestQueueSize = 50;
+constexpr unsigned int httpReadBodyLimit = 8192;
enum class ConnState
{
@@ -58,35 +60,66 @@ enum class ConnState
retry
};
-class HttpClient : public std::enable_shared_from_this<HttpClient>
+// We need to allow retry information to be set before a message has been sent
+// and a connection pool has been created
+struct RetryPolicyData
+{
+ uint32_t maxRetryAttempts = 5;
+ std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
+ std::string retryPolicyAction = "TerminateAfterRetries";
+ std::string name;
+};
+
+struct PendingRequest
+{
+ std::string requestData;
+ std::function<void(bool, uint32_t)> callback;
+ RetryPolicyData retryPolicy;
+ PendingRequest(const std::string& requestData,
+ const std::function<void(bool, uint32_t)>& callback,
+ const RetryPolicyData& retryPolicy) :
+ requestData(requestData),
+ callback(callback), retryPolicy(retryPolicy)
+ {}
+};
+
+class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
{
private:
- crow::async_resolve::Resolver resolver;
- boost::beast::tcp_stream conn;
- boost::asio::steady_timer timer;
- boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
+ ConnState state = ConnState::initialized;
+ uint32_t retryCount = 0;
+ bool runningTimer = false;
+ std::string subId;
+ std::string host;
+ uint16_t port;
+ uint32_t connId;
+
+ // Retry policy information
+ // This should be updated before each message is sent
+ RetryPolicyData retryPolicy;
+
+ // Data buffers
+ std::string data;
boost::beast::http::request<boost::beast::http::string_body> req;
std::optional<
boost::beast::http::response_parser<boost::beast::http::string_body>>
parser;
- boost::circular_buffer_space_optimized<std::string> requestDataQueue{
- maxRequestQueueSize};
+ boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
- ConnState state = ConnState::initialized;
+ // Ascync callables
+ std::function<void(bool, uint32_t)> callback;
+ crow::async_resolve::Resolver resolver;
+ boost::beast::tcp_stream conn;
+ boost::asio::steady_timer timer;
- std::string subId;
- std::string host;
- uint16_t port = 0;
- uint32_t retryCount = 0;
- uint32_t maxRetryAttempts = 5;
- uint32_t retryIntervalSecs = 0;
- std::string retryPolicyAction = "TerminateAfterRetries";
- bool runningTimer = false;
+ friend class ConnectionPool;
void doResolve()
{
state = ConnState::resolveInProgress;
- BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":" << port;
+ BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
+ << std::to_string(port)
+ << ", id: " << std::to_string(connId);
auto respHandler =
[self(shared_from_this())](
@@ -97,12 +130,15 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
{
BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
self->state = ConnState::resolveFailed;
- self->handleConnState();
+ self->waitAndRetry();
return;
}
- BMCWEB_LOG_DEBUG << "Resolved";
+ BMCWEB_LOG_DEBUG << "Resolved " << self->host << ":"
+ << std::to_string(self->port)
+ << ", id: " << std::to_string(self->connId);
self->doConnect(endpointList);
};
+
resolver.asyncResolve(host, port, std::move(respHandler));
}
@@ -111,7 +147,9 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
{
state = ConnState::connectInProgress;
- BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port;
+ BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
+ << std::to_string(port)
+ << ", id: " << std::to_string(connId);
conn.expires_after(std::chrono::seconds(30));
conn.async_connect(
@@ -121,20 +159,24 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
if (ec)
{
BMCWEB_LOG_ERROR << "Connect "
- << endpoint.address().to_string()
+ << endpoint.address().to_string() << ":"
+ << std::to_string(endpoint.port())
+ << ", id: " << std::to_string(self->connId)
<< " failed: " << ec.message();
self->state = ConnState::connectFailed;
- self->handleConnState();
+ self->waitAndRetry();
return;
}
- BMCWEB_LOG_DEBUG << "Connected to: "
- << endpoint.address().to_string();
+ BMCWEB_LOG_DEBUG
+ << "Connected to: " << endpoint.address().to_string() << ":"
+ << std::to_string(endpoint.port())
+ << ", id: " << std::to_string(self->connId);
self->state = ConnState::connected;
- self->handleConnState();
+ self->sendMessage();
});
}
- void sendMessage(const std::string& data)
+ void sendMessage()
{
state = ConnState::sendInProgress;
@@ -154,7 +196,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
BMCWEB_LOG_ERROR << "sendMessage() failed: "
<< ec.message();
self->state = ConnState::sendFailed;
- self->handleConnState();
+ self->waitAndRetry();
return;
}
BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
@@ -182,7 +224,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
BMCWEB_LOG_ERROR << "recvMessage() failed: "
<< ec.message();
self->state = ConnState::recvFailed;
- self->handleConnState();
+ self->waitAndRetry();
return;
}
BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
@@ -203,80 +245,44 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
"receive Sent-Event. Header Response Code: "
<< respCode;
self->state = ConnState::recvFailed;
- self->handleConnState();
+ self->waitAndRetry();
return;
}
- // Send is successful, Lets remove data from queue
- // check for next request data in queue.
- if (!self->requestDataQueue.empty())
- {
- self->requestDataQueue.pop_front();
- }
- self->state = ConnState::idle;
+ // Send is successful
+ // Reset the counter just in case this was after retrying
+ self->retryCount = 0;
// Keep the connection alive if server supports it
// Else close the connection
BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
<< self->parser->keep_alive();
- if (!self->parser->keep_alive())
- {
- // Abort the connection since server is not keep-alive
- // enabled
- self->state = ConnState::abortConnection;
- }
- self->handleConnState();
+ self->callback(self->parser->keep_alive(), self->connId);
});
}
- void doClose()
- {
- state = ConnState::closeInProgress;
- boost::beast::error_code ec;
- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
- conn.close();
-
- // not_connected happens sometimes so don't bother reporting it.
- if (ec && ec != boost::beast::errc::not_connected)
- {
- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message();
- return;
- }
- BMCWEB_LOG_DEBUG << "Connection closed gracefully";
- if ((state != ConnState::suspended) && (state != ConnState::terminated))
- {
- state = ConnState::closed;
- handleConnState();
- }
- }
-
void waitAndRetry()
{
- if (retryCount >= maxRetryAttempts)
+ if (retryCount >= retryPolicy.maxRetryAttempts)
{
BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
-
- // Clear queue.
- while (!requestDataQueue.empty())
- {
- requestDataQueue.pop_front();
- }
-
- BMCWEB_LOG_DEBUG << "Retry policy: " << retryPolicyAction;
- if (retryPolicyAction == "TerminateAfterRetries")
+ BMCWEB_LOG_DEBUG << "Retry policy: "
+ << retryPolicy.retryPolicyAction;
+ if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
{
// TODO: delete subscription
state = ConnState::terminated;
+ callback(false, connId);
}
- if (retryPolicyAction == "SuspendRetries")
+ if (retryPolicy.retryPolicyAction == "SuspendRetries")
{
state = ConnState::suspended;
+ callback(false, connId);
}
// Reset the retrycount to zero so that client can try connecting
// again if needed
retryCount = 0;
- handleConnState();
return;
}
@@ -289,11 +295,13 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
retryCount++;
- BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs
+ BMCWEB_LOG_DEBUG << "Attempt retry after "
+ << std::to_string(
+ retryPolicy.retryIntervalSecs.count())
<< " seconds. RetryCount = " << retryCount;
- timer.expires_after(std::chrono::seconds(retryIntervalSecs));
+ timer.expires_after(retryPolicy.retryIntervalSecs);
timer.async_wait(
- [self = shared_from_this()](const boost::system::error_code ec) {
+ [self(shared_from_this())](const boost::system::error_code ec) {
if (ec == boost::asio::error::operation_aborted)
{
BMCWEB_LOG_DEBUG
@@ -308,119 +316,383 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
}
self->runningTimer = false;
- // Lets close connection and start from resolve.
- self->doClose();
+ // Let's close the connection and restart from resolve.
+ self->doCloseAndRetry();
});
}
- void handleConnState()
+ void doClose()
{
- switch (state)
+ state = ConnState::closeInProgress;
+ boost::beast::error_code ec;
+ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+ conn.close();
+
+ // not_connected happens sometimes so don't bother reporting it.
+ if (ec && ec != boost::beast::errc::not_connected)
{
- case ConnState::resolveInProgress:
- case ConnState::connectInProgress:
- case ConnState::sendInProgress:
- case ConnState::recvInProgress:
- case ConnState::closeInProgress:
- {
- BMCWEB_LOG_DEBUG << "Async operation is already in progress";
- break;
- }
- case ConnState::initialized:
- case ConnState::closed:
+ BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
+ << ", id: " << std::to_string(connId)
+ << "shutdown failed: " << ec.message();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
+ << ", id: " << std::to_string(connId)
+ << " closed gracefully";
+ if ((state != ConnState::suspended) && (state != ConnState::terminated))
+ {
+ state = ConnState::closed;
+ }
+ }
+
+ void doCloseAndRetry()
+ {
+ state = ConnState::closeInProgress;
+ boost::beast::error_code ec;
+ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+ conn.close();
+
+ // not_connected happens sometimes so don't bother reporting it.
+ if (ec && ec != boost::beast::errc::not_connected)
+ {
+ BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
+ << ", id: " << std::to_string(connId)
+ << "shutdown failed: " << ec.message();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
+ << ", id: " << std::to_string(connId)
+ << " closed gracefully";
+ if ((state != ConnState::suspended) && (state != ConnState::terminated))
+ {
+ // Now let's try to resend the data
+ state = ConnState::retry;
+ this->doResolve();
+ }
+ }
+
+ public:
+ explicit ConnectionInfo(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const uint16_t destPort,
+ const std::string& destUri,
+ const boost::beast::http::fields& httpHeader,
+ const unsigned int connId) :
+ subId(id),
+ host(destIP), port(destPort), connId(connId),
+ req(boost::beast::http::verb::post, destUri, 11, "", httpHeader),
+ conn(ioc), timer(ioc)
+ {
+ req.set(boost::beast::http::field::host, host);
+ req.keep_alive(true);
+ }
+};
+
+class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
+{
+ private:
+ boost::asio::io_context& ioc;
+ const std::string id;
+ const std::string destIP;
+ const uint16_t destPort;
+ const std::string destUri;
+ const boost::beast::http::fields httpHeader;
+ std::vector<std::shared_ptr<ConnectionInfo>> connections;
+ boost::container::devector<PendingRequest> requestQueue;
+
+ friend class HttpClient;
+
+ // Configure a connections's data, callback, and retry info in preparation
+ // to begin sending a request
+ void setConnProps(ConnectionInfo& conn)
+ {
+ if (requestQueue.empty())
+ {
+ BMCWEB_LOG_ERROR
+ << "setConnProps() should not have been called when requestQueue is empty";
+ return;
+ }
+
+ auto req = requestQueue.front();
+ conn.retryPolicy = std::move(req.retryPolicy);
+ conn.data = std::move(req.requestData);
+ conn.callback = std::move(req.callback);
+
+ 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
+ << "\"";
+
+ // We can remove the request from the queue at this point
+ requestQueue.pop_front();
+ }
+
+ // Configures a connection to use the specific retry policy.
+ inline void setConnRetryPolicy(ConnectionInfo& conn,
+ const RetryPolicyData& retryPolicy)
+ {
+ BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
+ << ", id: " << std::to_string(conn.connId)
+ << " using retry policy \"" << retryPolicy.name
+ << "\"";
+
+ conn.retryPolicy = retryPolicy;
+ }
+
+ // Gets called as part of callback after request is sent
+ // Reuses the connection if there are any requests waiting to be sent
+ // Otherwise closes the connection if it is not a keep-alive
+ void sendNext(bool keepAlive, uint32_t connId)
+ {
+ auto conn = connections[connId];
+ // Reuse the connection to send the next request in the queue
+ if (!requestQueue.empty())
+ {
+ BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
+ << " requests remaining in queue for " << destIP
+ << ":" << std::to_string(destPort)
+ << ", reusing connnection "
+ << std::to_string(connId);
+
+ setConnProps(*conn);
+
+ if (keepAlive)
{
- if (requestDataQueue.empty())
- {
- BMCWEB_LOG_DEBUG << "requestDataQueue is empty";
- return;
- }
- doResolve();
- break;
+ conn->sendMessage();
}
- case ConnState::suspended:
- case ConnState::terminated:
+ else
{
- doClose();
- break;
+ // Server is not keep-alive enabled so we need to close the
+ // connection and then start over from resolve
+ conn->doClose();
+ conn->doResolve();
}
- case ConnState::resolveFailed:
- case ConnState::connectFailed:
- case ConnState::sendFailed:
- case ConnState::recvFailed:
- case ConnState::retry:
+ return;
+ }
+
+ // No more messages to send so close the connection if necessary
+ if (keepAlive)
+ {
+ conn->state = ConnState::idle;
+ }
+ else
+ {
+ // Abort the connection since server is not keep-alive enabled
+ conn->state = ConnState::abortConnection;
+ conn->doClose();
+ }
+ }
+
+ void sendData(std::string& data, const RetryPolicyData& retryPolicy)
+ {
+ std::weak_ptr<ConnectionPool> weakSelf = weak_from_this();
+
+ // Callback to be called once the request has been sent
+ auto cb = [weakSelf](bool keepAlive, uint32_t connId) {
+ // If requests remain in the queue then we want to reuse this
+ // connection to send the next request
+ std::shared_ptr<ConnectionPool> self = weakSelf.lock();
+ if (!self)
{
- // In case of failures during connect and handshake
- // the retry policy will be applied
- waitAndRetry();
- break;
+ BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
+ return;
}
- case ConnState::connected:
- case ConnState::idle:
+
+ self->sendNext(keepAlive, connId);
+ };
+
+ // Reuse an existing connection if one is available
+ for (unsigned int i = 0; i < connections.size(); i++)
+ {
+ auto conn = connections[i];
+ if ((conn->state == ConnState::idle) ||
+ (conn->state == ConnState::initialized) ||
+ (conn->state == ConnState::closed))
{
- // State idle means, previous attempt is successful
- // State connected means, client connection is established
- // successfully
- if (requestDataQueue.empty())
+ conn->data = std::move(data);
+ conn->callback = std::move(cb);
+ conn->retryPolicy = retryPolicy;
+ setConnRetryPolicy(*conn, retryPolicy);
+ std::string commonMsg = std::to_string(i) + " from pool " +
+ destIP + ":" + std::to_string(destPort);
+
+ if (conn->state == ConnState::idle)
{
- BMCWEB_LOG_DEBUG << "requestDataQueue is empty";
- return;
+ BMCWEB_LOG_DEBUG << "Grabbing idle connection "
+ << commonMsg;
+ conn->sendMessage();
}
- std::string data = requestDataQueue.front();
- sendMessage(data);
- break;
- }
- case ConnState::abortConnection:
- {
- // Server did not want to keep alive the session
- doClose();
- break;
+ else
+ {
+ BMCWEB_LOG_DEBUG << "Reusing existing connection "
+ << commonMsg;
+ conn->doResolve();
+ }
+ return;
}
}
+
+ // All connections in use so create a new connection or add request to
+ // the queue
+ if (connections.size() < maxPoolSize)
+ {
+ BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
+ << ":" << std::to_string(destPort);
+ auto conn = addConnection();
+ conn->data = std::move(data);
+ conn->callback = std::move(cb);
+ setConnRetryPolicy(*conn, retryPolicy);
+ conn->doResolve();
+ }
+ else if (requestQueue.size() < maxRequestQueueSize)
+ {
+ BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
+ requestQueue.emplace_back(std::move(data), std::move(cb),
+ retryPolicy);
+ }
+ else
+ {
+ BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
+ << " request queue full. Dropping request.";
+ }
+ }
+
+ std::shared_ptr<ConnectionInfo>& addConnection()
+ {
+ unsigned int newId = static_cast<unsigned int>(connections.size());
+
+ auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
+ ioc, id, destIP, destPort, destUri, httpHeader, newId));
+
+ BMCWEB_LOG_DEBUG << "Added connection "
+ << std::to_string(connections.size() - 1)
+ << " to pool " << destIP << ":"
+ << std::to_string(destPort);
+
+ return ret;
}
public:
- explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
- const std::string& destIP, uint16_t destPort,
- const std::string& destUri,
- const boost::beast::http::fields& httpHeader) :
- conn(ioc),
- timer(ioc),
- req(boost::beast::http::verb::post, destUri, 11, "", httpHeader),
- subId(id), host(destIP), port(destPort)
+ explicit ConnectionPool(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const uint16_t destPort,
+ const std::string& destUri,
+ const boost::beast::http::fields& httpHeader) :
+ ioc(ioc),
+ id(id), destIP(destIP), destPort(destPort), destUri(destUri),
+ httpHeader(httpHeader)
{
- req.set(boost::beast::http::field::host, host);
- req.keep_alive(true);
+ std::string clientKey = destIP + ":" + std::to_string(destPort);
+ BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
+ << std::to_string(destPort);
+
+ // Initialize the pool with a single connection
+ addConnection();
+ }
+};
+
+class HttpClient
+{
+ private:
+ std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
+ connectionPools;
+ boost::asio::io_context& ioc =
+ crow::connections::systemBus->get_io_context();
+ std::unordered_map<std::string, RetryPolicyData> retryInfo;
+ HttpClient() = default;
+
+ public:
+ HttpClient(const HttpClient&) = delete;
+ HttpClient& operator=(const HttpClient&) = delete;
+ HttpClient(HttpClient&&) = delete;
+ HttpClient& operator=(HttpClient&&) = delete;
+ ~HttpClient() = default;
+
+ static HttpClient& getInstance()
+ {
+ static HttpClient handler;
+ return handler;
}
- void sendData(const std::string& data)
+ void sendData(std::string& data, const std::string& id,
+ const std::string& destIP, const uint16_t destPort,
+ const std::string& destUri,
+ const boost::beast::http::fields& httpHeader,
+ std::string& retryPolicyName)
{
- if ((state == ConnState::suspended) || (state == ConnState::terminated))
+ std::string clientKey = destIP + ":" + std::to_string(destPort);
+ // Use nullptr to avoid creating a ConnectionPool each time
+ auto result = connectionPools.try_emplace(clientKey, nullptr);
+ if (result.second)
{
- return;
+ // Now actually create the ConnectionPool shared_ptr since it does
+ // not already exist
+ result.first->second = std::make_shared<ConnectionPool>(
+ ioc, id, destIP, destPort, destUri, httpHeader);
+ BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
}
-
- if (requestDataQueue.size() <= maxRequestQueueSize)
+ else
{
- requestDataQueue.push_back(data);
- handleConnState();
+ BMCWEB_LOG_DEBUG << "Using existing connection pool for "
+ << clientKey;
}
- else
+
+ // Get the associated retry policy
+ auto policy = retryInfo.try_emplace(retryPolicyName);
+ if (policy.second)
{
- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data.";
+ 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
+ // created connection pool
+ result.first->second->sendData(data, policy.first->second);
}
void setRetryConfig(const uint32_t retryAttempts,
- const uint32_t retryTimeoutInterval)
+ const uint32_t retryTimeoutInterval,
+ const std::string& retryPolicyName)
{
- maxRetryAttempts = retryAttempts;
- retryIntervalSecs = retryTimeoutInterval;
+ // We need to create the retry policy if one does not already exist for
+ // the given retryPolicyName
+ auto result = retryInfo.try_emplace(retryPolicyName);
+ if (result.second)
+ {
+ BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
+ << retryPolicyName << "\"";
+ result.first->second.name = retryPolicyName;
+ }
+ else
+ {
+ BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
+ << retryPolicyName << "\"";
+ }
+
+ result.first->second.maxRetryAttempts = retryAttempts;
+ result.first->second.retryIntervalSecs =
+ std::chrono::seconds(retryTimeoutInterval);
}
- void setRetryPolicy(const std::string& retryPolicy)
+ void setRetryPolicy(const std::string& retryPolicy,
+ const std::string& retryPolicyName)
{
- retryPolicyAction = retryPolicy;
+ // We need to create the retry policy if one does not already exist for
+ // the given retryPolicyName
+ auto result = retryInfo.try_emplace(retryPolicyName);
+ if (result.second)
+ {
+ BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
+ << retryPolicyName << "\"";
+ result.first->second.name = retryPolicyName;
+ }
+ else
+ {
+ BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
+ << retryPolicyName << "\"";
+ }
+
+ result.first->second.retryPolicyAction = retryPolicy;
}
};
-
} // namespace crow
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
index bc5af85060..58bf25786f 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -381,7 +381,7 @@ class Subscription : public persistent_data::UserSubscription
~Subscription() = default;
- bool sendEvent(const std::string& msg)
+ bool sendEvent(std::string& msg)
{
persistent_data::EventServiceConfig eventServiceConfig =
persistent_data::EventServiceStore::getInstance()
@@ -391,15 +391,9 @@ class Subscription : public persistent_data::UserSubscription
return false;
}
- if (conn == nullptr)
- {
- // create the HttpClient connection
- conn = std::make_shared<crow::HttpClient>(
- crow::connections::systemBus->get_io_context(), id, host, port,
- path, httpHeaders);
- }
-
- conn->sendData(msg);
+ // A connection pool will be created if one does not already exist
+ crow::HttpClient::getInstance().sendData(msg, id, host, port, path,
+ httpHeaders, retryPolicyName);
eventSeqNum++;
if (sseConn != nullptr)
@@ -430,8 +424,9 @@ class Subscription : public persistent_data::UserSubscription
{"Name", "Event Log"},
{"Events", logEntryArray}};
- return this->sendEvent(
- msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace));
+ std::string strMsg =
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
+ return this->sendEvent(strMsg);
}
#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
@@ -497,8 +492,9 @@ class Subscription : public persistent_data::UserSubscription
{"Name", "Event Log"},
{"Events", logEntryArray}};
- this->sendEvent(
- msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace));
+ std::string strMsg =
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
+ this->sendEvent(strMsg);
}
#endif
@@ -529,25 +525,22 @@ class Subscription : public persistent_data::UserSubscription
return;
}
- this->sendEvent(
- msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace));
+ std::string strMsg =
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
+ this->sendEvent(strMsg);
}
void updateRetryConfig(const uint32_t retryAttempts,
const uint32_t retryTimeoutInterval)
{
- if (conn != nullptr)
- {
- conn->setRetryConfig(retryAttempts, retryTimeoutInterval);
- }
+ crow::HttpClient::getInstance().setRetryConfig(
+ retryAttempts, retryTimeoutInterval, retryPolicyName);
}
void updateRetryPolicy()
{
- if (conn != nullptr)
- {
- conn->setRetryPolicy(retryPolicy);
- }
+ crow::HttpClient::getInstance().setRetryPolicy(retryPolicy,
+ retryPolicyName);
}
uint64_t getEventSeqNum() const
@@ -561,8 +554,8 @@ class Subscription : public persistent_data::UserSubscription
uint16_t port = 0;
std::string path;
std::string uriProto;
- std::shared_ptr<crow::HttpClient> conn = nullptr;
std::shared_ptr<crow::ServerSentEvents> sseConn = nullptr;
+ std::string retryPolicyName = "SubscriptionEvent";
};
class EventServiceManager
@@ -1040,8 +1033,10 @@ class EventServiceManager
{"Name", "Event Log"},
{"Id", eventId},
{"Events", eventRecord}};
- entry->sendEvent(msgJson.dump(
- 2, ' ', true, nlohmann::json::error_handler_t::replace));
+
+ std::string strMsg = msgJson.dump(
+ 2, ' ', true, nlohmann::json::error_handler_t::replace);
+ entry->sendEvent(strMsg);
eventId++; // increament the eventId
}
else
@@ -1060,8 +1055,10 @@ class EventServiceManager
{"OriginOfCondition", "/ibm/v1/HMC/BroadcastService"},
{"Name", "Broadcast Message"},
{"Message", broadcastMsg}};
- entry->sendEvent(msgJson.dump(
- 2, ' ', true, nlohmann::json::error_handler_t::replace));
+
+ std::string strMsg = msgJson.dump(
+ 2, ' ', true, nlohmann::json::error_handler_t::replace);
+ entry->sendEvent(strMsg);
}
}