xref: /openbmc/bmcweb/http/http_client.hpp (revision 244256cc)
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 {
75*244256ccSCarson Labrado     boost::beast::http::request<boost::beast::http::string_body> req;
76039a47e3SCarson Labrado     std::function<void(bool, uint32_t, Response&)> callback;
77f52c03c1SCarson Labrado     RetryPolicyData retryPolicy;
78039a47e3SCarson Labrado     PendingRequest(
79*244256ccSCarson Labrado         boost::beast::http::request<boost::beast::http::string_body>&& req,
80039a47e3SCarson Labrado         const std::function<void(bool, uint32_t, Response&)>& callback,
81f52c03c1SCarson Labrado         const RetryPolicyData& retryPolicy) :
82*244256ccSCarson Labrado         req(std::move(req)),
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
103bd030d0aSAppaRao Puli     boost::beast::http::request<boost::beast::http::string_body> req;
1046eaa1d2fSSunitha Harish     std::optional<
1056eaa1d2fSSunitha Harish         boost::beast::http::response_parser<boost::beast::http::string_body>>
1066eaa1d2fSSunitha Harish         parser;
107f52c03c1SCarson Labrado     boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
108039a47e3SCarson Labrado     Response res;
1096eaa1d2fSSunitha Harish 
110f52c03c1SCarson Labrado     // Ascync callables
111039a47e3SCarson Labrado     std::function<void(bool, uint32_t, Response&)> callback;
112f52c03c1SCarson Labrado     crow::async_resolve::Resolver resolver;
113f52c03c1SCarson Labrado     boost::beast::tcp_stream conn;
114f52c03c1SCarson Labrado     boost::asio::steady_timer timer;
11584b35604SEd Tanous 
116f52c03c1SCarson Labrado     friend class ConnectionPool;
117bd030d0aSAppaRao Puli 
11829a82b08SSunitha Harish     void doResolve()
11929a82b08SSunitha Harish     {
12029a82b08SSunitha Harish         state = ConnState::resolveInProgress;
121f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
122f52c03c1SCarson Labrado                          << std::to_string(port)
123f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId);
12429a82b08SSunitha Harish 
12529a82b08SSunitha Harish         auto respHandler =
12629a82b08SSunitha Harish             [self(shared_from_this())](
12729a82b08SSunitha Harish                 const boost::beast::error_code ec,
12829a82b08SSunitha Harish                 const std::vector<boost::asio::ip::tcp::endpoint>&
12929a82b08SSunitha Harish                     endpointList) {
13026f6976fSEd Tanous                 if (ec || (endpointList.empty()))
13129a82b08SSunitha Harish                 {
13229a82b08SSunitha Harish                     BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
13329a82b08SSunitha Harish                     self->state = ConnState::resolveFailed;
134f52c03c1SCarson Labrado                     self->waitAndRetry();
13529a82b08SSunitha Harish                     return;
13629a82b08SSunitha Harish                 }
137f52c03c1SCarson Labrado                 BMCWEB_LOG_DEBUG << "Resolved " << self->host << ":"
138f52c03c1SCarson Labrado                                  << std::to_string(self->port)
139f52c03c1SCarson Labrado                                  << ", id: " << std::to_string(self->connId);
14029a82b08SSunitha Harish                 self->doConnect(endpointList);
14129a82b08SSunitha Harish             };
142f52c03c1SCarson Labrado 
14329a82b08SSunitha Harish         resolver.asyncResolve(host, port, std::move(respHandler));
14429a82b08SSunitha Harish     }
14529a82b08SSunitha Harish 
14629a82b08SSunitha Harish     void doConnect(
14729a82b08SSunitha Harish         const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
148bd030d0aSAppaRao Puli     {
1492a5689a7SAppaRao Puli         state = ConnState::connectInProgress;
1502a5689a7SAppaRao Puli 
151f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
152f52c03c1SCarson Labrado                          << std::to_string(port)
153f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId);
154b00dcc27SEd Tanous 
15529a82b08SSunitha Harish         conn.expires_after(std::chrono::seconds(30));
15629a82b08SSunitha Harish         conn.async_connect(
15729a82b08SSunitha Harish             endpointList, [self(shared_from_this())](
15829a82b08SSunitha Harish                               const boost::beast::error_code ec,
15929a82b08SSunitha Harish                               const boost::asio::ip::tcp::endpoint& endpoint) {
1602a5689a7SAppaRao Puli                 if (ec)
1612a5689a7SAppaRao Puli                 {
1628cc8edecSEd Tanous                     BMCWEB_LOG_ERROR << "Connect "
163f52c03c1SCarson Labrado                                      << endpoint.address().to_string() << ":"
164f52c03c1SCarson Labrado                                      << std::to_string(endpoint.port())
165f52c03c1SCarson Labrado                                      << ", id: " << std::to_string(self->connId)
1662a5689a7SAppaRao Puli                                      << " failed: " << ec.message();
1672a5689a7SAppaRao Puli                     self->state = ConnState::connectFailed;
168f52c03c1SCarson Labrado                     self->waitAndRetry();
1692a5689a7SAppaRao Puli                     return;
1702a5689a7SAppaRao Puli                 }
171f52c03c1SCarson Labrado                 BMCWEB_LOG_DEBUG
172f52c03c1SCarson Labrado                     << "Connected to: " << endpoint.address().to_string() << ":"
173f52c03c1SCarson Labrado                     << std::to_string(endpoint.port())
174f52c03c1SCarson Labrado                     << ", id: " << std::to_string(self->connId);
1756eaa1d2fSSunitha Harish                 self->state = ConnState::connected;
176f52c03c1SCarson Labrado                 self->sendMessage();
1772a5689a7SAppaRao Puli             });
1782a5689a7SAppaRao Puli     }
1792a5689a7SAppaRao Puli 
180f52c03c1SCarson Labrado     void sendMessage()
1812a5689a7SAppaRao Puli     {
1822a5689a7SAppaRao Puli         state = ConnState::sendInProgress;
1832a5689a7SAppaRao Puli 
184bd030d0aSAppaRao Puli         // Set a timeout on the operation
185bd030d0aSAppaRao Puli         conn.expires_after(std::chrono::seconds(30));
186bd030d0aSAppaRao Puli 
187bd030d0aSAppaRao Puli         // Send the HTTP request to the remote host
188bd030d0aSAppaRao Puli         boost::beast::http::async_write(
189bd030d0aSAppaRao Puli             conn, req,
1902a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
191bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
192bd030d0aSAppaRao Puli                 if (ec)
193bd030d0aSAppaRao Puli                 {
194bd030d0aSAppaRao Puli                     BMCWEB_LOG_ERROR << "sendMessage() failed: "
195bd030d0aSAppaRao Puli                                      << ec.message();
1962a5689a7SAppaRao Puli                     self->state = ConnState::sendFailed;
197f52c03c1SCarson Labrado                     self->waitAndRetry();
198bd030d0aSAppaRao Puli                     return;
199bd030d0aSAppaRao Puli                 }
200bd030d0aSAppaRao Puli                 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
201bd030d0aSAppaRao Puli                                  << bytesTransferred;
202bd030d0aSAppaRao Puli                 boost::ignore_unused(bytesTransferred);
203bd030d0aSAppaRao Puli 
2042a5689a7SAppaRao Puli                 self->recvMessage();
205bd030d0aSAppaRao Puli             });
206bd030d0aSAppaRao Puli     }
207bd030d0aSAppaRao Puli 
208bd030d0aSAppaRao Puli     void recvMessage()
209bd030d0aSAppaRao Puli     {
2106eaa1d2fSSunitha Harish         state = ConnState::recvInProgress;
2116eaa1d2fSSunitha Harish 
2126eaa1d2fSSunitha Harish         parser.emplace(std::piecewise_construct, std::make_tuple());
2136eaa1d2fSSunitha Harish         parser->body_limit(httpReadBodyLimit);
2146eaa1d2fSSunitha Harish 
215bd030d0aSAppaRao Puli         // Receive the HTTP response
216bd030d0aSAppaRao Puli         boost::beast::http::async_read(
2176eaa1d2fSSunitha Harish             conn, buffer, *parser,
2182a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
219bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
220bd030d0aSAppaRao Puli                 if (ec)
221bd030d0aSAppaRao Puli                 {
222bd030d0aSAppaRao Puli                     BMCWEB_LOG_ERROR << "recvMessage() failed: "
223bd030d0aSAppaRao Puli                                      << ec.message();
2242a5689a7SAppaRao Puli                     self->state = ConnState::recvFailed;
225f52c03c1SCarson Labrado                     self->waitAndRetry();
226bd030d0aSAppaRao Puli                     return;
227bd030d0aSAppaRao Puli                 }
228bd030d0aSAppaRao Puli                 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
229bd030d0aSAppaRao Puli                                  << bytesTransferred;
2306eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG << "recvMessage() data: "
2318cc8edecSEd Tanous                                  << self->parser->get().body();
232bd030d0aSAppaRao Puli 
2336eaa1d2fSSunitha Harish                 unsigned int respCode = self->parser->get().result_int();
2346eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
2356eaa1d2fSSunitha Harish                                  << respCode;
2366eaa1d2fSSunitha Harish 
2376eaa1d2fSSunitha Harish                 // 2XX response is considered to be successful
2386eaa1d2fSSunitha Harish                 if ((respCode < 200) || (respCode >= 300))
2396eaa1d2fSSunitha Harish                 {
2406eaa1d2fSSunitha Harish                     // The listener failed to receive the Sent-Event
2417adb85acSSunitha Harish                     BMCWEB_LOG_ERROR
2427adb85acSSunitha Harish                         << "recvMessage() Listener Failed to "
2437adb85acSSunitha Harish                            "receive Sent-Event. Header Response Code: "
2447adb85acSSunitha Harish                         << respCode;
2456eaa1d2fSSunitha Harish                     self->state = ConnState::recvFailed;
246f52c03c1SCarson Labrado                     self->waitAndRetry();
2476eaa1d2fSSunitha Harish                     return;
2486eaa1d2fSSunitha Harish                 }
249bd030d0aSAppaRao Puli 
250f52c03c1SCarson Labrado                 // Send is successful
251f52c03c1SCarson Labrado                 // Reset the counter just in case this was after retrying
252f52c03c1SCarson Labrado                 self->retryCount = 0;
2536eaa1d2fSSunitha Harish 
2546eaa1d2fSSunitha Harish                 // Keep the connection alive if server supports it
2556eaa1d2fSSunitha Harish                 // Else close the connection
2566eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
2576eaa1d2fSSunitha Harish                                  << self->parser->keep_alive();
2586eaa1d2fSSunitha Harish 
259039a47e3SCarson Labrado                 // Copy the response into a Response object so that it can be
260039a47e3SCarson Labrado                 // processed by the callback function.
261039a47e3SCarson Labrado                 self->res.clear();
262039a47e3SCarson Labrado                 self->res.stringResponse = self->parser->release();
263039a47e3SCarson Labrado                 self->callback(self->parser->keep_alive(), self->connId,
264039a47e3SCarson Labrado                                self->res);
265bd030d0aSAppaRao Puli             });
266bd030d0aSAppaRao Puli     }
267bd030d0aSAppaRao Puli 
2686eaa1d2fSSunitha Harish     void waitAndRetry()
269bd030d0aSAppaRao Puli     {
270f52c03c1SCarson Labrado         if (retryCount >= retryPolicy.maxRetryAttempts)
2712a5689a7SAppaRao Puli         {
2726eaa1d2fSSunitha Harish             BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
273f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Retry policy: "
274f52c03c1SCarson Labrado                              << retryPolicy.retryPolicyAction;
275039a47e3SCarson Labrado 
276039a47e3SCarson Labrado             // We want to return a 502 to indicate there was an error with the
277039a47e3SCarson Labrado             // external server
278039a47e3SCarson Labrado             res.clear();
279039a47e3SCarson Labrado             redfish::messages::operationFailed(res);
280039a47e3SCarson Labrado 
281f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
282fe44eb0bSAyushi Smriti             {
283fe44eb0bSAyushi Smriti                 // TODO: delete subscription
284fe44eb0bSAyushi Smriti                 state = ConnState::terminated;
285039a47e3SCarson Labrado                 callback(false, connId, res);
286fe44eb0bSAyushi Smriti             }
287f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "SuspendRetries")
288fe44eb0bSAyushi Smriti             {
2892a5689a7SAppaRao Puli                 state = ConnState::suspended;
290039a47e3SCarson Labrado                 callback(false, connId, res);
2912a5689a7SAppaRao Puli             }
2926eaa1d2fSSunitha Harish             // Reset the retrycount to zero so that client can try connecting
2936eaa1d2fSSunitha Harish             // again if needed
294fe44eb0bSAyushi Smriti             retryCount = 0;
2952a5689a7SAppaRao Puli             return;
2962a5689a7SAppaRao Puli         }
2972a5689a7SAppaRao Puli 
298fe44eb0bSAyushi Smriti         if (runningTimer)
299fe44eb0bSAyushi Smriti         {
300fe44eb0bSAyushi Smriti             BMCWEB_LOG_DEBUG << "Retry timer is already running.";
301fe44eb0bSAyushi Smriti             return;
302fe44eb0bSAyushi Smriti         }
303fe44eb0bSAyushi Smriti         runningTimer = true;
304fe44eb0bSAyushi Smriti 
3052a5689a7SAppaRao Puli         retryCount++;
306fe44eb0bSAyushi Smriti 
307f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Attempt retry after "
308f52c03c1SCarson Labrado                          << std::to_string(
309f52c03c1SCarson Labrado                                 retryPolicy.retryIntervalSecs.count())
310fe44eb0bSAyushi Smriti                          << " seconds. RetryCount = " << retryCount;
311f52c03c1SCarson Labrado         timer.expires_after(retryPolicy.retryIntervalSecs);
312cb13a392SEd Tanous         timer.async_wait(
313f52c03c1SCarson Labrado             [self(shared_from_this())](const boost::system::error_code ec) {
3146eaa1d2fSSunitha Harish                 if (ec == boost::asio::error::operation_aborted)
3156eaa1d2fSSunitha Harish                 {
3166eaa1d2fSSunitha Harish                     BMCWEB_LOG_DEBUG
3176eaa1d2fSSunitha Harish                         << "async_wait failed since the operation is aborted"
3186eaa1d2fSSunitha Harish                         << ec.message();
3196eaa1d2fSSunitha Harish                 }
3206eaa1d2fSSunitha Harish                 else if (ec)
3216eaa1d2fSSunitha Harish                 {
3226eaa1d2fSSunitha Harish                     BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
3236eaa1d2fSSunitha Harish                     // Ignore the error and continue the retry loop to attempt
3246eaa1d2fSSunitha Harish                     // sending the event as per the retry policy
3256eaa1d2fSSunitha Harish                 }
326fe44eb0bSAyushi Smriti                 self->runningTimer = false;
3276eaa1d2fSSunitha Harish 
328f52c03c1SCarson Labrado                 // Let's close the connection and restart from resolve.
329f52c03c1SCarson Labrado                 self->doCloseAndRetry();
330fe44eb0bSAyushi Smriti             });
3312a5689a7SAppaRao Puli     }
3322a5689a7SAppaRao Puli 
333f52c03c1SCarson Labrado     void doClose()
334fe44eb0bSAyushi Smriti     {
335f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
336f52c03c1SCarson Labrado         boost::beast::error_code ec;
337f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
338f52c03c1SCarson Labrado         conn.close();
339f52c03c1SCarson Labrado 
340f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
341f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
3422a5689a7SAppaRao Puli         {
343f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
344f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
345f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3466eaa1d2fSSunitha Harish             return;
3476eaa1d2fSSunitha Harish         }
348f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
349f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
350f52c03c1SCarson Labrado                          << " closed gracefully";
351f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
352f52c03c1SCarson Labrado         {
353f52c03c1SCarson Labrado             state = ConnState::closed;
3546eaa1d2fSSunitha Harish         }
3556eaa1d2fSSunitha Harish     }
356f52c03c1SCarson Labrado 
357f52c03c1SCarson Labrado     void doCloseAndRetry()
35892a74e56SAppaRao Puli     {
359f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
360f52c03c1SCarson Labrado         boost::beast::error_code ec;
361f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
362f52c03c1SCarson Labrado         conn.close();
363f52c03c1SCarson Labrado 
364f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
365f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
36692a74e56SAppaRao Puli         {
367f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
368f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
369f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3706eaa1d2fSSunitha Harish             return;
3716eaa1d2fSSunitha Harish         }
372f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
373f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
374f52c03c1SCarson Labrado                          << " closed gracefully";
375f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
3766eaa1d2fSSunitha Harish         {
377f52c03c1SCarson Labrado             // Now let's try to resend the data
378f52c03c1SCarson Labrado             state = ConnState::retry;
379f52c03c1SCarson Labrado             this->doResolve();
3802a5689a7SAppaRao Puli         }
381bd030d0aSAppaRao Puli     }
382bd030d0aSAppaRao Puli 
383bd030d0aSAppaRao Puli   public:
384f52c03c1SCarson Labrado     explicit ConnectionInfo(boost::asio::io_context& ioc, const std::string& id,
385f52c03c1SCarson Labrado                             const std::string& destIP, const uint16_t destPort,
386f52c03c1SCarson Labrado                             const unsigned int connId) :
387f52c03c1SCarson Labrado         subId(id),
388*244256ccSCarson Labrado         host(destIP), port(destPort), connId(connId), conn(ioc), timer(ioc)
389*244256ccSCarson Labrado     {}
390f52c03c1SCarson Labrado };
391bd030d0aSAppaRao Puli 
392f52c03c1SCarson Labrado class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
393bd030d0aSAppaRao Puli {
394f52c03c1SCarson Labrado   private:
395f52c03c1SCarson Labrado     boost::asio::io_context& ioc;
396f52c03c1SCarson Labrado     const std::string id;
397f52c03c1SCarson Labrado     const std::string destIP;
398f52c03c1SCarson Labrado     const uint16_t destPort;
399f52c03c1SCarson Labrado     std::vector<std::shared_ptr<ConnectionInfo>> connections;
400f52c03c1SCarson Labrado     boost::container::devector<PendingRequest> requestQueue;
401f52c03c1SCarson Labrado 
402f52c03c1SCarson Labrado     friend class HttpClient;
403f52c03c1SCarson Labrado 
404*244256ccSCarson Labrado     // Configure a connections's request, callback, and retry info in
405*244256ccSCarson Labrado     // preparation to begin sending the request
406f52c03c1SCarson Labrado     void setConnProps(ConnectionInfo& conn)
407bd030d0aSAppaRao Puli     {
408f52c03c1SCarson Labrado         if (requestQueue.empty())
409f52c03c1SCarson Labrado         {
410f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR
411f52c03c1SCarson Labrado                 << "setConnProps() should not have been called when requestQueue is empty";
412bd030d0aSAppaRao Puli             return;
413bd030d0aSAppaRao Puli         }
414bd030d0aSAppaRao Puli 
415*244256ccSCarson Labrado         auto nextReq = requestQueue.front();
416*244256ccSCarson Labrado         conn.retryPolicy = std::move(nextReq.retryPolicy);
417*244256ccSCarson Labrado         conn.req = std::move(nextReq.req);
418*244256ccSCarson Labrado         conn.callback = std::move(nextReq.callback);
419f52c03c1SCarson Labrado 
420f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
421f52c03c1SCarson Labrado                          << ":" << std::to_string(conn.port)
422f52c03c1SCarson Labrado                          << ", id: " << std::to_string(conn.connId)
423f52c03c1SCarson Labrado                          << ", retry policy is \"" << conn.retryPolicy.name
424f52c03c1SCarson Labrado                          << "\"";
425f52c03c1SCarson Labrado 
426f52c03c1SCarson Labrado         // We can remove the request from the queue at this point
427f52c03c1SCarson Labrado         requestQueue.pop_front();
428f52c03c1SCarson Labrado     }
429f52c03c1SCarson Labrado 
430f52c03c1SCarson Labrado     // Configures a connection to use the specific retry policy.
431f52c03c1SCarson Labrado     inline void setConnRetryPolicy(ConnectionInfo& conn,
432f52c03c1SCarson Labrado                                    const RetryPolicyData& retryPolicy)
4332a5689a7SAppaRao Puli     {
434f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
435f52c03c1SCarson Labrado                          << ", id: " << std::to_string(conn.connId)
436f52c03c1SCarson Labrado                          << " using retry policy \"" << retryPolicy.name
437f52c03c1SCarson Labrado                          << "\"";
438f52c03c1SCarson Labrado 
439f52c03c1SCarson Labrado         conn.retryPolicy = retryPolicy;
440f52c03c1SCarson Labrado     }
441f52c03c1SCarson Labrado 
442f52c03c1SCarson Labrado     // Gets called as part of callback after request is sent
443f52c03c1SCarson Labrado     // Reuses the connection if there are any requests waiting to be sent
444f52c03c1SCarson Labrado     // Otherwise closes the connection if it is not a keep-alive
445f52c03c1SCarson Labrado     void sendNext(bool keepAlive, uint32_t connId)
446f52c03c1SCarson Labrado     {
447f52c03c1SCarson Labrado         auto conn = connections[connId];
448f52c03c1SCarson Labrado         // Reuse the connection to send the next request in the queue
449f52c03c1SCarson Labrado         if (!requestQueue.empty())
450f52c03c1SCarson Labrado         {
451f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
452f52c03c1SCarson Labrado                              << " requests remaining in queue for " << destIP
453f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort)
454f52c03c1SCarson Labrado                              << ", reusing connnection "
455f52c03c1SCarson Labrado                              << std::to_string(connId);
456f52c03c1SCarson Labrado 
457f52c03c1SCarson Labrado             setConnProps(*conn);
458f52c03c1SCarson Labrado 
459f52c03c1SCarson Labrado             if (keepAlive)
460f52c03c1SCarson Labrado             {
461f52c03c1SCarson Labrado                 conn->sendMessage();
4622a5689a7SAppaRao Puli             }
4632a5689a7SAppaRao Puli             else
4642a5689a7SAppaRao Puli             {
465f52c03c1SCarson Labrado                 // Server is not keep-alive enabled so we need to close the
466f52c03c1SCarson Labrado                 // connection and then start over from resolve
467f52c03c1SCarson Labrado                 conn->doClose();
468f52c03c1SCarson Labrado                 conn->doResolve();
469f52c03c1SCarson Labrado             }
470f52c03c1SCarson Labrado             return;
471f52c03c1SCarson Labrado         }
472f52c03c1SCarson Labrado 
473f52c03c1SCarson Labrado         // No more messages to send so close the connection if necessary
474f52c03c1SCarson Labrado         if (keepAlive)
475f52c03c1SCarson Labrado         {
476f52c03c1SCarson Labrado             conn->state = ConnState::idle;
477f52c03c1SCarson Labrado         }
478f52c03c1SCarson Labrado         else
479f52c03c1SCarson Labrado         {
480f52c03c1SCarson Labrado             // Abort the connection since server is not keep-alive enabled
481f52c03c1SCarson Labrado             conn->state = ConnState::abortConnection;
482f52c03c1SCarson Labrado             conn->doClose();
4832a5689a7SAppaRao Puli         }
484bd030d0aSAppaRao Puli     }
485bd030d0aSAppaRao Puli 
486*244256ccSCarson Labrado     void sendData(std::string& data, const std::string& destUri,
487*244256ccSCarson Labrado                   const boost::beast::http::fields& httpHeader,
488*244256ccSCarson Labrado                   const boost::beast::http::verb verb,
489*244256ccSCarson Labrado                   const RetryPolicyData& retryPolicy,
490039a47e3SCarson Labrado                   std::function<void(Response&)>& resHandler)
491fe44eb0bSAyushi Smriti     {
492f52c03c1SCarson Labrado         std::weak_ptr<ConnectionPool> weakSelf = weak_from_this();
493f52c03c1SCarson Labrado 
494f52c03c1SCarson Labrado         // Callback to be called once the request has been sent
495039a47e3SCarson Labrado         auto cb = [weakSelf, resHandler](bool keepAlive, uint32_t connId,
496039a47e3SCarson Labrado                                          Response& res) {
497039a47e3SCarson Labrado             // Allow provided callback to perform additional processing of the
498039a47e3SCarson Labrado             // request
499039a47e3SCarson Labrado             resHandler(res);
500039a47e3SCarson Labrado 
501f52c03c1SCarson Labrado             // If requests remain in the queue then we want to reuse this
502f52c03c1SCarson Labrado             // connection to send the next request
503f52c03c1SCarson Labrado             std::shared_ptr<ConnectionPool> self = weakSelf.lock();
504f52c03c1SCarson Labrado             if (!self)
505f52c03c1SCarson Labrado             {
506f52c03c1SCarson Labrado                 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
507f52c03c1SCarson Labrado                 return;
508fe44eb0bSAyushi Smriti             }
509fe44eb0bSAyushi Smriti 
510f52c03c1SCarson Labrado             self->sendNext(keepAlive, connId);
511f52c03c1SCarson Labrado         };
512f52c03c1SCarson Labrado 
513*244256ccSCarson Labrado         // Construct the request to be sent
514*244256ccSCarson Labrado         boost::beast::http::request<boost::beast::http::string_body> thisReq(
515*244256ccSCarson Labrado             verb, destUri, 11, "", httpHeader);
516*244256ccSCarson Labrado         thisReq.set(boost::beast::http::field::host, destIP);
517*244256ccSCarson Labrado         thisReq.keep_alive(true);
518*244256ccSCarson Labrado         thisReq.body() = std::move(data);
519*244256ccSCarson Labrado         thisReq.prepare_payload();
520*244256ccSCarson Labrado 
521f52c03c1SCarson Labrado         // Reuse an existing connection if one is available
522f52c03c1SCarson Labrado         for (unsigned int i = 0; i < connections.size(); i++)
523fe44eb0bSAyushi Smriti         {
524f52c03c1SCarson Labrado             auto conn = connections[i];
525f52c03c1SCarson Labrado             if ((conn->state == ConnState::idle) ||
526f52c03c1SCarson Labrado                 (conn->state == ConnState::initialized) ||
527f52c03c1SCarson Labrado                 (conn->state == ConnState::closed))
528f52c03c1SCarson Labrado             {
529*244256ccSCarson Labrado                 conn->req = std::move(thisReq);
530f52c03c1SCarson Labrado                 conn->callback = std::move(cb);
531f52c03c1SCarson Labrado                 setConnRetryPolicy(*conn, retryPolicy);
532f52c03c1SCarson Labrado                 std::string commonMsg = std::to_string(i) + " from pool " +
533f52c03c1SCarson Labrado                                         destIP + ":" + std::to_string(destPort);
534f52c03c1SCarson Labrado 
535f52c03c1SCarson Labrado                 if (conn->state == ConnState::idle)
536f52c03c1SCarson Labrado                 {
537f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Grabbing idle connection "
538f52c03c1SCarson Labrado                                      << commonMsg;
539f52c03c1SCarson Labrado                     conn->sendMessage();
540f52c03c1SCarson Labrado                 }
541f52c03c1SCarson Labrado                 else
542f52c03c1SCarson Labrado                 {
543f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Reusing existing connection "
544f52c03c1SCarson Labrado                                      << commonMsg;
545f52c03c1SCarson Labrado                     conn->doResolve();
546f52c03c1SCarson Labrado                 }
547f52c03c1SCarson Labrado                 return;
548f52c03c1SCarson Labrado             }
549f52c03c1SCarson Labrado         }
550f52c03c1SCarson Labrado 
551f52c03c1SCarson Labrado         // All connections in use so create a new connection or add request to
552f52c03c1SCarson Labrado         // the queue
553f52c03c1SCarson Labrado         if (connections.size() < maxPoolSize)
554f52c03c1SCarson Labrado         {
555f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
556f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort);
557f52c03c1SCarson Labrado             auto conn = addConnection();
558*244256ccSCarson Labrado             conn->req = std::move(thisReq);
559f52c03c1SCarson Labrado             conn->callback = std::move(cb);
560f52c03c1SCarson Labrado             setConnRetryPolicy(*conn, retryPolicy);
561f52c03c1SCarson Labrado             conn->doResolve();
562f52c03c1SCarson Labrado         }
563f52c03c1SCarson Labrado         else if (requestQueue.size() < maxRequestQueueSize)
564f52c03c1SCarson Labrado         {
565f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
566*244256ccSCarson Labrado             requestQueue.emplace_back(std::move(thisReq), std::move(cb),
567f52c03c1SCarson Labrado                                       retryPolicy);
568f52c03c1SCarson Labrado         }
569f52c03c1SCarson Labrado         else
570f52c03c1SCarson Labrado         {
571f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
572f52c03c1SCarson Labrado                              << " request queue full.  Dropping request.";
573f52c03c1SCarson Labrado         }
574f52c03c1SCarson Labrado     }
575f52c03c1SCarson Labrado 
576f52c03c1SCarson Labrado     std::shared_ptr<ConnectionInfo>& addConnection()
577f52c03c1SCarson Labrado     {
578f52c03c1SCarson Labrado         unsigned int newId = static_cast<unsigned int>(connections.size());
579f52c03c1SCarson Labrado 
580*244256ccSCarson Labrado         auto& ret = connections.emplace_back(
581*244256ccSCarson Labrado             std::make_shared<ConnectionInfo>(ioc, id, destIP, destPort, newId));
582f52c03c1SCarson Labrado 
583f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Added connection "
584f52c03c1SCarson Labrado                          << std::to_string(connections.size() - 1)
585f52c03c1SCarson Labrado                          << " to pool " << destIP << ":"
586f52c03c1SCarson Labrado                          << std::to_string(destPort);
587f52c03c1SCarson Labrado 
588f52c03c1SCarson Labrado         return ret;
589f52c03c1SCarson Labrado     }
590f52c03c1SCarson Labrado 
591f52c03c1SCarson Labrado   public:
592f52c03c1SCarson Labrado     explicit ConnectionPool(boost::asio::io_context& ioc, const std::string& id,
593*244256ccSCarson Labrado                             const std::string& destIP,
594*244256ccSCarson Labrado                             const uint16_t destPort) :
595f52c03c1SCarson Labrado         ioc(ioc),
596*244256ccSCarson Labrado         id(id), destIP(destIP), destPort(destPort)
597f52c03c1SCarson Labrado     {
598f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
599f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
600f52c03c1SCarson Labrado                          << std::to_string(destPort);
601f52c03c1SCarson Labrado 
602f52c03c1SCarson Labrado         // Initialize the pool with a single connection
603f52c03c1SCarson Labrado         addConnection();
604fe44eb0bSAyushi Smriti     }
605bd030d0aSAppaRao Puli };
606bd030d0aSAppaRao Puli 
607f52c03c1SCarson Labrado class HttpClient
608f52c03c1SCarson Labrado {
609f52c03c1SCarson Labrado   private:
610f52c03c1SCarson Labrado     std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
611f52c03c1SCarson Labrado         connectionPools;
612f52c03c1SCarson Labrado     boost::asio::io_context& ioc =
613f52c03c1SCarson Labrado         crow::connections::systemBus->get_io_context();
614f52c03c1SCarson Labrado     std::unordered_map<std::string, RetryPolicyData> retryInfo;
615f52c03c1SCarson Labrado     HttpClient() = default;
616f52c03c1SCarson Labrado 
617039a47e3SCarson Labrado     // Used as a dummy callback by sendData() in order to call
618039a47e3SCarson Labrado     // sendDataWithCallback()
619039a47e3SCarson Labrado     static void genericResHandler(Response& res)
620039a47e3SCarson Labrado     {
621039a47e3SCarson Labrado         BMCWEB_LOG_DEBUG << "Response handled with return code: "
622039a47e3SCarson Labrado                          << std::to_string(res.resultInt());
623039a47e3SCarson Labrado     };
624039a47e3SCarson Labrado 
625f52c03c1SCarson Labrado   public:
626f52c03c1SCarson Labrado     HttpClient(const HttpClient&) = delete;
627f52c03c1SCarson Labrado     HttpClient& operator=(const HttpClient&) = delete;
628f52c03c1SCarson Labrado     HttpClient(HttpClient&&) = delete;
629f52c03c1SCarson Labrado     HttpClient& operator=(HttpClient&&) = delete;
630f52c03c1SCarson Labrado     ~HttpClient() = default;
631f52c03c1SCarson Labrado 
632f52c03c1SCarson Labrado     static HttpClient& getInstance()
633f52c03c1SCarson Labrado     {
634f52c03c1SCarson Labrado         static HttpClient handler;
635f52c03c1SCarson Labrado         return handler;
636f52c03c1SCarson Labrado     }
637f52c03c1SCarson Labrado 
638039a47e3SCarson Labrado     // Send a request to destIP:destPort where additional processing of the
639039a47e3SCarson Labrado     // result is not required
640f52c03c1SCarson Labrado     void sendData(std::string& data, const std::string& id,
641f52c03c1SCarson Labrado                   const std::string& destIP, const uint16_t destPort,
642f52c03c1SCarson Labrado                   const std::string& destUri,
643f52c03c1SCarson Labrado                   const boost::beast::http::fields& httpHeader,
644*244256ccSCarson Labrado                   const boost::beast::http::verb verb,
645*244256ccSCarson Labrado                   const std::string& retryPolicyName)
646f52c03c1SCarson Labrado     {
647039a47e3SCarson Labrado         std::function<void(Response&)> cb = genericResHandler;
648039a47e3SCarson Labrado         sendDataWithCallback(data, id, destIP, destPort, destUri, httpHeader,
649*244256ccSCarson Labrado                              verb, retryPolicyName, cb);
650039a47e3SCarson Labrado     }
651039a47e3SCarson Labrado 
652039a47e3SCarson Labrado     // Send request to destIP:destPort and use the provided callback to
653039a47e3SCarson Labrado     // handle the response
654039a47e3SCarson Labrado     void sendDataWithCallback(std::string& data, const std::string& id,
655039a47e3SCarson Labrado                               const std::string& destIP,
656039a47e3SCarson Labrado                               const uint16_t destPort,
657039a47e3SCarson Labrado                               const std::string& destUri,
658039a47e3SCarson Labrado                               const boost::beast::http::fields& httpHeader,
659*244256ccSCarson Labrado                               const boost::beast::http::verb verb,
660*244256ccSCarson Labrado                               const std::string& retryPolicyName,
661039a47e3SCarson Labrado                               std::function<void(Response&)>& resHandler)
662039a47e3SCarson Labrado     {
663f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
664f52c03c1SCarson Labrado         // Use nullptr to avoid creating a ConnectionPool each time
665f52c03c1SCarson Labrado         auto result = connectionPools.try_emplace(clientKey, nullptr);
666f52c03c1SCarson Labrado         if (result.second)
667f52c03c1SCarson Labrado         {
668f52c03c1SCarson Labrado             // Now actually create the ConnectionPool shared_ptr since it does
669f52c03c1SCarson Labrado             // not already exist
670*244256ccSCarson Labrado             result.first->second =
671*244256ccSCarson Labrado                 std::make_shared<ConnectionPool>(ioc, id, destIP, destPort);
672f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
673f52c03c1SCarson Labrado         }
674f52c03c1SCarson Labrado         else
675f52c03c1SCarson Labrado         {
676f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Using existing connection pool for "
677f52c03c1SCarson Labrado                              << clientKey;
678f52c03c1SCarson Labrado         }
679f52c03c1SCarson Labrado 
680f52c03c1SCarson Labrado         // Get the associated retry policy
681f52c03c1SCarson Labrado         auto policy = retryInfo.try_emplace(retryPolicyName);
682f52c03c1SCarson Labrado         if (policy.second)
683f52c03c1SCarson Labrado         {
684f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
685f52c03c1SCarson Labrado                              << "\" with default values";
686f52c03c1SCarson Labrado             policy.first->second.name = retryPolicyName;
687f52c03c1SCarson Labrado         }
688f52c03c1SCarson Labrado 
689f52c03c1SCarson Labrado         // Send the data using either the existing connection pool or the newly
690f52c03c1SCarson Labrado         // created connection pool
691*244256ccSCarson Labrado         result.first->second->sendData(data, destUri, httpHeader, verb,
692*244256ccSCarson Labrado                                        policy.first->second, resHandler);
693f52c03c1SCarson Labrado     }
694f52c03c1SCarson Labrado 
695f52c03c1SCarson Labrado     void setRetryConfig(const uint32_t retryAttempts,
696f52c03c1SCarson Labrado                         const uint32_t retryTimeoutInterval,
697f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
698f52c03c1SCarson Labrado     {
699f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
700f52c03c1SCarson Labrado         // the given retryPolicyName
701f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
702f52c03c1SCarson Labrado         if (result.second)
703f52c03c1SCarson Labrado         {
704f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
705f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
706f52c03c1SCarson Labrado             result.first->second.name = retryPolicyName;
707f52c03c1SCarson Labrado         }
708f52c03c1SCarson Labrado         else
709f52c03c1SCarson Labrado         {
710f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
711f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
712f52c03c1SCarson Labrado         }
713f52c03c1SCarson Labrado 
714f52c03c1SCarson Labrado         result.first->second.maxRetryAttempts = retryAttempts;
715f52c03c1SCarson Labrado         result.first->second.retryIntervalSecs =
716f52c03c1SCarson Labrado             std::chrono::seconds(retryTimeoutInterval);
717f52c03c1SCarson Labrado     }
718f52c03c1SCarson Labrado 
719f52c03c1SCarson Labrado     void setRetryPolicy(const std::string& retryPolicy,
720f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
721f52c03c1SCarson Labrado     {
722f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
723f52c03c1SCarson Labrado         // the given retryPolicyName
724f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
725f52c03c1SCarson Labrado         if (result.second)
726f52c03c1SCarson Labrado         {
727f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
728f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
729f52c03c1SCarson Labrado             result.first->second.name = retryPolicyName;
730f52c03c1SCarson Labrado         }
731f52c03c1SCarson Labrado         else
732f52c03c1SCarson Labrado         {
733f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
734f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
735f52c03c1SCarson Labrado         }
736f52c03c1SCarson Labrado 
737f52c03c1SCarson Labrado         result.first->second.retryPolicyAction = retryPolicy;
738f52c03c1SCarson Labrado     }
739f52c03c1SCarson Labrado };
740bd030d0aSAppaRao Puli } // namespace crow
741