xref: /openbmc/bmcweb/http/http_client.hpp (revision 4ee8e211)
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 {
75244256ccSCarson 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(
79244256ccSCarson 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) :
82244256ccSCarson 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));
156002d39b4SEd Tanous         conn.async_connect(endpointList,
157002d39b4SEd Tanous                            [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             {
162002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
163002d39b4SEd Tanous                                  << ":" << std::to_string(endpoint.port())
164f52c03c1SCarson Labrado                                  << ", id: " << std::to_string(self->connId)
1652a5689a7SAppaRao Puli                                  << " failed: " << ec.message();
1662a5689a7SAppaRao Puli                 self->state = ConnState::connectFailed;
167f52c03c1SCarson Labrado                 self->waitAndRetry();
1682a5689a7SAppaRao Puli                 return;
1692a5689a7SAppaRao Puli             }
170f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG
171f52c03c1SCarson Labrado                 << "Connected to: " << endpoint.address().to_string() << ":"
172f52c03c1SCarson Labrado                 << std::to_string(endpoint.port())
173f52c03c1SCarson Labrado                 << ", id: " << std::to_string(self->connId);
1746eaa1d2fSSunitha Harish             self->state = ConnState::connected;
175f52c03c1SCarson Labrado             self->sendMessage();
1762a5689a7SAppaRao Puli         });
1772a5689a7SAppaRao Puli     }
1782a5689a7SAppaRao Puli 
179f52c03c1SCarson Labrado     void sendMessage()
1802a5689a7SAppaRao Puli     {
1812a5689a7SAppaRao Puli         state = ConnState::sendInProgress;
1822a5689a7SAppaRao Puli 
183bd030d0aSAppaRao Puli         // Set a timeout on the operation
184bd030d0aSAppaRao Puli         conn.expires_after(std::chrono::seconds(30));
185bd030d0aSAppaRao Puli 
186bd030d0aSAppaRao Puli         // Send the HTTP request to the remote host
187bd030d0aSAppaRao Puli         boost::beast::http::async_write(
188bd030d0aSAppaRao Puli             conn, req,
1892a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
190bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
191bd030d0aSAppaRao Puli             if (ec)
192bd030d0aSAppaRao Puli             {
193002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
1942a5689a7SAppaRao Puli                 self->state = ConnState::sendFailed;
195f52c03c1SCarson Labrado                 self->waitAndRetry();
196bd030d0aSAppaRao Puli                 return;
197bd030d0aSAppaRao Puli             }
198bd030d0aSAppaRao Puli             BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
199bd030d0aSAppaRao Puli                              << bytesTransferred;
200bd030d0aSAppaRao Puli             boost::ignore_unused(bytesTransferred);
201bd030d0aSAppaRao Puli 
2022a5689a7SAppaRao Puli             self->recvMessage();
203bd030d0aSAppaRao Puli             });
204bd030d0aSAppaRao Puli     }
205bd030d0aSAppaRao Puli 
206bd030d0aSAppaRao Puli     void recvMessage()
207bd030d0aSAppaRao Puli     {
2086eaa1d2fSSunitha Harish         state = ConnState::recvInProgress;
2096eaa1d2fSSunitha Harish 
2106eaa1d2fSSunitha Harish         parser.emplace(std::piecewise_construct, std::make_tuple());
2116eaa1d2fSSunitha Harish         parser->body_limit(httpReadBodyLimit);
2126eaa1d2fSSunitha Harish 
213bd030d0aSAppaRao Puli         // Receive the HTTP response
214bd030d0aSAppaRao Puli         boost::beast::http::async_read(
2156eaa1d2fSSunitha Harish             conn, buffer, *parser,
2162a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
217bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
218bd030d0aSAppaRao Puli             if (ec)
219bd030d0aSAppaRao Puli             {
220002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
2212a5689a7SAppaRao Puli                 self->state = ConnState::recvFailed;
222f52c03c1SCarson Labrado                 self->waitAndRetry();
223bd030d0aSAppaRao Puli                 return;
224bd030d0aSAppaRao Puli             }
225bd030d0aSAppaRao Puli             BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
226bd030d0aSAppaRao Puli                              << bytesTransferred;
2276eaa1d2fSSunitha Harish             BMCWEB_LOG_DEBUG << "recvMessage() data: "
2288cc8edecSEd Tanous                              << self->parser->get().body();
229bd030d0aSAppaRao Puli 
2306eaa1d2fSSunitha Harish             unsigned int respCode = self->parser->get().result_int();
2316eaa1d2fSSunitha Harish             BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
2326eaa1d2fSSunitha Harish                              << respCode;
2336eaa1d2fSSunitha Harish 
2346eaa1d2fSSunitha Harish             // 2XX response is considered to be successful
2356eaa1d2fSSunitha Harish             if ((respCode < 200) || (respCode >= 300))
2366eaa1d2fSSunitha Harish             {
2376eaa1d2fSSunitha Harish                 // The listener failed to receive the Sent-Event
238002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
2397adb85acSSunitha Harish                                     "receive Sent-Event. Header Response Code: "
2407adb85acSSunitha Harish                                  << respCode;
2416eaa1d2fSSunitha Harish                 self->state = ConnState::recvFailed;
242f52c03c1SCarson Labrado                 self->waitAndRetry();
2436eaa1d2fSSunitha Harish                 return;
2446eaa1d2fSSunitha Harish             }
245bd030d0aSAppaRao Puli 
246f52c03c1SCarson Labrado             // Send is successful
247f52c03c1SCarson Labrado             // Reset the counter just in case this was after retrying
248f52c03c1SCarson Labrado             self->retryCount = 0;
2496eaa1d2fSSunitha Harish 
2506eaa1d2fSSunitha Harish             // Keep the connection alive if server supports it
2516eaa1d2fSSunitha Harish             // Else close the connection
2526eaa1d2fSSunitha Harish             BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
2536eaa1d2fSSunitha Harish                              << self->parser->keep_alive();
2546eaa1d2fSSunitha Harish 
255039a47e3SCarson Labrado             // Copy the response into a Response object so that it can be
256039a47e3SCarson Labrado             // processed by the callback function.
257039a47e3SCarson Labrado             self->res.clear();
258039a47e3SCarson Labrado             self->res.stringResponse = self->parser->release();
259002d39b4SEd Tanous             self->callback(self->parser->keep_alive(), self->connId, self->res);
260bd030d0aSAppaRao Puli             });
261bd030d0aSAppaRao Puli     }
262bd030d0aSAppaRao Puli 
2636eaa1d2fSSunitha Harish     void waitAndRetry()
264bd030d0aSAppaRao Puli     {
265f52c03c1SCarson Labrado         if (retryCount >= retryPolicy.maxRetryAttempts)
2662a5689a7SAppaRao Puli         {
2676eaa1d2fSSunitha Harish             BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
268f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Retry policy: "
269f52c03c1SCarson Labrado                              << retryPolicy.retryPolicyAction;
270039a47e3SCarson Labrado 
271039a47e3SCarson Labrado             // We want to return a 502 to indicate there was an error with the
272039a47e3SCarson Labrado             // external server
273039a47e3SCarson Labrado             res.clear();
274039a47e3SCarson Labrado             redfish::messages::operationFailed(res);
275039a47e3SCarson Labrado 
276f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
277fe44eb0bSAyushi Smriti             {
278fe44eb0bSAyushi Smriti                 // TODO: delete subscription
279fe44eb0bSAyushi Smriti                 state = ConnState::terminated;
280039a47e3SCarson Labrado                 callback(false, connId, res);
281fe44eb0bSAyushi Smriti             }
282f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "SuspendRetries")
283fe44eb0bSAyushi Smriti             {
2842a5689a7SAppaRao Puli                 state = ConnState::suspended;
285039a47e3SCarson Labrado                 callback(false, connId, res);
2862a5689a7SAppaRao Puli             }
2876eaa1d2fSSunitha Harish             // Reset the retrycount to zero so that client can try connecting
2886eaa1d2fSSunitha Harish             // again if needed
289fe44eb0bSAyushi Smriti             retryCount = 0;
2902a5689a7SAppaRao Puli             return;
2912a5689a7SAppaRao Puli         }
2922a5689a7SAppaRao Puli 
293fe44eb0bSAyushi Smriti         if (runningTimer)
294fe44eb0bSAyushi Smriti         {
295fe44eb0bSAyushi Smriti             BMCWEB_LOG_DEBUG << "Retry timer is already running.";
296fe44eb0bSAyushi Smriti             return;
297fe44eb0bSAyushi Smriti         }
298fe44eb0bSAyushi Smriti         runningTimer = true;
299fe44eb0bSAyushi Smriti 
3002a5689a7SAppaRao Puli         retryCount++;
301fe44eb0bSAyushi Smriti 
302f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Attempt retry after "
303f52c03c1SCarson Labrado                          << std::to_string(
304f52c03c1SCarson Labrado                                 retryPolicy.retryIntervalSecs.count())
305fe44eb0bSAyushi Smriti                          << " seconds. RetryCount = " << retryCount;
306f52c03c1SCarson Labrado         timer.expires_after(retryPolicy.retryIntervalSecs);
307cb13a392SEd Tanous         timer.async_wait(
308f52c03c1SCarson Labrado             [self(shared_from_this())](const boost::system::error_code ec) {
3096eaa1d2fSSunitha Harish             if (ec == boost::asio::error::operation_aborted)
3106eaa1d2fSSunitha Harish             {
3116eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG
3126eaa1d2fSSunitha Harish                     << "async_wait failed since the operation is aborted"
3136eaa1d2fSSunitha Harish                     << ec.message();
3146eaa1d2fSSunitha Harish             }
3156eaa1d2fSSunitha Harish             else if (ec)
3166eaa1d2fSSunitha Harish             {
3176eaa1d2fSSunitha Harish                 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
3186eaa1d2fSSunitha Harish                 // Ignore the error and continue the retry loop to attempt
3196eaa1d2fSSunitha Harish                 // sending the event as per the retry policy
3206eaa1d2fSSunitha Harish             }
321fe44eb0bSAyushi Smriti             self->runningTimer = false;
3226eaa1d2fSSunitha Harish 
323f52c03c1SCarson Labrado             // Let's close the connection and restart from resolve.
324f52c03c1SCarson Labrado             self->doCloseAndRetry();
325fe44eb0bSAyushi Smriti         });
3262a5689a7SAppaRao Puli     }
3272a5689a7SAppaRao Puli 
328f52c03c1SCarson Labrado     void doClose()
329fe44eb0bSAyushi Smriti     {
330f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
331f52c03c1SCarson Labrado         boost::beast::error_code ec;
332f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
333f52c03c1SCarson Labrado         conn.close();
334f52c03c1SCarson Labrado 
335f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
336f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
3372a5689a7SAppaRao Puli         {
338f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
339f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
340f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3416eaa1d2fSSunitha Harish             return;
3426eaa1d2fSSunitha Harish         }
343f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
344f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
345f52c03c1SCarson Labrado                          << " closed gracefully";
346f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
347f52c03c1SCarson Labrado         {
348f52c03c1SCarson Labrado             state = ConnState::closed;
3496eaa1d2fSSunitha Harish         }
3506eaa1d2fSSunitha Harish     }
351f52c03c1SCarson Labrado 
352f52c03c1SCarson Labrado     void doCloseAndRetry()
35392a74e56SAppaRao Puli     {
354f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
355f52c03c1SCarson Labrado         boost::beast::error_code ec;
356f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
357f52c03c1SCarson Labrado         conn.close();
358f52c03c1SCarson Labrado 
359f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
360f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
36192a74e56SAppaRao Puli         {
362f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
363f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
364f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3656eaa1d2fSSunitha Harish             return;
3666eaa1d2fSSunitha Harish         }
367f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
368f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
369f52c03c1SCarson Labrado                          << " closed gracefully";
370f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
3716eaa1d2fSSunitha Harish         {
372f52c03c1SCarson Labrado             // Now let's try to resend the data
373f52c03c1SCarson Labrado             state = ConnState::retry;
374f52c03c1SCarson Labrado             this->doResolve();
3752a5689a7SAppaRao Puli         }
376bd030d0aSAppaRao Puli     }
377bd030d0aSAppaRao Puli 
378bd030d0aSAppaRao Puli   public:
379f52c03c1SCarson Labrado     explicit ConnectionInfo(boost::asio::io_context& ioc, const std::string& id,
380f52c03c1SCarson Labrado                             const std::string& destIP, const uint16_t destPort,
381f52c03c1SCarson Labrado                             const unsigned int connId) :
382f52c03c1SCarson Labrado         subId(id),
383244256ccSCarson Labrado         host(destIP), port(destPort), connId(connId), conn(ioc), timer(ioc)
384244256ccSCarson Labrado     {}
385f52c03c1SCarson Labrado };
386bd030d0aSAppaRao Puli 
387f52c03c1SCarson Labrado class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
388bd030d0aSAppaRao Puli {
389f52c03c1SCarson Labrado   private:
390f52c03c1SCarson Labrado     boost::asio::io_context& ioc;
391f52c03c1SCarson Labrado     const std::string id;
392f52c03c1SCarson Labrado     const std::string destIP;
393f52c03c1SCarson Labrado     const uint16_t destPort;
394f52c03c1SCarson Labrado     std::vector<std::shared_ptr<ConnectionInfo>> connections;
395f52c03c1SCarson Labrado     boost::container::devector<PendingRequest> requestQueue;
396f52c03c1SCarson Labrado 
397f52c03c1SCarson Labrado     friend class HttpClient;
398f52c03c1SCarson Labrado 
399244256ccSCarson Labrado     // Configure a connections's request, callback, and retry info in
400244256ccSCarson Labrado     // preparation to begin sending the request
401f52c03c1SCarson Labrado     void setConnProps(ConnectionInfo& conn)
402bd030d0aSAppaRao Puli     {
403f52c03c1SCarson Labrado         if (requestQueue.empty())
404f52c03c1SCarson Labrado         {
405f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR
406f52c03c1SCarson Labrado                 << "setConnProps() should not have been called when requestQueue is empty";
407bd030d0aSAppaRao Puli             return;
408bd030d0aSAppaRao Puli         }
409bd030d0aSAppaRao Puli 
410244256ccSCarson Labrado         auto nextReq = requestQueue.front();
411244256ccSCarson Labrado         conn.retryPolicy = std::move(nextReq.retryPolicy);
412244256ccSCarson Labrado         conn.req = std::move(nextReq.req);
413244256ccSCarson Labrado         conn.callback = std::move(nextReq.callback);
414f52c03c1SCarson Labrado 
415f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
416f52c03c1SCarson Labrado                          << ":" << std::to_string(conn.port)
417f52c03c1SCarson Labrado                          << ", id: " << std::to_string(conn.connId)
418f52c03c1SCarson Labrado                          << ", retry policy is \"" << conn.retryPolicy.name
419f52c03c1SCarson Labrado                          << "\"";
420f52c03c1SCarson Labrado 
421f52c03c1SCarson Labrado         // We can remove the request from the queue at this point
422f52c03c1SCarson Labrado         requestQueue.pop_front();
423f52c03c1SCarson Labrado     }
424f52c03c1SCarson Labrado 
425f52c03c1SCarson Labrado     // Configures a connection to use the specific retry policy.
426f52c03c1SCarson Labrado     inline void setConnRetryPolicy(ConnectionInfo& conn,
427f52c03c1SCarson Labrado                                    const RetryPolicyData& retryPolicy)
4282a5689a7SAppaRao Puli     {
429f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
430f52c03c1SCarson Labrado                          << ", id: " << std::to_string(conn.connId)
431f52c03c1SCarson Labrado                          << " using retry policy \"" << retryPolicy.name
432f52c03c1SCarson Labrado                          << "\"";
433f52c03c1SCarson Labrado 
434f52c03c1SCarson Labrado         conn.retryPolicy = retryPolicy;
435f52c03c1SCarson Labrado     }
436f52c03c1SCarson Labrado 
437f52c03c1SCarson Labrado     // Gets called as part of callback after request is sent
438f52c03c1SCarson Labrado     // Reuses the connection if there are any requests waiting to be sent
439f52c03c1SCarson Labrado     // Otherwise closes the connection if it is not a keep-alive
440f52c03c1SCarson Labrado     void sendNext(bool keepAlive, uint32_t connId)
441f52c03c1SCarson Labrado     {
442f52c03c1SCarson Labrado         auto conn = connections[connId];
443f52c03c1SCarson Labrado         // Reuse the connection to send the next request in the queue
444f52c03c1SCarson Labrado         if (!requestQueue.empty())
445f52c03c1SCarson Labrado         {
446f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
447f52c03c1SCarson Labrado                              << " requests remaining in queue for " << destIP
448f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort)
449f52c03c1SCarson Labrado                              << ", reusing connnection "
450f52c03c1SCarson Labrado                              << std::to_string(connId);
451f52c03c1SCarson Labrado 
452f52c03c1SCarson Labrado             setConnProps(*conn);
453f52c03c1SCarson Labrado 
454f52c03c1SCarson Labrado             if (keepAlive)
455f52c03c1SCarson Labrado             {
456f52c03c1SCarson Labrado                 conn->sendMessage();
4572a5689a7SAppaRao Puli             }
4582a5689a7SAppaRao Puli             else
4592a5689a7SAppaRao Puli             {
460f52c03c1SCarson Labrado                 // Server is not keep-alive enabled so we need to close the
461f52c03c1SCarson Labrado                 // connection and then start over from resolve
462f52c03c1SCarson Labrado                 conn->doClose();
463f52c03c1SCarson Labrado                 conn->doResolve();
464f52c03c1SCarson Labrado             }
465f52c03c1SCarson Labrado             return;
466f52c03c1SCarson Labrado         }
467f52c03c1SCarson Labrado 
468f52c03c1SCarson Labrado         // No more messages to send so close the connection if necessary
469f52c03c1SCarson Labrado         if (keepAlive)
470f52c03c1SCarson Labrado         {
471f52c03c1SCarson Labrado             conn->state = ConnState::idle;
472f52c03c1SCarson Labrado         }
473f52c03c1SCarson Labrado         else
474f52c03c1SCarson Labrado         {
475f52c03c1SCarson Labrado             // Abort the connection since server is not keep-alive enabled
476f52c03c1SCarson Labrado             conn->state = ConnState::abortConnection;
477f52c03c1SCarson Labrado             conn->doClose();
4782a5689a7SAppaRao Puli         }
479bd030d0aSAppaRao Puli     }
480bd030d0aSAppaRao Puli 
481244256ccSCarson Labrado     void sendData(std::string& data, const std::string& destUri,
482244256ccSCarson Labrado                   const boost::beast::http::fields& httpHeader,
483244256ccSCarson Labrado                   const boost::beast::http::verb verb,
484244256ccSCarson Labrado                   const RetryPolicyData& retryPolicy,
485039a47e3SCarson Labrado                   std::function<void(Response&)>& resHandler)
486fe44eb0bSAyushi Smriti     {
487f52c03c1SCarson Labrado         std::weak_ptr<ConnectionPool> weakSelf = weak_from_this();
488f52c03c1SCarson Labrado 
489f52c03c1SCarson Labrado         // Callback to be called once the request has been sent
490039a47e3SCarson Labrado         auto cb = [weakSelf, resHandler](bool keepAlive, uint32_t connId,
491039a47e3SCarson Labrado                                          Response& res) {
492039a47e3SCarson Labrado             // Allow provided callback to perform additional processing of the
493039a47e3SCarson Labrado             // request
494039a47e3SCarson Labrado             resHandler(res);
495039a47e3SCarson Labrado 
496f52c03c1SCarson Labrado             // If requests remain in the queue then we want to reuse this
497f52c03c1SCarson Labrado             // connection to send the next request
498f52c03c1SCarson Labrado             std::shared_ptr<ConnectionPool> self = weakSelf.lock();
499f52c03c1SCarson Labrado             if (!self)
500f52c03c1SCarson Labrado             {
501f52c03c1SCarson Labrado                 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
502f52c03c1SCarson Labrado                 return;
503fe44eb0bSAyushi Smriti             }
504fe44eb0bSAyushi Smriti 
505f52c03c1SCarson Labrado             self->sendNext(keepAlive, connId);
506f52c03c1SCarson Labrado         };
507f52c03c1SCarson Labrado 
508244256ccSCarson Labrado         // Construct the request to be sent
509244256ccSCarson Labrado         boost::beast::http::request<boost::beast::http::string_body> thisReq(
510244256ccSCarson Labrado             verb, destUri, 11, "", httpHeader);
511244256ccSCarson Labrado         thisReq.set(boost::beast::http::field::host, destIP);
512244256ccSCarson Labrado         thisReq.keep_alive(true);
513244256ccSCarson Labrado         thisReq.body() = std::move(data);
514244256ccSCarson Labrado         thisReq.prepare_payload();
515244256ccSCarson Labrado 
516f52c03c1SCarson Labrado         // Reuse an existing connection if one is available
517f52c03c1SCarson Labrado         for (unsigned int i = 0; i < connections.size(); i++)
518fe44eb0bSAyushi Smriti         {
519f52c03c1SCarson Labrado             auto conn = connections[i];
520f52c03c1SCarson Labrado             if ((conn->state == ConnState::idle) ||
521f52c03c1SCarson Labrado                 (conn->state == ConnState::initialized) ||
522f52c03c1SCarson Labrado                 (conn->state == ConnState::closed))
523f52c03c1SCarson Labrado             {
524244256ccSCarson Labrado                 conn->req = std::move(thisReq);
525f52c03c1SCarson Labrado                 conn->callback = std::move(cb);
526f52c03c1SCarson Labrado                 setConnRetryPolicy(*conn, retryPolicy);
527f52c03c1SCarson Labrado                 std::string commonMsg = std::to_string(i) + " from pool " +
528f52c03c1SCarson Labrado                                         destIP + ":" + std::to_string(destPort);
529f52c03c1SCarson Labrado 
530f52c03c1SCarson Labrado                 if (conn->state == ConnState::idle)
531f52c03c1SCarson Labrado                 {
532f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Grabbing idle connection "
533f52c03c1SCarson Labrado                                      << commonMsg;
534f52c03c1SCarson Labrado                     conn->sendMessage();
535f52c03c1SCarson Labrado                 }
536f52c03c1SCarson Labrado                 else
537f52c03c1SCarson Labrado                 {
538f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Reusing existing connection "
539f52c03c1SCarson Labrado                                      << commonMsg;
540f52c03c1SCarson Labrado                     conn->doResolve();
541f52c03c1SCarson Labrado                 }
542f52c03c1SCarson Labrado                 return;
543f52c03c1SCarson Labrado             }
544f52c03c1SCarson Labrado         }
545f52c03c1SCarson Labrado 
546f52c03c1SCarson Labrado         // All connections in use so create a new connection or add request to
547f52c03c1SCarson Labrado         // the queue
548f52c03c1SCarson Labrado         if (connections.size() < maxPoolSize)
549f52c03c1SCarson Labrado         {
550f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
551f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort);
552f52c03c1SCarson Labrado             auto conn = addConnection();
553244256ccSCarson Labrado             conn->req = std::move(thisReq);
554f52c03c1SCarson Labrado             conn->callback = std::move(cb);
555f52c03c1SCarson Labrado             setConnRetryPolicy(*conn, retryPolicy);
556f52c03c1SCarson Labrado             conn->doResolve();
557f52c03c1SCarson Labrado         }
558f52c03c1SCarson Labrado         else if (requestQueue.size() < maxRequestQueueSize)
559f52c03c1SCarson Labrado         {
560f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
561244256ccSCarson Labrado             requestQueue.emplace_back(std::move(thisReq), std::move(cb),
562f52c03c1SCarson Labrado                                       retryPolicy);
563f52c03c1SCarson Labrado         }
564f52c03c1SCarson Labrado         else
565f52c03c1SCarson Labrado         {
566f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
567f52c03c1SCarson Labrado                              << " request queue full.  Dropping request.";
568f52c03c1SCarson Labrado         }
569f52c03c1SCarson Labrado     }
570f52c03c1SCarson Labrado 
571f52c03c1SCarson Labrado     std::shared_ptr<ConnectionInfo>& addConnection()
572f52c03c1SCarson Labrado     {
573f52c03c1SCarson Labrado         unsigned int newId = static_cast<unsigned int>(connections.size());
574f52c03c1SCarson Labrado 
575244256ccSCarson Labrado         auto& ret = connections.emplace_back(
576244256ccSCarson Labrado             std::make_shared<ConnectionInfo>(ioc, id, destIP, destPort, newId));
577f52c03c1SCarson Labrado 
578f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Added connection "
579f52c03c1SCarson Labrado                          << std::to_string(connections.size() - 1)
580f52c03c1SCarson Labrado                          << " to pool " << destIP << ":"
581f52c03c1SCarson Labrado                          << std::to_string(destPort);
582f52c03c1SCarson Labrado 
583f52c03c1SCarson Labrado         return ret;
584f52c03c1SCarson Labrado     }
585f52c03c1SCarson Labrado 
586f52c03c1SCarson Labrado   public:
587f52c03c1SCarson Labrado     explicit ConnectionPool(boost::asio::io_context& ioc, const std::string& id,
588244256ccSCarson Labrado                             const std::string& destIP,
589244256ccSCarson Labrado                             const uint16_t destPort) :
590f52c03c1SCarson Labrado         ioc(ioc),
591244256ccSCarson Labrado         id(id), destIP(destIP), destPort(destPort)
592f52c03c1SCarson Labrado     {
593f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
594f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
595f52c03c1SCarson Labrado                          << std::to_string(destPort);
596f52c03c1SCarson Labrado 
597f52c03c1SCarson Labrado         // Initialize the pool with a single connection
598f52c03c1SCarson Labrado         addConnection();
599fe44eb0bSAyushi Smriti     }
600bd030d0aSAppaRao Puli };
601bd030d0aSAppaRao Puli 
602f52c03c1SCarson Labrado class HttpClient
603f52c03c1SCarson Labrado {
604f52c03c1SCarson Labrado   private:
605f52c03c1SCarson Labrado     std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
606f52c03c1SCarson Labrado         connectionPools;
607f52c03c1SCarson Labrado     boost::asio::io_context& ioc =
608f52c03c1SCarson Labrado         crow::connections::systemBus->get_io_context();
609f52c03c1SCarson Labrado     std::unordered_map<std::string, RetryPolicyData> retryInfo;
610f52c03c1SCarson Labrado     HttpClient() = default;
611f52c03c1SCarson Labrado 
612039a47e3SCarson Labrado     // Used as a dummy callback by sendData() in order to call
613039a47e3SCarson Labrado     // sendDataWithCallback()
614039a47e3SCarson Labrado     static void genericResHandler(Response& res)
615039a47e3SCarson Labrado     {
616039a47e3SCarson Labrado         BMCWEB_LOG_DEBUG << "Response handled with return code: "
617039a47e3SCarson Labrado                          << std::to_string(res.resultInt());
618*4ee8e211SEd Tanous     }
619039a47e3SCarson Labrado 
620f52c03c1SCarson Labrado   public:
621f52c03c1SCarson Labrado     HttpClient(const HttpClient&) = delete;
622f52c03c1SCarson Labrado     HttpClient& operator=(const HttpClient&) = delete;
623f52c03c1SCarson Labrado     HttpClient(HttpClient&&) = delete;
624f52c03c1SCarson Labrado     HttpClient& operator=(HttpClient&&) = delete;
625f52c03c1SCarson Labrado     ~HttpClient() = default;
626f52c03c1SCarson Labrado 
627f52c03c1SCarson Labrado     static HttpClient& getInstance()
628f52c03c1SCarson Labrado     {
629f52c03c1SCarson Labrado         static HttpClient handler;
630f52c03c1SCarson Labrado         return handler;
631f52c03c1SCarson Labrado     }
632f52c03c1SCarson Labrado 
633039a47e3SCarson Labrado     // Send a request to destIP:destPort where additional processing of the
634039a47e3SCarson Labrado     // result is not required
635f52c03c1SCarson Labrado     void sendData(std::string& data, const std::string& id,
636f52c03c1SCarson Labrado                   const std::string& destIP, const uint16_t destPort,
637f52c03c1SCarson Labrado                   const std::string& destUri,
638f52c03c1SCarson Labrado                   const boost::beast::http::fields& httpHeader,
639244256ccSCarson Labrado                   const boost::beast::http::verb verb,
640244256ccSCarson Labrado                   const std::string& retryPolicyName)
641f52c03c1SCarson Labrado     {
642039a47e3SCarson Labrado         std::function<void(Response&)> cb = genericResHandler;
643039a47e3SCarson Labrado         sendDataWithCallback(data, id, destIP, destPort, destUri, httpHeader,
644244256ccSCarson Labrado                              verb, retryPolicyName, cb);
645039a47e3SCarson Labrado     }
646039a47e3SCarson Labrado 
647039a47e3SCarson Labrado     // Send request to destIP:destPort and use the provided callback to
648039a47e3SCarson Labrado     // handle the response
649039a47e3SCarson Labrado     void sendDataWithCallback(std::string& data, const std::string& id,
650039a47e3SCarson Labrado                               const std::string& destIP,
651039a47e3SCarson Labrado                               const uint16_t destPort,
652039a47e3SCarson Labrado                               const std::string& destUri,
653039a47e3SCarson Labrado                               const boost::beast::http::fields& httpHeader,
654244256ccSCarson Labrado                               const boost::beast::http::verb verb,
655244256ccSCarson Labrado                               const std::string& retryPolicyName,
656039a47e3SCarson Labrado                               std::function<void(Response&)>& resHandler)
657039a47e3SCarson Labrado     {
658f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
659f52c03c1SCarson Labrado         // Use nullptr to avoid creating a ConnectionPool each time
660f52c03c1SCarson Labrado         auto result = connectionPools.try_emplace(clientKey, nullptr);
661f52c03c1SCarson Labrado         if (result.second)
662f52c03c1SCarson Labrado         {
663f52c03c1SCarson Labrado             // Now actually create the ConnectionPool shared_ptr since it does
664f52c03c1SCarson Labrado             // not already exist
665244256ccSCarson Labrado             result.first->second =
666244256ccSCarson Labrado                 std::make_shared<ConnectionPool>(ioc, id, destIP, destPort);
667f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
668f52c03c1SCarson Labrado         }
669f52c03c1SCarson Labrado         else
670f52c03c1SCarson Labrado         {
671f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Using existing connection pool for "
672f52c03c1SCarson Labrado                              << clientKey;
673f52c03c1SCarson Labrado         }
674f52c03c1SCarson Labrado 
675f52c03c1SCarson Labrado         // Get the associated retry policy
676f52c03c1SCarson Labrado         auto policy = retryInfo.try_emplace(retryPolicyName);
677f52c03c1SCarson Labrado         if (policy.second)
678f52c03c1SCarson Labrado         {
679f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
680f52c03c1SCarson Labrado                              << "\" with default values";
681f52c03c1SCarson Labrado             policy.first->second.name = retryPolicyName;
682f52c03c1SCarson Labrado         }
683f52c03c1SCarson Labrado 
684f52c03c1SCarson Labrado         // Send the data using either the existing connection pool or the newly
685f52c03c1SCarson Labrado         // created connection pool
686244256ccSCarson Labrado         result.first->second->sendData(data, destUri, httpHeader, verb,
687244256ccSCarson Labrado                                        policy.first->second, resHandler);
688f52c03c1SCarson Labrado     }
689f52c03c1SCarson Labrado 
690f52c03c1SCarson Labrado     void setRetryConfig(const uint32_t retryAttempts,
691f52c03c1SCarson Labrado                         const uint32_t retryTimeoutInterval,
692f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
693f52c03c1SCarson Labrado     {
694f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
695f52c03c1SCarson Labrado         // the given retryPolicyName
696f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
697f52c03c1SCarson Labrado         if (result.second)
698f52c03c1SCarson Labrado         {
699f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
700f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
701f52c03c1SCarson Labrado             result.first->second.name = retryPolicyName;
702f52c03c1SCarson Labrado         }
703f52c03c1SCarson Labrado         else
704f52c03c1SCarson Labrado         {
705f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
706f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
707f52c03c1SCarson Labrado         }
708f52c03c1SCarson Labrado 
709f52c03c1SCarson Labrado         result.first->second.maxRetryAttempts = retryAttempts;
710f52c03c1SCarson Labrado         result.first->second.retryIntervalSecs =
711f52c03c1SCarson Labrado             std::chrono::seconds(retryTimeoutInterval);
712f52c03c1SCarson Labrado     }
713f52c03c1SCarson Labrado 
714f52c03c1SCarson Labrado     void setRetryPolicy(const std::string& retryPolicy,
715f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
716f52c03c1SCarson Labrado     {
717f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
718f52c03c1SCarson Labrado         // the given retryPolicyName
719f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
720f52c03c1SCarson Labrado         if (result.second)
721f52c03c1SCarson Labrado         {
722f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
723f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
724f52c03c1SCarson Labrado             result.first->second.name = retryPolicyName;
725f52c03c1SCarson Labrado         }
726f52c03c1SCarson Labrado         else
727f52c03c1SCarson Labrado         {
728f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
729f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
730f52c03c1SCarson Labrado         }
731f52c03c1SCarson Labrado 
732f52c03c1SCarson Labrado         result.first->second.retryPolicyAction = retryPolicy;
733f52c03c1SCarson Labrado     }
734f52c03c1SCarson Labrado };
735bd030d0aSAppaRao Puli } // namespace crow
736