xref: /openbmc/bmcweb/http/http_client.hpp (revision 039a47e3)
1bd030d0aSAppaRao Puli /*
2bd030d0aSAppaRao Puli // Copyright (c) 2020 Intel Corporation
3bd030d0aSAppaRao Puli //
4bd030d0aSAppaRao Puli // Licensed under the Apache License, Version 2.0 (the "License");
5bd030d0aSAppaRao Puli // you may not use this file except in compliance with the License.
6bd030d0aSAppaRao Puli // You may obtain a copy of the License at
7bd030d0aSAppaRao Puli //
8bd030d0aSAppaRao Puli //      http://www.apache.org/licenses/LICENSE-2.0
9bd030d0aSAppaRao Puli //
10bd030d0aSAppaRao Puli // Unless required by applicable law or agreed to in writing, software
11bd030d0aSAppaRao Puli // distributed under the License is distributed on an "AS IS" BASIS,
12bd030d0aSAppaRao Puli // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bd030d0aSAppaRao Puli // See the License for the specific language governing permissions and
14bd030d0aSAppaRao Puli // limitations under the License.
15bd030d0aSAppaRao Puli */
16bd030d0aSAppaRao Puli #pragma once
1729a82b08SSunitha Harish #include <boost/asio/ip/address.hpp>
1829a82b08SSunitha Harish #include <boost/asio/ip/basic_endpoint.hpp>
19d43cd0caSEd Tanous #include <boost/asio/steady_timer.hpp>
20d43cd0caSEd Tanous #include <boost/beast/core/flat_buffer.hpp>
21d43cd0caSEd Tanous #include <boost/beast/core/tcp_stream.hpp>
22d43cd0caSEd Tanous #include <boost/beast/http/message.hpp>
23bd030d0aSAppaRao Puli #include <boost/beast/version.hpp>
24f52c03c1SCarson Labrado #include <boost/container/devector.hpp>
2529a82b08SSunitha Harish #include <include/async_resolve.hpp>
261214b7e7SGunnar Mills 
27bd030d0aSAppaRao Puli #include <cstdlib>
28bd030d0aSAppaRao Puli #include <functional>
29bd030d0aSAppaRao Puli #include <iostream>
30bd030d0aSAppaRao Puli #include <memory>
312a5689a7SAppaRao Puli #include <queue>
32bd030d0aSAppaRao Puli #include <string>
33bd030d0aSAppaRao Puli 
34bd030d0aSAppaRao Puli namespace crow
35bd030d0aSAppaRao Puli {
36bd030d0aSAppaRao Puli 
37f52c03c1SCarson Labrado // It is assumed that the BMC should be able to handle 4 parallel connections
38f52c03c1SCarson Labrado constexpr uint8_t maxPoolSize = 4;
39f52c03c1SCarson Labrado constexpr uint8_t maxRequestQueueSize = 50;
40f52c03c1SCarson Labrado constexpr unsigned int httpReadBodyLimit = 8192;
412a5689a7SAppaRao Puli 
42bd030d0aSAppaRao Puli enum class ConnState
43bd030d0aSAppaRao Puli {
442a5689a7SAppaRao Puli     initialized,
4529a82b08SSunitha Harish     resolveInProgress,
4629a82b08SSunitha Harish     resolveFailed,
472a5689a7SAppaRao Puli     connectInProgress,
482a5689a7SAppaRao Puli     connectFailed,
49bd030d0aSAppaRao Puli     connected,
502a5689a7SAppaRao Puli     sendInProgress,
512a5689a7SAppaRao Puli     sendFailed,
526eaa1d2fSSunitha Harish     recvInProgress,
532a5689a7SAppaRao Puli     recvFailed,
542a5689a7SAppaRao Puli     idle,
556eaa1d2fSSunitha Harish     closeInProgress,
56fe44eb0bSAyushi Smriti     closed,
576eaa1d2fSSunitha Harish     suspended,
586eaa1d2fSSunitha Harish     terminated,
596eaa1d2fSSunitha Harish     abortConnection,
606eaa1d2fSSunitha Harish     retry
61bd030d0aSAppaRao Puli };
62bd030d0aSAppaRao Puli 
63f52c03c1SCarson Labrado // We need to allow retry information to be set before a message has been sent
64f52c03c1SCarson Labrado // and a connection pool has been created
65f52c03c1SCarson Labrado struct RetryPolicyData
66f52c03c1SCarson Labrado {
67f52c03c1SCarson Labrado     uint32_t maxRetryAttempts = 5;
68f52c03c1SCarson Labrado     std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
69f52c03c1SCarson Labrado     std::string retryPolicyAction = "TerminateAfterRetries";
70f52c03c1SCarson Labrado     std::string name;
71f52c03c1SCarson Labrado };
72f52c03c1SCarson Labrado 
73f52c03c1SCarson Labrado struct PendingRequest
74f52c03c1SCarson Labrado {
75f52c03c1SCarson Labrado     std::string requestData;
76*039a47e3SCarson Labrado     std::function<void(bool, uint32_t, Response&)> callback;
77f52c03c1SCarson Labrado     RetryPolicyData retryPolicy;
78*039a47e3SCarson Labrado     PendingRequest(
79*039a47e3SCarson Labrado         const std::string& requestData,
80*039a47e3SCarson Labrado         const std::function<void(bool, uint32_t, Response&)>& callback,
81f52c03c1SCarson Labrado         const RetryPolicyData& retryPolicy) :
82f52c03c1SCarson Labrado         requestData(requestData),
83f52c03c1SCarson Labrado         callback(callback), retryPolicy(retryPolicy)
84f52c03c1SCarson Labrado     {}
85f52c03c1SCarson Labrado };
86f52c03c1SCarson Labrado 
87f52c03c1SCarson Labrado class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
88bd030d0aSAppaRao Puli {
89bd030d0aSAppaRao Puli   private:
90f52c03c1SCarson Labrado     ConnState state = ConnState::initialized;
91f52c03c1SCarson Labrado     uint32_t retryCount = 0;
92f52c03c1SCarson Labrado     bool runningTimer = false;
93f52c03c1SCarson Labrado     std::string subId;
94f52c03c1SCarson Labrado     std::string host;
95f52c03c1SCarson Labrado     uint16_t port;
96f52c03c1SCarson Labrado     uint32_t connId;
97f52c03c1SCarson Labrado 
98f52c03c1SCarson Labrado     // Retry policy information
99f52c03c1SCarson Labrado     // This should be updated before each message is sent
100f52c03c1SCarson Labrado     RetryPolicyData retryPolicy;
101f52c03c1SCarson Labrado 
102f52c03c1SCarson Labrado     // Data buffers
103f52c03c1SCarson Labrado     std::string data;
104bd030d0aSAppaRao Puli     boost::beast::http::request<boost::beast::http::string_body> req;
1056eaa1d2fSSunitha Harish     std::optional<
1066eaa1d2fSSunitha Harish         boost::beast::http::response_parser<boost::beast::http::string_body>>
1076eaa1d2fSSunitha Harish         parser;
108f52c03c1SCarson Labrado     boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
109*039a47e3SCarson Labrado     Response res;
1106eaa1d2fSSunitha Harish 
111f52c03c1SCarson Labrado     // Ascync callables
112*039a47e3SCarson Labrado     std::function<void(bool, uint32_t, Response&)> callback;
113f52c03c1SCarson Labrado     crow::async_resolve::Resolver resolver;
114f52c03c1SCarson Labrado     boost::beast::tcp_stream conn;
115f52c03c1SCarson Labrado     boost::asio::steady_timer timer;
11684b35604SEd Tanous 
117f52c03c1SCarson Labrado     friend class ConnectionPool;
118bd030d0aSAppaRao Puli 
11929a82b08SSunitha Harish     void doResolve()
12029a82b08SSunitha Harish     {
12129a82b08SSunitha Harish         state = ConnState::resolveInProgress;
122f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
123f52c03c1SCarson Labrado                          << std::to_string(port)
124f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId);
12529a82b08SSunitha Harish 
12629a82b08SSunitha Harish         auto respHandler =
12729a82b08SSunitha Harish             [self(shared_from_this())](
12829a82b08SSunitha Harish                 const boost::beast::error_code ec,
12929a82b08SSunitha Harish                 const std::vector<boost::asio::ip::tcp::endpoint>&
13029a82b08SSunitha Harish                     endpointList) {
13126f6976fSEd Tanous                 if (ec || (endpointList.empty()))
13229a82b08SSunitha Harish                 {
13329a82b08SSunitha Harish                     BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
13429a82b08SSunitha Harish                     self->state = ConnState::resolveFailed;
135f52c03c1SCarson Labrado                     self->waitAndRetry();
13629a82b08SSunitha Harish                     return;
13729a82b08SSunitha Harish                 }
138f52c03c1SCarson Labrado                 BMCWEB_LOG_DEBUG << "Resolved " << self->host << ":"
139f52c03c1SCarson Labrado                                  << std::to_string(self->port)
140f52c03c1SCarson Labrado                                  << ", id: " << std::to_string(self->connId);
14129a82b08SSunitha Harish                 self->doConnect(endpointList);
14229a82b08SSunitha Harish             };
143f52c03c1SCarson Labrado 
14429a82b08SSunitha Harish         resolver.asyncResolve(host, port, std::move(respHandler));
14529a82b08SSunitha Harish     }
14629a82b08SSunitha Harish 
14729a82b08SSunitha Harish     void doConnect(
14829a82b08SSunitha Harish         const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
149bd030d0aSAppaRao Puli     {
1502a5689a7SAppaRao Puli         state = ConnState::connectInProgress;
1512a5689a7SAppaRao Puli 
152f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
153f52c03c1SCarson Labrado                          << std::to_string(port)
154f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId);
155b00dcc27SEd Tanous 
15629a82b08SSunitha Harish         conn.expires_after(std::chrono::seconds(30));
15729a82b08SSunitha Harish         conn.async_connect(
15829a82b08SSunitha Harish             endpointList, [self(shared_from_this())](
15929a82b08SSunitha Harish                               const boost::beast::error_code ec,
16029a82b08SSunitha Harish                               const boost::asio::ip::tcp::endpoint& endpoint) {
1612a5689a7SAppaRao Puli                 if (ec)
1622a5689a7SAppaRao Puli                 {
1638cc8edecSEd Tanous                     BMCWEB_LOG_ERROR << "Connect "
164f52c03c1SCarson Labrado                                      << endpoint.address().to_string() << ":"
165f52c03c1SCarson Labrado                                      << std::to_string(endpoint.port())
166f52c03c1SCarson Labrado                                      << ", id: " << std::to_string(self->connId)
1672a5689a7SAppaRao Puli                                      << " failed: " << ec.message();
1682a5689a7SAppaRao Puli                     self->state = ConnState::connectFailed;
169f52c03c1SCarson Labrado                     self->waitAndRetry();
1702a5689a7SAppaRao Puli                     return;
1712a5689a7SAppaRao Puli                 }
172f52c03c1SCarson Labrado                 BMCWEB_LOG_DEBUG
173f52c03c1SCarson Labrado                     << "Connected to: " << endpoint.address().to_string() << ":"
174f52c03c1SCarson Labrado                     << std::to_string(endpoint.port())
175f52c03c1SCarson Labrado                     << ", id: " << std::to_string(self->connId);
1766eaa1d2fSSunitha Harish                 self->state = ConnState::connected;
177f52c03c1SCarson Labrado                 self->sendMessage();
1782a5689a7SAppaRao Puli             });
1792a5689a7SAppaRao Puli     }
1802a5689a7SAppaRao Puli 
181f52c03c1SCarson Labrado     void sendMessage()
1822a5689a7SAppaRao Puli     {
1832a5689a7SAppaRao Puli         state = ConnState::sendInProgress;
1842a5689a7SAppaRao Puli 
1852a5689a7SAppaRao Puli         req.body() = data;
1862a5689a7SAppaRao Puli         req.prepare_payload();
187bd030d0aSAppaRao Puli 
188bd030d0aSAppaRao Puli         // Set a timeout on the operation
189bd030d0aSAppaRao Puli         conn.expires_after(std::chrono::seconds(30));
190bd030d0aSAppaRao Puli 
191bd030d0aSAppaRao Puli         // Send the HTTP request to the remote host
192bd030d0aSAppaRao Puli         boost::beast::http::async_write(
193bd030d0aSAppaRao Puli             conn, req,
1942a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
195bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
196bd030d0aSAppaRao Puli                 if (ec)
197bd030d0aSAppaRao Puli                 {
198bd030d0aSAppaRao Puli                     BMCWEB_LOG_ERROR << "sendMessage() failed: "
199bd030d0aSAppaRao Puli                                      << ec.message();
2002a5689a7SAppaRao Puli                     self->state = ConnState::sendFailed;
201f52c03c1SCarson Labrado                     self->waitAndRetry();
202bd030d0aSAppaRao Puli                     return;
203bd030d0aSAppaRao Puli                 }
204bd030d0aSAppaRao Puli                 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
205bd030d0aSAppaRao Puli                                  << bytesTransferred;
206bd030d0aSAppaRao Puli                 boost::ignore_unused(bytesTransferred);
207bd030d0aSAppaRao Puli 
2082a5689a7SAppaRao Puli                 self->recvMessage();
209bd030d0aSAppaRao Puli             });
210bd030d0aSAppaRao Puli     }
211bd030d0aSAppaRao Puli 
212bd030d0aSAppaRao Puli     void recvMessage()
213bd030d0aSAppaRao Puli     {
2146eaa1d2fSSunitha Harish         state = ConnState::recvInProgress;
2156eaa1d2fSSunitha Harish 
2166eaa1d2fSSunitha Harish         parser.emplace(std::piecewise_construct, std::make_tuple());
2176eaa1d2fSSunitha Harish         parser->body_limit(httpReadBodyLimit);
2186eaa1d2fSSunitha Harish 
219bd030d0aSAppaRao Puli         // Receive the HTTP response
220bd030d0aSAppaRao Puli         boost::beast::http::async_read(
2216eaa1d2fSSunitha Harish             conn, buffer, *parser,
2222a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
223bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
224bd030d0aSAppaRao Puli                 if (ec)
225bd030d0aSAppaRao Puli                 {
226bd030d0aSAppaRao Puli                     BMCWEB_LOG_ERROR << "recvMessage() failed: "
227bd030d0aSAppaRao Puli                                      << ec.message();
2282a5689a7SAppaRao Puli                     self->state = ConnState::recvFailed;
229f52c03c1SCarson Labrado                     self->waitAndRetry();
230bd030d0aSAppaRao Puli                     return;
231bd030d0aSAppaRao Puli                 }
232bd030d0aSAppaRao Puli                 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
233bd030d0aSAppaRao Puli                                  << bytesTransferred;
2346eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG << "recvMessage() data: "
2358cc8edecSEd Tanous                                  << self->parser->get().body();
236bd030d0aSAppaRao Puli 
2376eaa1d2fSSunitha Harish                 unsigned int respCode = self->parser->get().result_int();
2386eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
2396eaa1d2fSSunitha Harish                                  << respCode;
2406eaa1d2fSSunitha Harish 
2416eaa1d2fSSunitha Harish                 // 2XX response is considered to be successful
2426eaa1d2fSSunitha Harish                 if ((respCode < 200) || (respCode >= 300))
2436eaa1d2fSSunitha Harish                 {
2446eaa1d2fSSunitha Harish                     // The listener failed to receive the Sent-Event
2457adb85acSSunitha Harish                     BMCWEB_LOG_ERROR
2467adb85acSSunitha Harish                         << "recvMessage() Listener Failed to "
2477adb85acSSunitha Harish                            "receive Sent-Event. Header Response Code: "
2487adb85acSSunitha Harish                         << respCode;
2496eaa1d2fSSunitha Harish                     self->state = ConnState::recvFailed;
250f52c03c1SCarson Labrado                     self->waitAndRetry();
2516eaa1d2fSSunitha Harish                     return;
2526eaa1d2fSSunitha Harish                 }
253bd030d0aSAppaRao Puli 
254f52c03c1SCarson Labrado                 // Send is successful
255f52c03c1SCarson Labrado                 // Reset the counter just in case this was after retrying
256f52c03c1SCarson Labrado                 self->retryCount = 0;
2576eaa1d2fSSunitha Harish 
2586eaa1d2fSSunitha Harish                 // Keep the connection alive if server supports it
2596eaa1d2fSSunitha Harish                 // Else close the connection
2606eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
2616eaa1d2fSSunitha Harish                                  << self->parser->keep_alive();
2626eaa1d2fSSunitha Harish 
263*039a47e3SCarson Labrado                 // Copy the response into a Response object so that it can be
264*039a47e3SCarson Labrado                 // processed by the callback function.
265*039a47e3SCarson Labrado                 self->res.clear();
266*039a47e3SCarson Labrado                 self->res.stringResponse = self->parser->release();
267*039a47e3SCarson Labrado                 self->callback(self->parser->keep_alive(), self->connId,
268*039a47e3SCarson Labrado                                self->res);
269bd030d0aSAppaRao Puli             });
270bd030d0aSAppaRao Puli     }
271bd030d0aSAppaRao Puli 
2726eaa1d2fSSunitha Harish     void waitAndRetry()
273bd030d0aSAppaRao Puli     {
274f52c03c1SCarson Labrado         if (retryCount >= retryPolicy.maxRetryAttempts)
2752a5689a7SAppaRao Puli         {
2766eaa1d2fSSunitha Harish             BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
277f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Retry policy: "
278f52c03c1SCarson Labrado                              << retryPolicy.retryPolicyAction;
279*039a47e3SCarson Labrado 
280*039a47e3SCarson Labrado             // We want to return a 502 to indicate there was an error with the
281*039a47e3SCarson Labrado             // external server
282*039a47e3SCarson Labrado             res.clear();
283*039a47e3SCarson Labrado             redfish::messages::operationFailed(res);
284*039a47e3SCarson Labrado 
285f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
286fe44eb0bSAyushi Smriti             {
287fe44eb0bSAyushi Smriti                 // TODO: delete subscription
288fe44eb0bSAyushi Smriti                 state = ConnState::terminated;
289*039a47e3SCarson Labrado                 callback(false, connId, res);
290fe44eb0bSAyushi Smriti             }
291f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "SuspendRetries")
292fe44eb0bSAyushi Smriti             {
2932a5689a7SAppaRao Puli                 state = ConnState::suspended;
294*039a47e3SCarson Labrado                 callback(false, connId, res);
2952a5689a7SAppaRao Puli             }
2966eaa1d2fSSunitha Harish             // Reset the retrycount to zero so that client can try connecting
2976eaa1d2fSSunitha Harish             // again if needed
298fe44eb0bSAyushi Smriti             retryCount = 0;
2992a5689a7SAppaRao Puli             return;
3002a5689a7SAppaRao Puli         }
3012a5689a7SAppaRao Puli 
302fe44eb0bSAyushi Smriti         if (runningTimer)
303fe44eb0bSAyushi Smriti         {
304fe44eb0bSAyushi Smriti             BMCWEB_LOG_DEBUG << "Retry timer is already running.";
305fe44eb0bSAyushi Smriti             return;
306fe44eb0bSAyushi Smriti         }
307fe44eb0bSAyushi Smriti         runningTimer = true;
308fe44eb0bSAyushi Smriti 
3092a5689a7SAppaRao Puli         retryCount++;
310fe44eb0bSAyushi Smriti 
311f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Attempt retry after "
312f52c03c1SCarson Labrado                          << std::to_string(
313f52c03c1SCarson Labrado                                 retryPolicy.retryIntervalSecs.count())
314fe44eb0bSAyushi Smriti                          << " seconds. RetryCount = " << retryCount;
315f52c03c1SCarson Labrado         timer.expires_after(retryPolicy.retryIntervalSecs);
316cb13a392SEd Tanous         timer.async_wait(
317f52c03c1SCarson Labrado             [self(shared_from_this())](const boost::system::error_code ec) {
3186eaa1d2fSSunitha Harish                 if (ec == boost::asio::error::operation_aborted)
3196eaa1d2fSSunitha Harish                 {
3206eaa1d2fSSunitha Harish                     BMCWEB_LOG_DEBUG
3216eaa1d2fSSunitha Harish                         << "async_wait failed since the operation is aborted"
3226eaa1d2fSSunitha Harish                         << ec.message();
3236eaa1d2fSSunitha Harish                 }
3246eaa1d2fSSunitha Harish                 else if (ec)
3256eaa1d2fSSunitha Harish                 {
3266eaa1d2fSSunitha Harish                     BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
3276eaa1d2fSSunitha Harish                     // Ignore the error and continue the retry loop to attempt
3286eaa1d2fSSunitha Harish                     // sending the event as per the retry policy
3296eaa1d2fSSunitha Harish                 }
330fe44eb0bSAyushi Smriti                 self->runningTimer = false;
3316eaa1d2fSSunitha Harish 
332f52c03c1SCarson Labrado                 // Let's close the connection and restart from resolve.
333f52c03c1SCarson Labrado                 self->doCloseAndRetry();
334fe44eb0bSAyushi Smriti             });
3352a5689a7SAppaRao Puli     }
3362a5689a7SAppaRao Puli 
337f52c03c1SCarson Labrado     void doClose()
338fe44eb0bSAyushi Smriti     {
339f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
340f52c03c1SCarson Labrado         boost::beast::error_code ec;
341f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
342f52c03c1SCarson Labrado         conn.close();
343f52c03c1SCarson Labrado 
344f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
345f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
3462a5689a7SAppaRao Puli         {
347f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
348f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
349f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3506eaa1d2fSSunitha Harish             return;
3516eaa1d2fSSunitha Harish         }
352f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
353f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
354f52c03c1SCarson Labrado                          << " closed gracefully";
355f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
356f52c03c1SCarson Labrado         {
357f52c03c1SCarson Labrado             state = ConnState::closed;
3586eaa1d2fSSunitha Harish         }
3596eaa1d2fSSunitha Harish     }
360f52c03c1SCarson Labrado 
361f52c03c1SCarson Labrado     void doCloseAndRetry()
36292a74e56SAppaRao Puli     {
363f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
364f52c03c1SCarson Labrado         boost::beast::error_code ec;
365f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
366f52c03c1SCarson Labrado         conn.close();
367f52c03c1SCarson Labrado 
368f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
369f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
37092a74e56SAppaRao Puli         {
371f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
372f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
373f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3746eaa1d2fSSunitha Harish             return;
3756eaa1d2fSSunitha Harish         }
376f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
377f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
378f52c03c1SCarson Labrado                          << " closed gracefully";
379f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
3806eaa1d2fSSunitha Harish         {
381f52c03c1SCarson Labrado             // Now let's try to resend the data
382f52c03c1SCarson Labrado             state = ConnState::retry;
383f52c03c1SCarson Labrado             this->doResolve();
3842a5689a7SAppaRao Puli         }
385bd030d0aSAppaRao Puli     }
386bd030d0aSAppaRao Puli 
387bd030d0aSAppaRao Puli   public:
388f52c03c1SCarson Labrado     explicit ConnectionInfo(boost::asio::io_context& ioc, const std::string& id,
389f52c03c1SCarson Labrado                             const std::string& destIP, const uint16_t destPort,
3904da04455SEd Tanous                             const std::string& destUri,
391f52c03c1SCarson Labrado                             const boost::beast::http::fields& httpHeader,
392f52c03c1SCarson Labrado                             const unsigned int connId) :
393f52c03c1SCarson Labrado         subId(id),
394f52c03c1SCarson Labrado         host(destIP), port(destPort), connId(connId),
3954da04455SEd Tanous         req(boost::beast::http::verb::post, destUri, 11, "", httpHeader),
396f52c03c1SCarson Labrado         conn(ioc), timer(ioc)
397bd030d0aSAppaRao Puli     {
3984da04455SEd Tanous         req.set(boost::beast::http::field::host, host);
3994da04455SEd Tanous         req.keep_alive(true);
400bd030d0aSAppaRao Puli     }
401f52c03c1SCarson Labrado };
402bd030d0aSAppaRao Puli 
403f52c03c1SCarson Labrado class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
404bd030d0aSAppaRao Puli {
405f52c03c1SCarson Labrado   private:
406f52c03c1SCarson Labrado     boost::asio::io_context& ioc;
407f52c03c1SCarson Labrado     const std::string id;
408f52c03c1SCarson Labrado     const std::string destIP;
409f52c03c1SCarson Labrado     const uint16_t destPort;
410f52c03c1SCarson Labrado     const std::string destUri;
411f52c03c1SCarson Labrado     const boost::beast::http::fields httpHeader;
412f52c03c1SCarson Labrado     std::vector<std::shared_ptr<ConnectionInfo>> connections;
413f52c03c1SCarson Labrado     boost::container::devector<PendingRequest> requestQueue;
414f52c03c1SCarson Labrado 
415f52c03c1SCarson Labrado     friend class HttpClient;
416f52c03c1SCarson Labrado 
417f52c03c1SCarson Labrado     // Configure a connections's data, callback, and retry info in preparation
418f52c03c1SCarson Labrado     // to begin sending a request
419f52c03c1SCarson Labrado     void setConnProps(ConnectionInfo& conn)
420bd030d0aSAppaRao Puli     {
421f52c03c1SCarson Labrado         if (requestQueue.empty())
422f52c03c1SCarson Labrado         {
423f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR
424f52c03c1SCarson Labrado                 << "setConnProps() should not have been called when requestQueue is empty";
425bd030d0aSAppaRao Puli             return;
426bd030d0aSAppaRao Puli         }
427bd030d0aSAppaRao Puli 
428f52c03c1SCarson Labrado         auto req = requestQueue.front();
429f52c03c1SCarson Labrado         conn.retryPolicy = std::move(req.retryPolicy);
430f52c03c1SCarson Labrado         conn.data = std::move(req.requestData);
431f52c03c1SCarson Labrado         conn.callback = std::move(req.callback);
432f52c03c1SCarson Labrado 
433f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
434f52c03c1SCarson Labrado                          << ":" << std::to_string(conn.port)
435f52c03c1SCarson Labrado                          << ", id: " << std::to_string(conn.connId)
436f52c03c1SCarson Labrado                          << ", retry policy is \"" << conn.retryPolicy.name
437f52c03c1SCarson Labrado                          << "\"";
438f52c03c1SCarson Labrado 
439f52c03c1SCarson Labrado         // We can remove the request from the queue at this point
440f52c03c1SCarson Labrado         requestQueue.pop_front();
441f52c03c1SCarson Labrado     }
442f52c03c1SCarson Labrado 
443f52c03c1SCarson Labrado     // Configures a connection to use the specific retry policy.
444f52c03c1SCarson Labrado     inline void setConnRetryPolicy(ConnectionInfo& conn,
445f52c03c1SCarson Labrado                                    const RetryPolicyData& retryPolicy)
4462a5689a7SAppaRao Puli     {
447f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
448f52c03c1SCarson Labrado                          << ", id: " << std::to_string(conn.connId)
449f52c03c1SCarson Labrado                          << " using retry policy \"" << retryPolicy.name
450f52c03c1SCarson Labrado                          << "\"";
451f52c03c1SCarson Labrado 
452f52c03c1SCarson Labrado         conn.retryPolicy = retryPolicy;
453f52c03c1SCarson Labrado     }
454f52c03c1SCarson Labrado 
455f52c03c1SCarson Labrado     // Gets called as part of callback after request is sent
456f52c03c1SCarson Labrado     // Reuses the connection if there are any requests waiting to be sent
457f52c03c1SCarson Labrado     // Otherwise closes the connection if it is not a keep-alive
458f52c03c1SCarson Labrado     void sendNext(bool keepAlive, uint32_t connId)
459f52c03c1SCarson Labrado     {
460f52c03c1SCarson Labrado         auto conn = connections[connId];
461f52c03c1SCarson Labrado         // Reuse the connection to send the next request in the queue
462f52c03c1SCarson Labrado         if (!requestQueue.empty())
463f52c03c1SCarson Labrado         {
464f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
465f52c03c1SCarson Labrado                              << " requests remaining in queue for " << destIP
466f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort)
467f52c03c1SCarson Labrado                              << ", reusing connnection "
468f52c03c1SCarson Labrado                              << std::to_string(connId);
469f52c03c1SCarson Labrado 
470f52c03c1SCarson Labrado             setConnProps(*conn);
471f52c03c1SCarson Labrado 
472f52c03c1SCarson Labrado             if (keepAlive)
473f52c03c1SCarson Labrado             {
474f52c03c1SCarson Labrado                 conn->sendMessage();
4752a5689a7SAppaRao Puli             }
4762a5689a7SAppaRao Puli             else
4772a5689a7SAppaRao Puli             {
478f52c03c1SCarson Labrado                 // Server is not keep-alive enabled so we need to close the
479f52c03c1SCarson Labrado                 // connection and then start over from resolve
480f52c03c1SCarson Labrado                 conn->doClose();
481f52c03c1SCarson Labrado                 conn->doResolve();
482f52c03c1SCarson Labrado             }
483f52c03c1SCarson Labrado             return;
484f52c03c1SCarson Labrado         }
485f52c03c1SCarson Labrado 
486f52c03c1SCarson Labrado         // No more messages to send so close the connection if necessary
487f52c03c1SCarson Labrado         if (keepAlive)
488f52c03c1SCarson Labrado         {
489f52c03c1SCarson Labrado             conn->state = ConnState::idle;
490f52c03c1SCarson Labrado         }
491f52c03c1SCarson Labrado         else
492f52c03c1SCarson Labrado         {
493f52c03c1SCarson Labrado             // Abort the connection since server is not keep-alive enabled
494f52c03c1SCarson Labrado             conn->state = ConnState::abortConnection;
495f52c03c1SCarson Labrado             conn->doClose();
4962a5689a7SAppaRao Puli         }
497bd030d0aSAppaRao Puli     }
498bd030d0aSAppaRao Puli 
499*039a47e3SCarson Labrado     void sendData(std::string& data, const RetryPolicyData& retryPolicy,
500*039a47e3SCarson Labrado                   std::function<void(Response&)>& resHandler)
501fe44eb0bSAyushi Smriti     {
502f52c03c1SCarson Labrado         std::weak_ptr<ConnectionPool> weakSelf = weak_from_this();
503f52c03c1SCarson Labrado 
504f52c03c1SCarson Labrado         // Callback to be called once the request has been sent
505*039a47e3SCarson Labrado         auto cb = [weakSelf, resHandler](bool keepAlive, uint32_t connId,
506*039a47e3SCarson Labrado                                          Response& res) {
507*039a47e3SCarson Labrado             // Allow provided callback to perform additional processing of the
508*039a47e3SCarson Labrado             // request
509*039a47e3SCarson Labrado             resHandler(res);
510*039a47e3SCarson Labrado 
511f52c03c1SCarson Labrado             // If requests remain in the queue then we want to reuse this
512f52c03c1SCarson Labrado             // connection to send the next request
513f52c03c1SCarson Labrado             std::shared_ptr<ConnectionPool> self = weakSelf.lock();
514f52c03c1SCarson Labrado             if (!self)
515f52c03c1SCarson Labrado             {
516f52c03c1SCarson Labrado                 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
517f52c03c1SCarson Labrado                 return;
518fe44eb0bSAyushi Smriti             }
519fe44eb0bSAyushi Smriti 
520f52c03c1SCarson Labrado             self->sendNext(keepAlive, connId);
521f52c03c1SCarson Labrado         };
522f52c03c1SCarson Labrado 
523f52c03c1SCarson Labrado         // Reuse an existing connection if one is available
524f52c03c1SCarson Labrado         for (unsigned int i = 0; i < connections.size(); i++)
525fe44eb0bSAyushi Smriti         {
526f52c03c1SCarson Labrado             auto conn = connections[i];
527f52c03c1SCarson Labrado             if ((conn->state == ConnState::idle) ||
528f52c03c1SCarson Labrado                 (conn->state == ConnState::initialized) ||
529f52c03c1SCarson Labrado                 (conn->state == ConnState::closed))
530f52c03c1SCarson Labrado             {
531f52c03c1SCarson Labrado                 conn->data = std::move(data);
532f52c03c1SCarson Labrado                 conn->callback = std::move(cb);
533f52c03c1SCarson Labrado                 conn->retryPolicy = retryPolicy;
534f52c03c1SCarson Labrado                 setConnRetryPolicy(*conn, retryPolicy);
535f52c03c1SCarson Labrado                 std::string commonMsg = std::to_string(i) + " from pool " +
536f52c03c1SCarson Labrado                                         destIP + ":" + std::to_string(destPort);
537f52c03c1SCarson Labrado 
538f52c03c1SCarson Labrado                 if (conn->state == ConnState::idle)
539f52c03c1SCarson Labrado                 {
540f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Grabbing idle connection "
541f52c03c1SCarson Labrado                                      << commonMsg;
542f52c03c1SCarson Labrado                     conn->sendMessage();
543f52c03c1SCarson Labrado                 }
544f52c03c1SCarson Labrado                 else
545f52c03c1SCarson Labrado                 {
546f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Reusing existing connection "
547f52c03c1SCarson Labrado                                      << commonMsg;
548f52c03c1SCarson Labrado                     conn->doResolve();
549f52c03c1SCarson Labrado                 }
550f52c03c1SCarson Labrado                 return;
551f52c03c1SCarson Labrado             }
552f52c03c1SCarson Labrado         }
553f52c03c1SCarson Labrado 
554f52c03c1SCarson Labrado         // All connections in use so create a new connection or add request to
555f52c03c1SCarson Labrado         // the queue
556f52c03c1SCarson Labrado         if (connections.size() < maxPoolSize)
557f52c03c1SCarson Labrado         {
558f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
559f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort);
560f52c03c1SCarson Labrado             auto conn = addConnection();
561f52c03c1SCarson Labrado             conn->data = std::move(data);
562f52c03c1SCarson Labrado             conn->callback = std::move(cb);
563f52c03c1SCarson Labrado             setConnRetryPolicy(*conn, retryPolicy);
564f52c03c1SCarson Labrado             conn->doResolve();
565f52c03c1SCarson Labrado         }
566f52c03c1SCarson Labrado         else if (requestQueue.size() < maxRequestQueueSize)
567f52c03c1SCarson Labrado         {
568f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
569f52c03c1SCarson Labrado             requestQueue.emplace_back(std::move(data), std::move(cb),
570f52c03c1SCarson Labrado                                       retryPolicy);
571f52c03c1SCarson Labrado         }
572f52c03c1SCarson Labrado         else
573f52c03c1SCarson Labrado         {
574f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
575f52c03c1SCarson Labrado                              << " request queue full.  Dropping request.";
576f52c03c1SCarson Labrado         }
577f52c03c1SCarson Labrado     }
578f52c03c1SCarson Labrado 
579f52c03c1SCarson Labrado     std::shared_ptr<ConnectionInfo>& addConnection()
580f52c03c1SCarson Labrado     {
581f52c03c1SCarson Labrado         unsigned int newId = static_cast<unsigned int>(connections.size());
582f52c03c1SCarson Labrado 
583f52c03c1SCarson Labrado         auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
584f52c03c1SCarson Labrado             ioc, id, destIP, destPort, destUri, httpHeader, newId));
585f52c03c1SCarson Labrado 
586f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Added connection "
587f52c03c1SCarson Labrado                          << std::to_string(connections.size() - 1)
588f52c03c1SCarson Labrado                          << " to pool " << destIP << ":"
589f52c03c1SCarson Labrado                          << std::to_string(destPort);
590f52c03c1SCarson Labrado 
591f52c03c1SCarson Labrado         return ret;
592f52c03c1SCarson Labrado     }
593f52c03c1SCarson Labrado 
594f52c03c1SCarson Labrado   public:
595f52c03c1SCarson Labrado     explicit ConnectionPool(boost::asio::io_context& ioc, const std::string& id,
596f52c03c1SCarson Labrado                             const std::string& destIP, const uint16_t destPort,
597f52c03c1SCarson Labrado                             const std::string& destUri,
598f52c03c1SCarson Labrado                             const boost::beast::http::fields& httpHeader) :
599f52c03c1SCarson Labrado         ioc(ioc),
600f52c03c1SCarson Labrado         id(id), destIP(destIP), destPort(destPort), destUri(destUri),
601f52c03c1SCarson Labrado         httpHeader(httpHeader)
602f52c03c1SCarson Labrado     {
603f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
604f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
605f52c03c1SCarson Labrado                          << std::to_string(destPort);
606f52c03c1SCarson Labrado 
607f52c03c1SCarson Labrado         // Initialize the pool with a single connection
608f52c03c1SCarson Labrado         addConnection();
609fe44eb0bSAyushi Smriti     }
610bd030d0aSAppaRao Puli };
611bd030d0aSAppaRao Puli 
612f52c03c1SCarson Labrado class HttpClient
613f52c03c1SCarson Labrado {
614f52c03c1SCarson Labrado   private:
615f52c03c1SCarson Labrado     std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
616f52c03c1SCarson Labrado         connectionPools;
617f52c03c1SCarson Labrado     boost::asio::io_context& ioc =
618f52c03c1SCarson Labrado         crow::connections::systemBus->get_io_context();
619f52c03c1SCarson Labrado     std::unordered_map<std::string, RetryPolicyData> retryInfo;
620f52c03c1SCarson Labrado     HttpClient() = default;
621f52c03c1SCarson Labrado 
622*039a47e3SCarson Labrado     // Used as a dummy callback by sendData() in order to call
623*039a47e3SCarson Labrado     // sendDataWithCallback()
624*039a47e3SCarson Labrado     static void genericResHandler(Response& res)
625*039a47e3SCarson Labrado     {
626*039a47e3SCarson Labrado         BMCWEB_LOG_DEBUG << "Response handled with return code: "
627*039a47e3SCarson Labrado                          << std::to_string(res.resultInt());
628*039a47e3SCarson Labrado     };
629*039a47e3SCarson Labrado 
630f52c03c1SCarson Labrado   public:
631f52c03c1SCarson Labrado     HttpClient(const HttpClient&) = delete;
632f52c03c1SCarson Labrado     HttpClient& operator=(const HttpClient&) = delete;
633f52c03c1SCarson Labrado     HttpClient(HttpClient&&) = delete;
634f52c03c1SCarson Labrado     HttpClient& operator=(HttpClient&&) = delete;
635f52c03c1SCarson Labrado     ~HttpClient() = default;
636f52c03c1SCarson Labrado 
637f52c03c1SCarson Labrado     static HttpClient& getInstance()
638f52c03c1SCarson Labrado     {
639f52c03c1SCarson Labrado         static HttpClient handler;
640f52c03c1SCarson Labrado         return handler;
641f52c03c1SCarson Labrado     }
642f52c03c1SCarson Labrado 
643*039a47e3SCarson Labrado     // Send a request to destIP:destPort where additional processing of the
644*039a47e3SCarson Labrado     // result is not required
645f52c03c1SCarson Labrado     void sendData(std::string& data, const std::string& id,
646f52c03c1SCarson Labrado                   const std::string& destIP, const uint16_t destPort,
647f52c03c1SCarson Labrado                   const std::string& destUri,
648f52c03c1SCarson Labrado                   const boost::beast::http::fields& httpHeader,
649f52c03c1SCarson Labrado                   std::string& retryPolicyName)
650f52c03c1SCarson Labrado     {
651*039a47e3SCarson Labrado         std::function<void(Response&)> cb = genericResHandler;
652*039a47e3SCarson Labrado         sendDataWithCallback(data, id, destIP, destPort, destUri, httpHeader,
653*039a47e3SCarson Labrado                              retryPolicyName, cb);
654*039a47e3SCarson Labrado     }
655*039a47e3SCarson Labrado 
656*039a47e3SCarson Labrado     // Send request to destIP:destPort and use the provided callback to
657*039a47e3SCarson Labrado     // handle the response
658*039a47e3SCarson Labrado     void sendDataWithCallback(std::string& data, const std::string& id,
659*039a47e3SCarson Labrado                               const std::string& destIP,
660*039a47e3SCarson Labrado                               const uint16_t destPort,
661*039a47e3SCarson Labrado                               const std::string& destUri,
662*039a47e3SCarson Labrado                               const boost::beast::http::fields& httpHeader,
663*039a47e3SCarson Labrado                               std::string& retryPolicyName,
664*039a47e3SCarson Labrado                               std::function<void(Response&)>& resHandler)
665*039a47e3SCarson Labrado     {
666f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
667f52c03c1SCarson Labrado         // Use nullptr to avoid creating a ConnectionPool each time
668f52c03c1SCarson Labrado         auto result = connectionPools.try_emplace(clientKey, nullptr);
669f52c03c1SCarson Labrado         if (result.second)
670f52c03c1SCarson Labrado         {
671f52c03c1SCarson Labrado             // Now actually create the ConnectionPool shared_ptr since it does
672f52c03c1SCarson Labrado             // not already exist
673f52c03c1SCarson Labrado             result.first->second = std::make_shared<ConnectionPool>(
674f52c03c1SCarson Labrado                 ioc, id, destIP, destPort, destUri, httpHeader);
675f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
676f52c03c1SCarson Labrado         }
677f52c03c1SCarson Labrado         else
678f52c03c1SCarson Labrado         {
679f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Using existing connection pool for "
680f52c03c1SCarson Labrado                              << clientKey;
681f52c03c1SCarson Labrado         }
682f52c03c1SCarson Labrado 
683f52c03c1SCarson Labrado         // Get the associated retry policy
684f52c03c1SCarson Labrado         auto policy = retryInfo.try_emplace(retryPolicyName);
685f52c03c1SCarson Labrado         if (policy.second)
686f52c03c1SCarson Labrado         {
687f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
688f52c03c1SCarson Labrado                              << "\" with default values";
689f52c03c1SCarson Labrado             policy.first->second.name = retryPolicyName;
690f52c03c1SCarson Labrado         }
691f52c03c1SCarson Labrado 
692f52c03c1SCarson Labrado         // Send the data using either the existing connection pool or the newly
693f52c03c1SCarson Labrado         // created connection pool
694*039a47e3SCarson Labrado         result.first->second->sendData(data, policy.first->second, resHandler);
695f52c03c1SCarson Labrado     }
696f52c03c1SCarson Labrado 
697f52c03c1SCarson Labrado     void setRetryConfig(const uint32_t retryAttempts,
698f52c03c1SCarson Labrado                         const uint32_t retryTimeoutInterval,
699f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
700f52c03c1SCarson Labrado     {
701f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
702f52c03c1SCarson Labrado         // the given retryPolicyName
703f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
704f52c03c1SCarson Labrado         if (result.second)
705f52c03c1SCarson Labrado         {
706f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
707f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
708f52c03c1SCarson Labrado             result.first->second.name = retryPolicyName;
709f52c03c1SCarson Labrado         }
710f52c03c1SCarson Labrado         else
711f52c03c1SCarson Labrado         {
712f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
713f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
714f52c03c1SCarson Labrado         }
715f52c03c1SCarson Labrado 
716f52c03c1SCarson Labrado         result.first->second.maxRetryAttempts = retryAttempts;
717f52c03c1SCarson Labrado         result.first->second.retryIntervalSecs =
718f52c03c1SCarson Labrado             std::chrono::seconds(retryTimeoutInterval);
719f52c03c1SCarson Labrado     }
720f52c03c1SCarson Labrado 
721f52c03c1SCarson Labrado     void setRetryPolicy(const std::string& retryPolicy,
722f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
723f52c03c1SCarson Labrado     {
724f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
725f52c03c1SCarson Labrado         // the given retryPolicyName
726f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
727f52c03c1SCarson Labrado         if (result.second)
728f52c03c1SCarson Labrado         {
729f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
730f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
731f52c03c1SCarson Labrado             result.first->second.name = retryPolicyName;
732f52c03c1SCarson Labrado         }
733f52c03c1SCarson Labrado         else
734f52c03c1SCarson Labrado         {
735f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
736f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
737f52c03c1SCarson Labrado         }
738f52c03c1SCarson Labrado 
739f52c03c1SCarson Labrado         result.first->second.retryPolicyAction = retryPolicy;
740f52c03c1SCarson Labrado     }
741f52c03c1SCarson Labrado };
742bd030d0aSAppaRao Puli } // namespace crow
743