xref: /openbmc/bmcweb/http/http_client.hpp (revision a7a80296)
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 
63*a7a80296SCarson Labrado static inline boost::system::error_code
64*a7a80296SCarson Labrado     defaultRetryHandler(unsigned int respCode)
65*a7a80296SCarson Labrado {
66*a7a80296SCarson Labrado     // As a default, assume 200X is alright
67*a7a80296SCarson Labrado     BMCWEB_LOG_DEBUG << "Using default check for response code validity";
68*a7a80296SCarson Labrado     if ((respCode < 200) || (respCode >= 300))
69*a7a80296SCarson Labrado     {
70*a7a80296SCarson Labrado         return boost::system::errc::make_error_code(
71*a7a80296SCarson Labrado             boost::system::errc::result_out_of_range);
72*a7a80296SCarson Labrado     }
73*a7a80296SCarson Labrado 
74*a7a80296SCarson Labrado     // Return 0 if the response code is valid
75*a7a80296SCarson Labrado     return boost::system::errc::make_error_code(boost::system::errc::success);
76*a7a80296SCarson Labrado };
77*a7a80296SCarson Labrado 
78f52c03c1SCarson Labrado // We need to allow retry information to be set before a message has been sent
79f52c03c1SCarson Labrado // and a connection pool has been created
80f52c03c1SCarson Labrado struct RetryPolicyData
81f52c03c1SCarson Labrado {
82f52c03c1SCarson Labrado     uint32_t maxRetryAttempts = 5;
83f52c03c1SCarson Labrado     std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
84f52c03c1SCarson Labrado     std::string retryPolicyAction = "TerminateAfterRetries";
85*a7a80296SCarson Labrado     std::function<boost::system::error_code(unsigned int respCode)>
86*a7a80296SCarson Labrado         invalidResp = defaultRetryHandler;
87f52c03c1SCarson Labrado };
88f52c03c1SCarson Labrado 
89f52c03c1SCarson Labrado struct PendingRequest
90f52c03c1SCarson Labrado {
91244256ccSCarson Labrado     boost::beast::http::request<boost::beast::http::string_body> req;
92039a47e3SCarson Labrado     std::function<void(bool, uint32_t, Response&)> callback;
93f52c03c1SCarson Labrado     RetryPolicyData retryPolicy;
94039a47e3SCarson Labrado     PendingRequest(
95244256ccSCarson Labrado         boost::beast::http::request<boost::beast::http::string_body>&& req,
96039a47e3SCarson Labrado         const std::function<void(bool, uint32_t, Response&)>& callback,
97f52c03c1SCarson Labrado         const RetryPolicyData& retryPolicy) :
98244256ccSCarson Labrado         req(std::move(req)),
99f52c03c1SCarson Labrado         callback(callback), retryPolicy(retryPolicy)
100f52c03c1SCarson Labrado     {}
101f52c03c1SCarson Labrado };
102f52c03c1SCarson Labrado 
103f52c03c1SCarson Labrado class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
104bd030d0aSAppaRao Puli {
105bd030d0aSAppaRao Puli   private:
106f52c03c1SCarson Labrado     ConnState state = ConnState::initialized;
107f52c03c1SCarson Labrado     uint32_t retryCount = 0;
108f52c03c1SCarson Labrado     bool runningTimer = false;
109f52c03c1SCarson Labrado     std::string subId;
110f52c03c1SCarson Labrado     std::string host;
111f52c03c1SCarson Labrado     uint16_t port;
112f52c03c1SCarson Labrado     uint32_t connId;
113f52c03c1SCarson Labrado 
114f52c03c1SCarson Labrado     // Retry policy information
115f52c03c1SCarson Labrado     // This should be updated before each message is sent
116f52c03c1SCarson Labrado     RetryPolicyData retryPolicy;
117f52c03c1SCarson Labrado 
118f52c03c1SCarson Labrado     // Data buffers
119bd030d0aSAppaRao Puli     boost::beast::http::request<boost::beast::http::string_body> req;
1206eaa1d2fSSunitha Harish     std::optional<
1216eaa1d2fSSunitha Harish         boost::beast::http::response_parser<boost::beast::http::string_body>>
1226eaa1d2fSSunitha Harish         parser;
123f52c03c1SCarson Labrado     boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
124039a47e3SCarson Labrado     Response res;
1256eaa1d2fSSunitha Harish 
126f52c03c1SCarson Labrado     // Ascync callables
127039a47e3SCarson Labrado     std::function<void(bool, uint32_t, Response&)> callback;
128f52c03c1SCarson Labrado     crow::async_resolve::Resolver resolver;
129f52c03c1SCarson Labrado     boost::beast::tcp_stream conn;
130f52c03c1SCarson Labrado     boost::asio::steady_timer timer;
13184b35604SEd Tanous 
132f52c03c1SCarson Labrado     friend class ConnectionPool;
133bd030d0aSAppaRao Puli 
13429a82b08SSunitha Harish     void doResolve()
13529a82b08SSunitha Harish     {
13629a82b08SSunitha Harish         state = ConnState::resolveInProgress;
137f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
138f52c03c1SCarson Labrado                          << std::to_string(port)
139f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId);
14029a82b08SSunitha Harish 
14129a82b08SSunitha Harish         auto respHandler =
14229a82b08SSunitha Harish             [self(shared_from_this())](
14329a82b08SSunitha Harish                 const boost::beast::error_code ec,
14429a82b08SSunitha Harish                 const std::vector<boost::asio::ip::tcp::endpoint>&
14529a82b08SSunitha Harish                     endpointList) {
14626f6976fSEd Tanous             if (ec || (endpointList.empty()))
14729a82b08SSunitha Harish             {
14829a82b08SSunitha Harish                 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
14929a82b08SSunitha Harish                 self->state = ConnState::resolveFailed;
150f52c03c1SCarson Labrado                 self->waitAndRetry();
15129a82b08SSunitha Harish                 return;
15229a82b08SSunitha Harish             }
153f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Resolved " << self->host << ":"
154f52c03c1SCarson Labrado                              << std::to_string(self->port)
155f52c03c1SCarson Labrado                              << ", id: " << std::to_string(self->connId);
15629a82b08SSunitha Harish             self->doConnect(endpointList);
15729a82b08SSunitha Harish         };
158f52c03c1SCarson Labrado 
15929a82b08SSunitha Harish         resolver.asyncResolve(host, port, std::move(respHandler));
16029a82b08SSunitha Harish     }
16129a82b08SSunitha Harish 
16229a82b08SSunitha Harish     void doConnect(
16329a82b08SSunitha Harish         const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
164bd030d0aSAppaRao Puli     {
1652a5689a7SAppaRao Puli         state = ConnState::connectInProgress;
1662a5689a7SAppaRao Puli 
167f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
168f52c03c1SCarson Labrado                          << std::to_string(port)
169f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId);
170b00dcc27SEd Tanous 
17129a82b08SSunitha Harish         conn.expires_after(std::chrono::seconds(30));
172002d39b4SEd Tanous         conn.async_connect(endpointList,
173002d39b4SEd Tanous                            [self(shared_from_this())](
17429a82b08SSunitha Harish                                const boost::beast::error_code ec,
17529a82b08SSunitha Harish                                const boost::asio::ip::tcp::endpoint& endpoint) {
1762a5689a7SAppaRao Puli             if (ec)
1772a5689a7SAppaRao Puli             {
178002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
179002d39b4SEd Tanous                                  << ":" << std::to_string(endpoint.port())
180f52c03c1SCarson Labrado                                  << ", id: " << std::to_string(self->connId)
1812a5689a7SAppaRao Puli                                  << " failed: " << ec.message();
1822a5689a7SAppaRao Puli                 self->state = ConnState::connectFailed;
183f52c03c1SCarson Labrado                 self->waitAndRetry();
1842a5689a7SAppaRao Puli                 return;
1852a5689a7SAppaRao Puli             }
186f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG
187f52c03c1SCarson Labrado                 << "Connected to: " << endpoint.address().to_string() << ":"
188f52c03c1SCarson Labrado                 << std::to_string(endpoint.port())
189f52c03c1SCarson Labrado                 << ", id: " << std::to_string(self->connId);
1906eaa1d2fSSunitha Harish             self->state = ConnState::connected;
191f52c03c1SCarson Labrado             self->sendMessage();
1922a5689a7SAppaRao Puli         });
1932a5689a7SAppaRao Puli     }
1942a5689a7SAppaRao Puli 
195f52c03c1SCarson Labrado     void sendMessage()
1962a5689a7SAppaRao Puli     {
1972a5689a7SAppaRao Puli         state = ConnState::sendInProgress;
1982a5689a7SAppaRao Puli 
199bd030d0aSAppaRao Puli         // Set a timeout on the operation
200bd030d0aSAppaRao Puli         conn.expires_after(std::chrono::seconds(30));
201bd030d0aSAppaRao Puli 
202bd030d0aSAppaRao Puli         // Send the HTTP request to the remote host
203bd030d0aSAppaRao Puli         boost::beast::http::async_write(
204bd030d0aSAppaRao Puli             conn, req,
2052a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
206bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
207bd030d0aSAppaRao Puli             if (ec)
208bd030d0aSAppaRao Puli             {
209002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
2102a5689a7SAppaRao Puli                 self->state = ConnState::sendFailed;
211f52c03c1SCarson Labrado                 self->waitAndRetry();
212bd030d0aSAppaRao Puli                 return;
213bd030d0aSAppaRao Puli             }
214bd030d0aSAppaRao Puli             BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
215bd030d0aSAppaRao Puli                              << bytesTransferred;
216bd030d0aSAppaRao Puli             boost::ignore_unused(bytesTransferred);
217bd030d0aSAppaRao Puli 
2182a5689a7SAppaRao Puli             self->recvMessage();
219bd030d0aSAppaRao Puli             });
220bd030d0aSAppaRao Puli     }
221bd030d0aSAppaRao Puli 
222bd030d0aSAppaRao Puli     void recvMessage()
223bd030d0aSAppaRao Puli     {
2246eaa1d2fSSunitha Harish         state = ConnState::recvInProgress;
2256eaa1d2fSSunitha Harish 
2266eaa1d2fSSunitha Harish         parser.emplace(std::piecewise_construct, std::make_tuple());
2276eaa1d2fSSunitha Harish         parser->body_limit(httpReadBodyLimit);
2286eaa1d2fSSunitha Harish 
229bd030d0aSAppaRao Puli         // Receive the HTTP response
230bd030d0aSAppaRao Puli         boost::beast::http::async_read(
2316eaa1d2fSSunitha Harish             conn, buffer, *parser,
2322a5689a7SAppaRao Puli             [self(shared_from_this())](const boost::beast::error_code& ec,
233bd030d0aSAppaRao Puli                                        const std::size_t& bytesTransferred) {
234bd030d0aSAppaRao Puli             if (ec)
235bd030d0aSAppaRao Puli             {
236002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
2372a5689a7SAppaRao Puli                 self->state = ConnState::recvFailed;
238f52c03c1SCarson Labrado                 self->waitAndRetry();
239bd030d0aSAppaRao Puli                 return;
240bd030d0aSAppaRao Puli             }
241bd030d0aSAppaRao Puli             BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
242bd030d0aSAppaRao Puli                              << bytesTransferred;
2436eaa1d2fSSunitha Harish             BMCWEB_LOG_DEBUG << "recvMessage() data: "
2448cc8edecSEd Tanous                              << self->parser->get().body();
245bd030d0aSAppaRao Puli 
2466eaa1d2fSSunitha Harish             unsigned int respCode = self->parser->get().result_int();
2476eaa1d2fSSunitha Harish             BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
2486eaa1d2fSSunitha Harish                              << respCode;
2496eaa1d2fSSunitha Harish 
250*a7a80296SCarson Labrado             // Make sure the received response code is valid as defined by
251*a7a80296SCarson Labrado             // the associated retry policy
252*a7a80296SCarson Labrado             if (self->retryPolicy.invalidResp(respCode))
2536eaa1d2fSSunitha Harish             {
2546eaa1d2fSSunitha Harish                 // The listener failed to receive the Sent-Event
255002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
2567adb85acSSunitha Harish                                     "receive Sent-Event. Header Response Code: "
2577adb85acSSunitha Harish                                  << respCode;
2586eaa1d2fSSunitha Harish                 self->state = ConnState::recvFailed;
259f52c03c1SCarson Labrado                 self->waitAndRetry();
2606eaa1d2fSSunitha Harish                 return;
2616eaa1d2fSSunitha Harish             }
262bd030d0aSAppaRao Puli 
263f52c03c1SCarson Labrado             // Send is successful
264f52c03c1SCarson Labrado             // Reset the counter just in case this was after retrying
265f52c03c1SCarson Labrado             self->retryCount = 0;
2666eaa1d2fSSunitha Harish 
2676eaa1d2fSSunitha Harish             // Keep the connection alive if server supports it
2686eaa1d2fSSunitha Harish             // Else close the connection
2696eaa1d2fSSunitha Harish             BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
2706eaa1d2fSSunitha Harish                              << self->parser->keep_alive();
2716eaa1d2fSSunitha Harish 
272039a47e3SCarson Labrado             // Copy the response into a Response object so that it can be
273039a47e3SCarson Labrado             // processed by the callback function.
274039a47e3SCarson Labrado             self->res.clear();
275039a47e3SCarson Labrado             self->res.stringResponse = self->parser->release();
276002d39b4SEd Tanous             self->callback(self->parser->keep_alive(), self->connId, self->res);
277bd030d0aSAppaRao Puli             });
278bd030d0aSAppaRao Puli     }
279bd030d0aSAppaRao Puli 
2806eaa1d2fSSunitha Harish     void waitAndRetry()
281bd030d0aSAppaRao Puli     {
282f52c03c1SCarson Labrado         if (retryCount >= retryPolicy.maxRetryAttempts)
2832a5689a7SAppaRao Puli         {
2846eaa1d2fSSunitha Harish             BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
285f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Retry policy: "
286f52c03c1SCarson Labrado                              << retryPolicy.retryPolicyAction;
287039a47e3SCarson Labrado 
288039a47e3SCarson Labrado             // We want to return a 502 to indicate there was an error with the
289039a47e3SCarson Labrado             // external server
290039a47e3SCarson Labrado             res.clear();
291039a47e3SCarson Labrado             redfish::messages::operationFailed(res);
292039a47e3SCarson Labrado 
293f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
294fe44eb0bSAyushi Smriti             {
295fe44eb0bSAyushi Smriti                 // TODO: delete subscription
296fe44eb0bSAyushi Smriti                 state = ConnState::terminated;
297039a47e3SCarson Labrado                 callback(false, connId, res);
298fe44eb0bSAyushi Smriti             }
299f52c03c1SCarson Labrado             if (retryPolicy.retryPolicyAction == "SuspendRetries")
300fe44eb0bSAyushi Smriti             {
3012a5689a7SAppaRao Puli                 state = ConnState::suspended;
302039a47e3SCarson Labrado                 callback(false, connId, res);
3032a5689a7SAppaRao Puli             }
3046eaa1d2fSSunitha Harish             // Reset the retrycount to zero so that client can try connecting
3056eaa1d2fSSunitha Harish             // again if needed
306fe44eb0bSAyushi Smriti             retryCount = 0;
3072a5689a7SAppaRao Puli             return;
3082a5689a7SAppaRao Puli         }
3092a5689a7SAppaRao Puli 
310fe44eb0bSAyushi Smriti         if (runningTimer)
311fe44eb0bSAyushi Smriti         {
312fe44eb0bSAyushi Smriti             BMCWEB_LOG_DEBUG << "Retry timer is already running.";
313fe44eb0bSAyushi Smriti             return;
314fe44eb0bSAyushi Smriti         }
315fe44eb0bSAyushi Smriti         runningTimer = true;
316fe44eb0bSAyushi Smriti 
3172a5689a7SAppaRao Puli         retryCount++;
318fe44eb0bSAyushi Smriti 
319f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Attempt retry after "
320f52c03c1SCarson Labrado                          << std::to_string(
321f52c03c1SCarson Labrado                                 retryPolicy.retryIntervalSecs.count())
322fe44eb0bSAyushi Smriti                          << " seconds. RetryCount = " << retryCount;
323f52c03c1SCarson Labrado         timer.expires_after(retryPolicy.retryIntervalSecs);
324cb13a392SEd Tanous         timer.async_wait(
325f52c03c1SCarson Labrado             [self(shared_from_this())](const boost::system::error_code ec) {
3266eaa1d2fSSunitha Harish             if (ec == boost::asio::error::operation_aborted)
3276eaa1d2fSSunitha Harish             {
3286eaa1d2fSSunitha Harish                 BMCWEB_LOG_DEBUG
3296eaa1d2fSSunitha Harish                     << "async_wait failed since the operation is aborted"
3306eaa1d2fSSunitha Harish                     << ec.message();
3316eaa1d2fSSunitha Harish             }
3326eaa1d2fSSunitha Harish             else if (ec)
3336eaa1d2fSSunitha Harish             {
3346eaa1d2fSSunitha Harish                 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
3356eaa1d2fSSunitha Harish                 // Ignore the error and continue the retry loop to attempt
3366eaa1d2fSSunitha Harish                 // sending the event as per the retry policy
3376eaa1d2fSSunitha Harish             }
338fe44eb0bSAyushi Smriti             self->runningTimer = false;
3396eaa1d2fSSunitha Harish 
340f52c03c1SCarson Labrado             // Let's close the connection and restart from resolve.
341f52c03c1SCarson Labrado             self->doCloseAndRetry();
342fe44eb0bSAyushi Smriti         });
3432a5689a7SAppaRao Puli     }
3442a5689a7SAppaRao Puli 
345f52c03c1SCarson Labrado     void doClose()
346fe44eb0bSAyushi Smriti     {
347f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
348f52c03c1SCarson Labrado         boost::beast::error_code ec;
349f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
350f52c03c1SCarson Labrado         conn.close();
351f52c03c1SCarson Labrado 
352f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
353f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
3542a5689a7SAppaRao Puli         {
355f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
356f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
357f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3586eaa1d2fSSunitha Harish             return;
3596eaa1d2fSSunitha Harish         }
360f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
361f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
362f52c03c1SCarson Labrado                          << " closed gracefully";
363f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
364f52c03c1SCarson Labrado         {
365f52c03c1SCarson Labrado             state = ConnState::closed;
3666eaa1d2fSSunitha Harish         }
3676eaa1d2fSSunitha Harish     }
368f52c03c1SCarson Labrado 
369f52c03c1SCarson Labrado     void doCloseAndRetry()
37092a74e56SAppaRao Puli     {
371f52c03c1SCarson Labrado         state = ConnState::closeInProgress;
372f52c03c1SCarson Labrado         boost::beast::error_code ec;
373f52c03c1SCarson Labrado         conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
374f52c03c1SCarson Labrado         conn.close();
375f52c03c1SCarson Labrado 
376f52c03c1SCarson Labrado         // not_connected happens sometimes so don't bother reporting it.
377f52c03c1SCarson Labrado         if (ec && ec != boost::beast::errc::not_connected)
37892a74e56SAppaRao Puli         {
379f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
380f52c03c1SCarson Labrado                              << ", id: " << std::to_string(connId)
381f52c03c1SCarson Labrado                              << "shutdown failed: " << ec.message();
3826eaa1d2fSSunitha Harish             return;
3836eaa1d2fSSunitha Harish         }
384f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
385f52c03c1SCarson Labrado                          << ", id: " << std::to_string(connId)
386f52c03c1SCarson Labrado                          << " closed gracefully";
387f52c03c1SCarson Labrado         if ((state != ConnState::suspended) && (state != ConnState::terminated))
3886eaa1d2fSSunitha Harish         {
389f52c03c1SCarson Labrado             // Now let's try to resend the data
390f52c03c1SCarson Labrado             state = ConnState::retry;
391f52c03c1SCarson Labrado             this->doResolve();
3922a5689a7SAppaRao Puli         }
393bd030d0aSAppaRao Puli     }
394bd030d0aSAppaRao Puli 
395bd030d0aSAppaRao Puli   public:
396f52c03c1SCarson Labrado     explicit ConnectionInfo(boost::asio::io_context& ioc, const std::string& id,
397f52c03c1SCarson Labrado                             const std::string& destIP, const uint16_t destPort,
398f52c03c1SCarson Labrado                             const unsigned int connId) :
399f52c03c1SCarson Labrado         subId(id),
400244256ccSCarson Labrado         host(destIP), port(destPort), connId(connId), conn(ioc), timer(ioc)
401244256ccSCarson Labrado     {}
402f52c03c1SCarson Labrado };
403bd030d0aSAppaRao Puli 
404f52c03c1SCarson Labrado class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
405bd030d0aSAppaRao Puli {
406f52c03c1SCarson Labrado   private:
407f52c03c1SCarson Labrado     boost::asio::io_context& ioc;
408f52c03c1SCarson Labrado     const std::string id;
409f52c03c1SCarson Labrado     const std::string destIP;
410f52c03c1SCarson Labrado     const uint16_t destPort;
411f52c03c1SCarson Labrado     std::vector<std::shared_ptr<ConnectionInfo>> connections;
412f52c03c1SCarson Labrado     boost::container::devector<PendingRequest> requestQueue;
413f52c03c1SCarson Labrado 
414f52c03c1SCarson Labrado     friend class HttpClient;
415f52c03c1SCarson Labrado 
416244256ccSCarson Labrado     // Configure a connections's request, callback, and retry info in
417244256ccSCarson Labrado     // preparation to begin sending the request
418f52c03c1SCarson Labrado     void setConnProps(ConnectionInfo& conn)
419bd030d0aSAppaRao Puli     {
420f52c03c1SCarson Labrado         if (requestQueue.empty())
421f52c03c1SCarson Labrado         {
422f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR
423f52c03c1SCarson Labrado                 << "setConnProps() should not have been called when requestQueue is empty";
424bd030d0aSAppaRao Puli             return;
425bd030d0aSAppaRao Puli         }
426bd030d0aSAppaRao Puli 
427244256ccSCarson Labrado         auto nextReq = requestQueue.front();
428244256ccSCarson Labrado         conn.retryPolicy = std::move(nextReq.retryPolicy);
429244256ccSCarson Labrado         conn.req = std::move(nextReq.req);
430244256ccSCarson Labrado         conn.callback = std::move(nextReq.callback);
431f52c03c1SCarson Labrado 
432f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
433f52c03c1SCarson Labrado                          << ":" << std::to_string(conn.port)
434*a7a80296SCarson Labrado                          << ", id: " << std::to_string(conn.connId);
435f52c03c1SCarson Labrado 
436f52c03c1SCarson Labrado         // We can remove the request from the queue at this point
437f52c03c1SCarson Labrado         requestQueue.pop_front();
438f52c03c1SCarson Labrado     }
439f52c03c1SCarson Labrado 
440f52c03c1SCarson Labrado     // Configures a connection to use the specific retry policy.
441f52c03c1SCarson Labrado     inline void setConnRetryPolicy(ConnectionInfo& conn,
442f52c03c1SCarson Labrado                                    const RetryPolicyData& retryPolicy)
4432a5689a7SAppaRao Puli     {
444f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
445*a7a80296SCarson Labrado                          << ", id: " << std::to_string(conn.connId);
446f52c03c1SCarson Labrado 
447f52c03c1SCarson Labrado         conn.retryPolicy = retryPolicy;
448f52c03c1SCarson Labrado     }
449f52c03c1SCarson Labrado 
450f52c03c1SCarson Labrado     // Gets called as part of callback after request is sent
451f52c03c1SCarson Labrado     // Reuses the connection if there are any requests waiting to be sent
452f52c03c1SCarson Labrado     // Otherwise closes the connection if it is not a keep-alive
453f52c03c1SCarson Labrado     void sendNext(bool keepAlive, uint32_t connId)
454f52c03c1SCarson Labrado     {
455f52c03c1SCarson Labrado         auto conn = connections[connId];
456f52c03c1SCarson Labrado         // Reuse the connection to send the next request in the queue
457f52c03c1SCarson Labrado         if (!requestQueue.empty())
458f52c03c1SCarson Labrado         {
459f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
460f52c03c1SCarson Labrado                              << " requests remaining in queue for " << destIP
461f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort)
462f52c03c1SCarson Labrado                              << ", reusing connnection "
463f52c03c1SCarson Labrado                              << std::to_string(connId);
464f52c03c1SCarson Labrado 
465f52c03c1SCarson Labrado             setConnProps(*conn);
466f52c03c1SCarson Labrado 
467f52c03c1SCarson Labrado             if (keepAlive)
468f52c03c1SCarson Labrado             {
469f52c03c1SCarson Labrado                 conn->sendMessage();
4702a5689a7SAppaRao Puli             }
4712a5689a7SAppaRao Puli             else
4722a5689a7SAppaRao Puli             {
473f52c03c1SCarson Labrado                 // Server is not keep-alive enabled so we need to close the
474f52c03c1SCarson Labrado                 // connection and then start over from resolve
475f52c03c1SCarson Labrado                 conn->doClose();
476f52c03c1SCarson Labrado                 conn->doResolve();
477f52c03c1SCarson Labrado             }
478f52c03c1SCarson Labrado             return;
479f52c03c1SCarson Labrado         }
480f52c03c1SCarson Labrado 
481f52c03c1SCarson Labrado         // No more messages to send so close the connection if necessary
482f52c03c1SCarson Labrado         if (keepAlive)
483f52c03c1SCarson Labrado         {
484f52c03c1SCarson Labrado             conn->state = ConnState::idle;
485f52c03c1SCarson Labrado         }
486f52c03c1SCarson Labrado         else
487f52c03c1SCarson Labrado         {
488f52c03c1SCarson Labrado             // Abort the connection since server is not keep-alive enabled
489f52c03c1SCarson Labrado             conn->state = ConnState::abortConnection;
490f52c03c1SCarson Labrado             conn->doClose();
4912a5689a7SAppaRao Puli         }
492bd030d0aSAppaRao Puli     }
493bd030d0aSAppaRao Puli 
494244256ccSCarson Labrado     void sendData(std::string& data, const std::string& destUri,
495244256ccSCarson Labrado                   const boost::beast::http::fields& httpHeader,
496244256ccSCarson Labrado                   const boost::beast::http::verb verb,
497244256ccSCarson Labrado                   const RetryPolicyData& retryPolicy,
498039a47e3SCarson Labrado                   std::function<void(Response&)>& resHandler)
499fe44eb0bSAyushi Smriti     {
500f52c03c1SCarson Labrado         std::weak_ptr<ConnectionPool> weakSelf = weak_from_this();
501f52c03c1SCarson Labrado 
502f52c03c1SCarson Labrado         // Callback to be called once the request has been sent
503039a47e3SCarson Labrado         auto cb = [weakSelf, resHandler](bool keepAlive, uint32_t connId,
504039a47e3SCarson Labrado                                          Response& res) {
505039a47e3SCarson Labrado             // Allow provided callback to perform additional processing of the
506039a47e3SCarson Labrado             // request
507039a47e3SCarson Labrado             resHandler(res);
508039a47e3SCarson Labrado 
509f52c03c1SCarson Labrado             // If requests remain in the queue then we want to reuse this
510f52c03c1SCarson Labrado             // connection to send the next request
511f52c03c1SCarson Labrado             std::shared_ptr<ConnectionPool> self = weakSelf.lock();
512f52c03c1SCarson Labrado             if (!self)
513f52c03c1SCarson Labrado             {
514f52c03c1SCarson Labrado                 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
515f52c03c1SCarson Labrado                 return;
516fe44eb0bSAyushi Smriti             }
517fe44eb0bSAyushi Smriti 
518f52c03c1SCarson Labrado             self->sendNext(keepAlive, connId);
519f52c03c1SCarson Labrado         };
520f52c03c1SCarson Labrado 
521244256ccSCarson Labrado         // Construct the request to be sent
522244256ccSCarson Labrado         boost::beast::http::request<boost::beast::http::string_body> thisReq(
523244256ccSCarson Labrado             verb, destUri, 11, "", httpHeader);
524244256ccSCarson Labrado         thisReq.set(boost::beast::http::field::host, destIP);
525244256ccSCarson Labrado         thisReq.keep_alive(true);
526244256ccSCarson Labrado         thisReq.body() = std::move(data);
527244256ccSCarson Labrado         thisReq.prepare_payload();
528244256ccSCarson Labrado 
529f52c03c1SCarson Labrado         // Reuse an existing connection if one is available
530f52c03c1SCarson Labrado         for (unsigned int i = 0; i < connections.size(); i++)
531fe44eb0bSAyushi Smriti         {
532f52c03c1SCarson Labrado             auto conn = connections[i];
533f52c03c1SCarson Labrado             if ((conn->state == ConnState::idle) ||
534f52c03c1SCarson Labrado                 (conn->state == ConnState::initialized) ||
535f52c03c1SCarson Labrado                 (conn->state == ConnState::closed))
536f52c03c1SCarson Labrado             {
537244256ccSCarson Labrado                 conn->req = std::move(thisReq);
538f52c03c1SCarson Labrado                 conn->callback = std::move(cb);
539f52c03c1SCarson Labrado                 setConnRetryPolicy(*conn, retryPolicy);
540f52c03c1SCarson Labrado                 std::string commonMsg = std::to_string(i) + " from pool " +
541f52c03c1SCarson Labrado                                         destIP + ":" + std::to_string(destPort);
542f52c03c1SCarson Labrado 
543f52c03c1SCarson Labrado                 if (conn->state == ConnState::idle)
544f52c03c1SCarson Labrado                 {
545f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Grabbing idle connection "
546f52c03c1SCarson Labrado                                      << commonMsg;
547f52c03c1SCarson Labrado                     conn->sendMessage();
548f52c03c1SCarson Labrado                 }
549f52c03c1SCarson Labrado                 else
550f52c03c1SCarson Labrado                 {
551f52c03c1SCarson Labrado                     BMCWEB_LOG_DEBUG << "Reusing existing connection "
552f52c03c1SCarson Labrado                                      << commonMsg;
553f52c03c1SCarson Labrado                     conn->doResolve();
554f52c03c1SCarson Labrado                 }
555f52c03c1SCarson Labrado                 return;
556f52c03c1SCarson Labrado             }
557f52c03c1SCarson Labrado         }
558f52c03c1SCarson Labrado 
559f52c03c1SCarson Labrado         // All connections in use so create a new connection or add request to
560f52c03c1SCarson Labrado         // the queue
561f52c03c1SCarson Labrado         if (connections.size() < maxPoolSize)
562f52c03c1SCarson Labrado         {
563f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
564f52c03c1SCarson Labrado                              << ":" << std::to_string(destPort);
565f52c03c1SCarson Labrado             auto conn = addConnection();
566244256ccSCarson Labrado             conn->req = std::move(thisReq);
567f52c03c1SCarson Labrado             conn->callback = std::move(cb);
568f52c03c1SCarson Labrado             setConnRetryPolicy(*conn, retryPolicy);
569f52c03c1SCarson Labrado             conn->doResolve();
570f52c03c1SCarson Labrado         }
571f52c03c1SCarson Labrado         else if (requestQueue.size() < maxRequestQueueSize)
572f52c03c1SCarson Labrado         {
573f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
574244256ccSCarson Labrado             requestQueue.emplace_back(std::move(thisReq), std::move(cb),
575f52c03c1SCarson Labrado                                       retryPolicy);
576f52c03c1SCarson Labrado         }
577f52c03c1SCarson Labrado         else
578f52c03c1SCarson Labrado         {
579f52c03c1SCarson Labrado             BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
580f52c03c1SCarson Labrado                              << " request queue full.  Dropping request.";
581f52c03c1SCarson Labrado         }
582f52c03c1SCarson Labrado     }
583f52c03c1SCarson Labrado 
584f52c03c1SCarson Labrado     std::shared_ptr<ConnectionInfo>& addConnection()
585f52c03c1SCarson Labrado     {
586f52c03c1SCarson Labrado         unsigned int newId = static_cast<unsigned int>(connections.size());
587f52c03c1SCarson Labrado 
588244256ccSCarson Labrado         auto& ret = connections.emplace_back(
589244256ccSCarson Labrado             std::make_shared<ConnectionInfo>(ioc, id, destIP, destPort, newId));
590f52c03c1SCarson Labrado 
591f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Added connection "
592f52c03c1SCarson Labrado                          << std::to_string(connections.size() - 1)
593f52c03c1SCarson Labrado                          << " to pool " << destIP << ":"
594f52c03c1SCarson Labrado                          << std::to_string(destPort);
595f52c03c1SCarson Labrado 
596f52c03c1SCarson Labrado         return ret;
597f52c03c1SCarson Labrado     }
598f52c03c1SCarson Labrado 
599f52c03c1SCarson Labrado   public:
600f52c03c1SCarson Labrado     explicit ConnectionPool(boost::asio::io_context& ioc, const std::string& id,
601244256ccSCarson Labrado                             const std::string& destIP,
602244256ccSCarson Labrado                             const uint16_t destPort) :
603f52c03c1SCarson Labrado         ioc(ioc),
604244256ccSCarson Labrado         id(id), destIP(destIP), destPort(destPort)
605f52c03c1SCarson Labrado     {
606f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
607f52c03c1SCarson Labrado         BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
608f52c03c1SCarson Labrado                          << std::to_string(destPort);
609f52c03c1SCarson Labrado 
610f52c03c1SCarson Labrado         // Initialize the pool with a single connection
611f52c03c1SCarson Labrado         addConnection();
612fe44eb0bSAyushi Smriti     }
613bd030d0aSAppaRao Puli };
614bd030d0aSAppaRao Puli 
615f52c03c1SCarson Labrado class HttpClient
616f52c03c1SCarson Labrado {
617f52c03c1SCarson Labrado   private:
618f52c03c1SCarson Labrado     std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
619f52c03c1SCarson Labrado         connectionPools;
620f52c03c1SCarson Labrado     boost::asio::io_context& ioc =
621f52c03c1SCarson Labrado         crow::connections::systemBus->get_io_context();
622f52c03c1SCarson Labrado     std::unordered_map<std::string, RetryPolicyData> retryInfo;
623f52c03c1SCarson Labrado     HttpClient() = default;
624f52c03c1SCarson Labrado 
625039a47e3SCarson Labrado     // Used as a dummy callback by sendData() in order to call
626039a47e3SCarson Labrado     // sendDataWithCallback()
627039a47e3SCarson Labrado     static void genericResHandler(Response& res)
628039a47e3SCarson Labrado     {
629039a47e3SCarson Labrado         BMCWEB_LOG_DEBUG << "Response handled with return code: "
630039a47e3SCarson Labrado                          << std::to_string(res.resultInt());
6314ee8e211SEd Tanous     }
632039a47e3SCarson Labrado 
633f52c03c1SCarson Labrado   public:
634f52c03c1SCarson Labrado     HttpClient(const HttpClient&) = delete;
635f52c03c1SCarson Labrado     HttpClient& operator=(const HttpClient&) = delete;
636f52c03c1SCarson Labrado     HttpClient(HttpClient&&) = delete;
637f52c03c1SCarson Labrado     HttpClient& operator=(HttpClient&&) = delete;
638f52c03c1SCarson Labrado     ~HttpClient() = default;
639f52c03c1SCarson Labrado 
640f52c03c1SCarson Labrado     static HttpClient& getInstance()
641f52c03c1SCarson Labrado     {
642f52c03c1SCarson Labrado         static HttpClient handler;
643f52c03c1SCarson Labrado         return handler;
644f52c03c1SCarson Labrado     }
645f52c03c1SCarson Labrado 
646039a47e3SCarson Labrado     // Send a request to destIP:destPort where additional processing of the
647039a47e3SCarson Labrado     // result is not required
648f52c03c1SCarson Labrado     void sendData(std::string& data, const std::string& id,
649f52c03c1SCarson Labrado                   const std::string& destIP, const uint16_t destPort,
650f52c03c1SCarson Labrado                   const std::string& destUri,
651f52c03c1SCarson Labrado                   const boost::beast::http::fields& httpHeader,
652244256ccSCarson Labrado                   const boost::beast::http::verb verb,
653244256ccSCarson Labrado                   const std::string& retryPolicyName)
654f52c03c1SCarson Labrado     {
655039a47e3SCarson Labrado         std::function<void(Response&)> cb = genericResHandler;
656039a47e3SCarson Labrado         sendDataWithCallback(data, id, destIP, destPort, destUri, httpHeader,
657244256ccSCarson Labrado                              verb, retryPolicyName, cb);
658039a47e3SCarson Labrado     }
659039a47e3SCarson Labrado 
660039a47e3SCarson Labrado     // Send request to destIP:destPort and use the provided callback to
661039a47e3SCarson Labrado     // handle the response
662039a47e3SCarson Labrado     void sendDataWithCallback(std::string& data, const std::string& id,
663039a47e3SCarson Labrado                               const std::string& destIP,
664039a47e3SCarson Labrado                               const uint16_t destPort,
665039a47e3SCarson Labrado                               const std::string& destUri,
666039a47e3SCarson Labrado                               const boost::beast::http::fields& httpHeader,
667244256ccSCarson Labrado                               const boost::beast::http::verb verb,
668244256ccSCarson Labrado                               const std::string& retryPolicyName,
669039a47e3SCarson Labrado                               std::function<void(Response&)>& resHandler)
670039a47e3SCarson Labrado     {
671f52c03c1SCarson Labrado         std::string clientKey = destIP + ":" + std::to_string(destPort);
672f52c03c1SCarson Labrado         // Use nullptr to avoid creating a ConnectionPool each time
673f52c03c1SCarson Labrado         auto result = connectionPools.try_emplace(clientKey, nullptr);
674f52c03c1SCarson Labrado         if (result.second)
675f52c03c1SCarson Labrado         {
676f52c03c1SCarson Labrado             // Now actually create the ConnectionPool shared_ptr since it does
677f52c03c1SCarson Labrado             // not already exist
678244256ccSCarson Labrado             result.first->second =
679244256ccSCarson Labrado                 std::make_shared<ConnectionPool>(ioc, id, destIP, destPort);
680f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
681f52c03c1SCarson Labrado         }
682f52c03c1SCarson Labrado         else
683f52c03c1SCarson Labrado         {
684f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Using existing connection pool for "
685f52c03c1SCarson Labrado                              << clientKey;
686f52c03c1SCarson Labrado         }
687f52c03c1SCarson Labrado 
688f52c03c1SCarson Labrado         // Get the associated retry policy
689f52c03c1SCarson Labrado         auto policy = retryInfo.try_emplace(retryPolicyName);
690f52c03c1SCarson Labrado         if (policy.second)
691f52c03c1SCarson Labrado         {
692f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
693f52c03c1SCarson Labrado                              << "\" with default values";
694f52c03c1SCarson Labrado         }
695f52c03c1SCarson Labrado 
696f52c03c1SCarson Labrado         // Send the data using either the existing connection pool or the newly
697f52c03c1SCarson Labrado         // created connection pool
698244256ccSCarson Labrado         result.first->second->sendData(data, destUri, httpHeader, verb,
699244256ccSCarson Labrado                                        policy.first->second, resHandler);
700f52c03c1SCarson Labrado     }
701f52c03c1SCarson Labrado 
702*a7a80296SCarson Labrado     void setRetryConfig(
703*a7a80296SCarson Labrado         const uint32_t retryAttempts, const uint32_t retryTimeoutInterval,
704*a7a80296SCarson Labrado         const std::function<boost::system::error_code(unsigned int respCode)>&
705*a7a80296SCarson Labrado             invalidResp,
706f52c03c1SCarson Labrado         const std::string& retryPolicyName)
707f52c03c1SCarson Labrado     {
708f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
709f52c03c1SCarson Labrado         // the given retryPolicyName
710f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
711f52c03c1SCarson Labrado         if (result.second)
712f52c03c1SCarson Labrado         {
713f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
714f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
715f52c03c1SCarson Labrado         }
716f52c03c1SCarson Labrado         else
717f52c03c1SCarson Labrado         {
718f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
719f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
720f52c03c1SCarson Labrado         }
721f52c03c1SCarson Labrado 
722f52c03c1SCarson Labrado         result.first->second.maxRetryAttempts = retryAttempts;
723f52c03c1SCarson Labrado         result.first->second.retryIntervalSecs =
724f52c03c1SCarson Labrado             std::chrono::seconds(retryTimeoutInterval);
725*a7a80296SCarson Labrado         result.first->second.invalidResp = invalidResp;
726f52c03c1SCarson Labrado     }
727f52c03c1SCarson Labrado 
728f52c03c1SCarson Labrado     void setRetryPolicy(const std::string& retryPolicy,
729f52c03c1SCarson Labrado                         const std::string& retryPolicyName)
730f52c03c1SCarson Labrado     {
731f52c03c1SCarson Labrado         // We need to create the retry policy if one does not already exist for
732f52c03c1SCarson Labrado         // the given retryPolicyName
733f52c03c1SCarson Labrado         auto result = retryInfo.try_emplace(retryPolicyName);
734f52c03c1SCarson Labrado         if (result.second)
735f52c03c1SCarson Labrado         {
736f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
737f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
738f52c03c1SCarson Labrado         }
739f52c03c1SCarson Labrado         else
740f52c03c1SCarson Labrado         {
741f52c03c1SCarson Labrado             BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
742f52c03c1SCarson Labrado                              << retryPolicyName << "\"";
743f52c03c1SCarson Labrado         }
744f52c03c1SCarson Labrado 
745f52c03c1SCarson Labrado         result.first->second.retryPolicyAction = retryPolicy;
746f52c03c1SCarson Labrado     }
747f52c03c1SCarson Labrado };
748bd030d0aSAppaRao Puli } // namespace crow
749