blob: ae8bfeb09f4fff153a7b87405b77db8e067772fa [file] [log] [blame]
AppaRao Pulibd030d02020-03-20 03:34:29 +05301/*
2// Copyright (c) 2020 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
Ed Tanous0d5f5cf2022-03-12 15:30:55 -080017#include <boost/asio/connect.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070018#include <boost/asio/io_context.hpp>
Sunitha Harish29a82b02021-02-18 15:54:16 +053019#include <boost/asio/ip/address.hpp>
20#include <boost/asio/ip/basic_endpoint.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070021#include <boost/asio/ip/tcp.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000022#include <boost/asio/ssl/context.hpp>
23#include <boost/asio/ssl/error.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070024#include <boost/asio/steady_timer.hpp>
25#include <boost/beast/core/flat_buffer.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070026#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070027#include <boost/beast/http/message.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070028#include <boost/beast/http/parser.hpp>
29#include <boost/beast/http/read.hpp>
30#include <boost/beast/http/string_body.hpp>
31#include <boost/beast/http/write.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000032#include <boost/beast/ssl/ssl_stream.hpp>
AppaRao Pulibd030d02020-03-20 03:34:29 +053033#include <boost/beast/version.hpp>
Carson Labradof52c03c2022-03-23 18:50:15 +000034#include <boost/container/devector.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070035#include <boost/system/error_code.hpp>
36#include <http/http_response.hpp>
Sunitha Harish29a82b02021-02-18 15:54:16 +053037#include <include/async_resolve.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070038#include <logging.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000039#include <ssl_key_handler.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040
AppaRao Pulibd030d02020-03-20 03:34:29 +053041#include <cstdlib>
42#include <functional>
43#include <iostream>
44#include <memory>
AppaRao Puli2a5689a2020-04-29 15:24:31 +053045#include <queue>
AppaRao Pulibd030d02020-03-20 03:34:29 +053046#include <string>
47
48namespace crow
49{
50
Carson Labradof52c03c2022-03-23 18:50:15 +000051// It is assumed that the BMC should be able to handle 4 parallel connections
52constexpr uint8_t maxPoolSize = 4;
53constexpr uint8_t maxRequestQueueSize = 50;
Carson Labrado17dcc312022-07-28 22:17:28 +000054constexpr unsigned int httpReadBodyLimit = 131072;
Carson Labrado4d942722022-06-22 22:16:10 +000055constexpr unsigned int httpReadBufferSize = 4096;
AppaRao Puli2a5689a2020-04-29 15:24:31 +053056
AppaRao Pulibd030d02020-03-20 03:34:29 +053057enum class ConnState
58{
AppaRao Puli2a5689a2020-04-29 15:24:31 +053059 initialized,
Sunitha Harish29a82b02021-02-18 15:54:16 +053060 resolveInProgress,
61 resolveFailed,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053062 connectInProgress,
63 connectFailed,
AppaRao Pulibd030d02020-03-20 03:34:29 +053064 connected,
AppaRao Pulie38778a2022-06-27 23:09:03 +000065 handshakeInProgress,
66 handshakeFailed,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053067 sendInProgress,
68 sendFailed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053069 recvInProgress,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053070 recvFailed,
71 idle,
Ayushi Smritife44eb02020-05-15 15:24:45 +053072 closed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053073 suspended,
74 terminated,
75 abortConnection,
AppaRao Pulie38778a2022-06-27 23:09:03 +000076 sslInitFailed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053077 retry
AppaRao Pulibd030d02020-03-20 03:34:29 +053078};
79
Carson Labradoa7a80292022-06-01 16:01:52 +000080static inline boost::system::error_code
81 defaultRetryHandler(unsigned int respCode)
82{
83 // As a default, assume 200X is alright
84 BMCWEB_LOG_DEBUG << "Using default check for response code validity";
85 if ((respCode < 200) || (respCode >= 300))
86 {
87 return boost::system::errc::make_error_code(
88 boost::system::errc::result_out_of_range);
89 }
90
91 // Return 0 if the response code is valid
92 return boost::system::errc::make_error_code(boost::system::errc::success);
93};
94
Carson Labradof52c03c2022-03-23 18:50:15 +000095// We need to allow retry information to be set before a message has been sent
96// and a connection pool has been created
97struct RetryPolicyData
98{
99 uint32_t maxRetryAttempts = 5;
100 std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
101 std::string retryPolicyAction = "TerminateAfterRetries";
Carson Labradoa7a80292022-06-01 16:01:52 +0000102 std::function<boost::system::error_code(unsigned int respCode)>
103 invalidResp = defaultRetryHandler;
Carson Labradof52c03c2022-03-23 18:50:15 +0000104};
105
106struct PendingRequest
107{
Carson Labrado244256c2022-04-27 17:16:32 +0000108 boost::beast::http::request<boost::beast::http::string_body> req;
Carson Labrado039a47e2022-04-05 16:03:20 +0000109 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labradof52c03c2022-03-23 18:50:15 +0000110 RetryPolicyData retryPolicy;
Carson Labrado039a47e2022-04-05 16:03:20 +0000111 PendingRequest(
Ed Tanous8a592812022-06-04 09:06:59 -0700112 boost::beast::http::request<boost::beast::http::string_body>&& reqIn,
113 const std::function<void(bool, uint32_t, Response&)>& callbackIn,
114 const RetryPolicyData& retryPolicyIn) :
115 req(std::move(reqIn)),
116 callback(callbackIn), retryPolicy(retryPolicyIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000117 {}
118};
119
120class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
AppaRao Pulibd030d02020-03-20 03:34:29 +0530121{
122 private:
Carson Labradof52c03c2022-03-23 18:50:15 +0000123 ConnState state = ConnState::initialized;
124 uint32_t retryCount = 0;
Carson Labradof52c03c2022-03-23 18:50:15 +0000125 std::string subId;
126 std::string host;
127 uint16_t port;
128 uint32_t connId;
129
130 // Retry policy information
131 // This should be updated before each message is sent
132 RetryPolicyData retryPolicy;
133
134 // Data buffers
AppaRao Pulibd030d02020-03-20 03:34:29 +0530135 boost::beast::http::request<boost::beast::http::string_body> req;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530136 std::optional<
137 boost::beast::http::response_parser<boost::beast::http::string_body>>
138 parser;
Carson Labrado4d942722022-06-22 22:16:10 +0000139 boost::beast::flat_static_buffer<httpReadBufferSize> buffer;
Carson Labrado039a47e2022-04-05 16:03:20 +0000140 Response res;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530141
Carson Labradof52c03c2022-03-23 18:50:15 +0000142 // Ascync callables
Carson Labrado039a47e2022-04-05 16:03:20 +0000143 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labradof52c03c2022-03-23 18:50:15 +0000144 crow::async_resolve::Resolver resolver;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800145 boost::asio::ip::tcp::socket conn;
146 std::optional<boost::beast::ssl_stream<boost::asio::ip::tcp::socket&>>
147 sslConn;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000148
Carson Labradof52c03c2022-03-23 18:50:15 +0000149 boost::asio::steady_timer timer;
Ed Tanous84b35602021-09-08 20:06:32 -0700150
Carson Labradof52c03c2022-03-23 18:50:15 +0000151 friend class ConnectionPool;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530152
Sunitha Harish29a82b02021-02-18 15:54:16 +0530153 void doResolve()
154 {
Sunitha Harish29a82b02021-02-18 15:54:16 +0530155 state = ConnState::resolveInProgress;
Carson Labradof52c03c2022-03-23 18:50:15 +0000156 BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
157 << std::to_string(port)
158 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530159
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700160 resolver.asyncResolve(host, port,
161 std::bind_front(&ConnectionInfo::afterResolve,
162 this, shared_from_this()));
Sunitha Harish29a82b02021-02-18 15:54:16 +0530163 }
164
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700165 void afterResolve(
166 const std::shared_ptr<ConnectionInfo>& /*self*/,
167 const boost::beast::error_code ec,
Sunitha Harish29a82b02021-02-18 15:54:16 +0530168 const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530169 {
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700170 if (ec || (endpointList.empty()))
171 {
172 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
173 state = ConnState::resolveFailed;
174 waitAndRetry();
175 return;
176 }
177 BMCWEB_LOG_DEBUG << "Resolved " << host << ":" << std::to_string(port)
178 << ", id: " << std::to_string(connId);
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530179 state = ConnState::connectInProgress;
180
Carson Labradof52c03c2022-03-23 18:50:15 +0000181 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
182 << std::to_string(port)
183 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530184
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800185 timer.expires_after(std::chrono::seconds(30));
186 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
187
188 boost::asio::async_connect(
189 conn, endpointList,
190 std::bind_front(&ConnectionInfo::afterConnect, this,
191 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000192 }
193
194 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
195 boost::beast::error_code ec,
196 const boost::asio::ip::tcp::endpoint& endpoint)
197 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800198 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000199 if (ec)
200 {
201 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
202 << ":" << std::to_string(endpoint.port())
203 << ", id: " << std::to_string(connId)
204 << " failed: " << ec.message();
205 state = ConnState::connectFailed;
206 waitAndRetry();
207 return;
208 }
209 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
210 << ":" << std::to_string(endpoint.port())
211 << ", id: " << std::to_string(connId);
212 if (sslConn)
213 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800214 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000215 return;
216 }
217 state = ConnState::connected;
218 sendMessage();
219 }
220
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800221 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000222 {
223 if (!sslConn)
224 {
225 return;
226 }
227 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800228 timer.expires_after(std::chrono::seconds(30));
229 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000230 sslConn->async_handshake(
231 boost::asio::ssl::stream_base::client,
232 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
233 shared_from_this()));
234 }
235
236 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
237 boost::beast::error_code ec)
238 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800239 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000240 if (ec)
241 {
242 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
243 << " id: " << std::to_string(connId)
244 << " error: " << ec.message();
245 state = ConnState::handshakeFailed;
246 waitAndRetry();
247 return;
248 }
249 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
250 << " id: " << std::to_string(connId);
251 state = ConnState::connected;
252 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530253 }
254
Carson Labradof52c03c2022-03-23 18:50:15 +0000255 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530256 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530257 state = ConnState::sendInProgress;
258
AppaRao Pulibd030d02020-03-20 03:34:29 +0530259 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800260 timer.expires_after(std::chrono::seconds(30));
261 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530262
263 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000264 if (sslConn)
265 {
266 boost::beast::http::async_write(
267 *sslConn, req,
268 std::bind_front(&ConnectionInfo::afterWrite, this,
269 shared_from_this()));
270 }
271 else
272 {
273 boost::beast::http::async_write(
274 conn, req,
275 std::bind_front(&ConnectionInfo::afterWrite, this,
276 shared_from_this()));
277 }
278 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530279
AppaRao Pulie38778a2022-06-27 23:09:03 +0000280 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
281 const boost::beast::error_code& ec, size_t bytesTransferred)
282 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800283 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000284 if (ec)
285 {
286 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
287 state = ConnState::sendFailed;
288 waitAndRetry();
289 return;
290 }
291 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
292 << bytesTransferred;
293
294 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530295 }
296
297 void recvMessage()
298 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530299 state = ConnState::recvInProgress;
300
301 parser.emplace(std::piecewise_construct, std::make_tuple());
302 parser->body_limit(httpReadBodyLimit);
303
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800304 timer.expires_after(std::chrono::seconds(30));
305 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
306
AppaRao Pulibd030d02020-03-20 03:34:29 +0530307 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000308 if (sslConn)
309 {
310 boost::beast::http::async_read(
311 *sslConn, buffer, *parser,
312 std::bind_front(&ConnectionInfo::afterRead, this,
313 shared_from_this()));
314 }
315 else
316 {
317 boost::beast::http::async_read(
318 conn, buffer, *parser,
319 std::bind_front(&ConnectionInfo::afterRead, this,
320 shared_from_this()));
321 }
322 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530323
AppaRao Pulie38778a2022-06-27 23:09:03 +0000324 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
325 const boost::beast::error_code& ec,
326 const std::size_t& bytesTransferred)
327 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800328 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000329 if (ec && ec != boost::asio::ssl::error::stream_truncated)
330 {
331 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
332 state = ConnState::recvFailed;
333 waitAndRetry();
334 return;
335 }
336 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
337 << bytesTransferred;
338 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
339
340 unsigned int respCode = parser->get().result_int();
341 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
342
343 // Make sure the received response code is valid as defined by
344 // the associated retry policy
345 if (retryPolicy.invalidResp(respCode))
346 {
347 // The listener failed to receive the Sent-Event
348 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
349 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700350 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000351 state = ConnState::recvFailed;
352 waitAndRetry();
353 return;
354 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700355
AppaRao Pulie38778a2022-06-27 23:09:03 +0000356 // Send is successful
357 // Reset the counter just in case this was after retrying
358 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530359
AppaRao Pulie38778a2022-06-27 23:09:03 +0000360 // Keep the connection alive if server supports it
361 // Else close the connection
362 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
363 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530364
AppaRao Pulie38778a2022-06-27 23:09:03 +0000365 // Copy the response into a Response object so that it can be
366 // processed by the callback function.
367 res.clear();
368 res.stringResponse = parser->release();
369 callback(parser->keep_alive(), connId, res);
AppaRao Pulibd030d02020-03-20 03:34:29 +0530370 }
371
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800372 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
373 const boost::system::error_code ec)
374 {
375 if (ec == boost::asio::error::operation_aborted)
376 {
377 BMCWEB_LOG_DEBUG
378 << "async_wait failed since the operation is aborted"
379 << ec.message();
380 return;
381 }
382 if (ec)
383 {
384 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
385 // If the timer fails, we need to close the socket anyway, same as
386 // if it expired.
387 }
388 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
389 if (self == nullptr)
390 {
391 return;
392 }
393 self->waitAndRetry();
394 }
395
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530396 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530397 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000398 if ((retryCount >= retryPolicy.maxRetryAttempts) ||
399 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530400 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530401 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000402 BMCWEB_LOG_DEBUG << "Retry policy: "
403 << retryPolicy.retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000404
405 // We want to return a 502 to indicate there was an error with the
406 // external server
407 res.clear();
Ed Tanous40d799e2022-06-28 12:07:22 -0700408 res.result(boost::beast::http::status::bad_gateway);
Carson Labrado039a47e2022-04-05 16:03:20 +0000409
Carson Labradof52c03c2022-03-23 18:50:15 +0000410 if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530411 {
412 // TODO: delete subscription
413 state = ConnState::terminated;
Carson Labrado039a47e2022-04-05 16:03:20 +0000414 callback(false, connId, res);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530415 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000416 if (retryPolicy.retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530417 {
418 state = ConnState::suspended;
Carson Labrado039a47e2022-04-05 16:03:20 +0000419 callback(false, connId, res);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530420 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530421 // Reset the retrycount to zero so that client can try connecting
422 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700423 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530424 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530425 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530426
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530427 retryCount++;
428
Carson Labradof52c03c2022-03-23 18:50:15 +0000429 BMCWEB_LOG_DEBUG << "Attempt retry after "
430 << std::to_string(
431 retryPolicy.retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530432 << " seconds. RetryCount = " << retryCount;
Carson Labradof52c03c2022-03-23 18:50:15 +0000433 timer.expires_after(retryPolicy.retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700434 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
435 shared_from_this()));
436 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530437
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700438 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
439 const boost::system::error_code& ec)
440 {
441 if (ec == boost::asio::error::operation_aborted)
442 {
443 BMCWEB_LOG_DEBUG
444 << "async_wait failed since the operation is aborted"
445 << ec.message();
446 }
447 else if (ec)
448 {
449 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
450 // Ignore the error and continue the retry loop to attempt
451 // sending the event as per the retry policy
452 }
453
454 // Let's close the connection and restart from resolve.
455 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530456 }
457
AppaRao Pulie38778a2022-06-27 23:09:03 +0000458 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530459 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000460 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800461 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000462 conn.close();
463
464 // not_connected happens sometimes so don't bother reporting it.
465 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530466 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000467 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
468 << ", id: " << std::to_string(connId)
469 << "shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000470 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000471 else
472 {
473 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
474 << ", id: " << std::to_string(connId)
475 << " closed gracefully";
476 }
Ed Tanousca723762022-06-28 19:40:39 -0700477
AppaRao Pulie38778a2022-06-27 23:09:03 +0000478 if ((state != ConnState::suspended) && (state != ConnState::terminated))
479 {
480 if (retry)
481 {
482 // Now let's try to resend the data
483 state = ConnState::retry;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800484 doResolve();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000485 }
486 else
487 {
488 state = ConnState::closed;
489 }
490 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000491 }
492
AppaRao Pulie38778a2022-06-27 23:09:03 +0000493 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000494 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000495 if (!sslConn)
496 {
497 shutdownConn(retry);
498 return;
499 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000500
AppaRao Pulie38778a2022-06-27 23:09:03 +0000501 sslConn->async_shutdown(
502 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
503 shared_from_this(), retry));
504 }
505
506 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
507 bool retry, const boost::system::error_code& ec)
508 {
509
510 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000511 {
512 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
513 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000514 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000515 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000516 else
517 {
518 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
519 << ", id: " << std::to_string(connId)
520 << " closed gracefully";
521 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000522 shutdownConn(retry);
523 }
Ed Tanousca723762022-06-28 19:40:39 -0700524
AppaRao Pulie38778a2022-06-27 23:09:03 +0000525 void setCipherSuiteTLSext()
526 {
527 if (!sslConn)
528 {
529 return;
530 }
531 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
532 // file but its having old style casting (name is cast to void*).
533 // Since bmcweb compiler treats all old-style-cast as error, its
534 // causing the build failure. So replaced the same macro inline and
535 // did corrected the code by doing static_cast to viod*. This has to
536 // be fixed in openssl library in long run. Set SNI Hostname (many
537 // hosts need this to handshake successfully)
538 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
539 TLSEXT_NAMETYPE_host_name,
540 static_cast<void*>(&host.front())) == 0)
541
542 {
543 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
544 boost::asio::error::get_ssl_category()};
545
546 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
547 << port << ", id: " << std::to_string(connId)
548 << " failed: " << ec.message();
549 // Set state as sslInit failed so that we close the connection
550 // and take appropriate action as per retry configuration.
551 state = ConnState::sslInitFailed;
552 waitAndRetry();
553 return;
554 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530555 }
556
557 public:
AppaRao Pulie38778a2022-06-27 23:09:03 +0000558 explicit ConnectionInfo(boost::asio::io_context& iocIn,
559 const std::string& idIn,
560 const std::string& destIPIn, uint16_t destPortIn,
561 bool useSSL, unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700562 subId(idIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000563 host(destIPIn), port(destPortIn), connId(connIdIn), conn(iocIn),
564 timer(iocIn)
565 {
566 if (useSSL)
567 {
568 std::optional<boost::asio::ssl::context> sslCtx =
569 ensuressl::getSSLClientContext();
570
571 if (!sslCtx)
572 {
573 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
574 << port << ", id: " << std::to_string(connId);
575 // Don't retry if failure occurs while preparing SSL context
576 // such as certificate is invalid or set cipher failure or set
577 // host name failure etc... Setting conn state to sslInitFailed
578 // and connection state will be transitioned to next state
579 // depending on retry policy set by subscription.
580 state = ConnState::sslInitFailed;
581 waitAndRetry();
582 return;
583 }
584 sslConn.emplace(conn, *sslCtx);
585 setCipherSuiteTLSext();
586 }
587 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000588};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530589
Carson Labradof52c03c2022-03-23 18:50:15 +0000590class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
591{
592 private:
593 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000594 std::string id;
595 std::string destIP;
596 uint16_t destPort;
597 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000598 std::vector<std::shared_ptr<ConnectionInfo>> connections;
599 boost::container::devector<PendingRequest> requestQueue;
600
601 friend class HttpClient;
602
Carson Labrado244256c2022-04-27 17:16:32 +0000603 // Configure a connections's request, callback, and retry info in
604 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000605 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530606 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000607 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530608 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000609 BMCWEB_LOG_ERROR
610 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530611 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530612 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530613
Carson Labrado244256c2022-04-27 17:16:32 +0000614 auto nextReq = requestQueue.front();
615 conn.retryPolicy = std::move(nextReq.retryPolicy);
616 conn.req = std::move(nextReq.req);
617 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000618
619 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
620 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000621 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000622
623 // We can remove the request from the queue at this point
624 requestQueue.pop_front();
625 }
626
627 // Configures a connection to use the specific retry policy.
628 inline void setConnRetryPolicy(ConnectionInfo& conn,
629 const RetryPolicyData& retryPolicy)
630 {
631 BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
Carson Labradoa7a80292022-06-01 16:01:52 +0000632 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000633
634 conn.retryPolicy = retryPolicy;
635 }
636
637 // Gets called as part of callback after request is sent
638 // Reuses the connection if there are any requests waiting to be sent
639 // Otherwise closes the connection if it is not a keep-alive
640 void sendNext(bool keepAlive, uint32_t connId)
641 {
642 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000643
644 // Allow the connection's handler to be deleted
645 // This is needed because of Redfish Aggregation passing an
646 // AsyncResponse shared_ptr to this callback
647 conn->callback = nullptr;
648
Carson Labradof52c03c2022-03-23 18:50:15 +0000649 // Reuse the connection to send the next request in the queue
650 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530651 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000652 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
653 << " requests remaining in queue for " << destIP
654 << ":" << std::to_string(destPort)
655 << ", reusing connnection "
656 << std::to_string(connId);
657
658 setConnProps(*conn);
659
660 if (keepAlive)
661 {
662 conn->sendMessage();
663 }
664 else
665 {
666 // Server is not keep-alive enabled so we need to close the
667 // connection and then start over from resolve
668 conn->doClose();
669 conn->doResolve();
670 }
671 return;
672 }
673
674 // No more messages to send so close the connection if necessary
675 if (keepAlive)
676 {
677 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530678 }
679 else
680 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000681 // Abort the connection since server is not keep-alive enabled
682 conn->state = ConnState::abortConnection;
683 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530684 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530685 }
686
Carson Labrado244256c2022-04-27 17:16:32 +0000687 void sendData(std::string& data, const std::string& destUri,
688 const boost::beast::http::fields& httpHeader,
689 const boost::beast::http::verb verb,
690 const RetryPolicyData& retryPolicy,
Ed Tanous6b3db602022-06-28 19:41:44 -0700691 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530692 {
Carson Labrado244256c2022-04-27 17:16:32 +0000693 // Construct the request to be sent
694 boost::beast::http::request<boost::beast::http::string_body> thisReq(
695 verb, destUri, 11, "", httpHeader);
696 thisReq.set(boost::beast::http::field::host, destIP);
697 thisReq.keep_alive(true);
698 thisReq.body() = std::move(data);
699 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700700 auto cb = std::bind_front(&ConnectionPool::afterSendData,
701 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000702 // Reuse an existing connection if one is available
703 for (unsigned int i = 0; i < connections.size(); i++)
704 {
705 auto conn = connections[i];
706 if ((conn->state == ConnState::idle) ||
707 (conn->state == ConnState::initialized) ||
708 (conn->state == ConnState::closed))
709 {
Carson Labrado244256c2022-04-27 17:16:32 +0000710 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000711 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000712 setConnRetryPolicy(*conn, retryPolicy);
713 std::string commonMsg = std::to_string(i) + " from pool " +
714 destIP + ":" + std::to_string(destPort);
715
716 if (conn->state == ConnState::idle)
717 {
718 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
719 << commonMsg;
720 conn->sendMessage();
721 }
722 else
723 {
724 BMCWEB_LOG_DEBUG << "Reusing existing connection "
725 << commonMsg;
726 conn->doResolve();
727 }
728 return;
729 }
730 }
731
732 // All connections in use so create a new connection or add request to
733 // the queue
734 if (connections.size() < maxPoolSize)
735 {
736 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
737 << ":" << std::to_string(destPort);
738 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000739 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000740 conn->callback = std::move(cb);
741 setConnRetryPolicy(*conn, retryPolicy);
742 conn->doResolve();
743 }
744 else if (requestQueue.size() < maxRequestQueueSize)
745 {
746 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labrado244256c2022-04-27 17:16:32 +0000747 requestQueue.emplace_back(std::move(thisReq), std::move(cb),
Carson Labradof52c03c2022-03-23 18:50:15 +0000748 retryPolicy);
749 }
750 else
751 {
752 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
753 << " request queue full. Dropping request.";
754 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530755 }
756
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700757 // Callback to be called once the request has been sent
758 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
759 const std::function<void(Response&)>& resHandler,
760 bool keepAlive, uint32_t connId, Response& res)
761 {
762 // Allow provided callback to perform additional processing of the
763 // request
764 resHandler(res);
765
766 // If requests remain in the queue then we want to reuse this
767 // connection to send the next request
768 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
769 if (!self)
770 {
771 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
772 return;
773 }
774
775 self->sendNext(keepAlive, connId);
776 }
777
Carson Labradof52c03c2022-03-23 18:50:15 +0000778 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530779 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000780 unsigned int newId = static_cast<unsigned int>(connections.size());
781
AppaRao Pulie38778a2022-06-27 23:09:03 +0000782 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
783 ioc, id, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000784
785 BMCWEB_LOG_DEBUG << "Added connection "
786 << std::to_string(connections.size() - 1)
787 << " to pool " << destIP << ":"
788 << std::to_string(destPort);
789
790 return ret;
791 }
792
793 public:
Ed Tanous8a592812022-06-04 09:06:59 -0700794 explicit ConnectionPool(boost::asio::io_context& iocIn,
795 const std::string& idIn,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000796 const std::string& destIPIn, uint16_t destPortIn,
797 bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700798 ioc(iocIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000799 id(idIn), destIP(destIPIn), destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000800 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000801 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
802 << std::to_string(destPort);
803
804 // Initialize the pool with a single connection
805 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530806 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530807};
808
Carson Labradof52c03c2022-03-23 18:50:15 +0000809class HttpClient
810{
811 private:
812 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
813 connectionPools;
814 boost::asio::io_context& ioc =
815 crow::connections::systemBus->get_io_context();
816 std::unordered_map<std::string, RetryPolicyData> retryInfo;
817 HttpClient() = default;
818
Carson Labrado039a47e2022-04-05 16:03:20 +0000819 // Used as a dummy callback by sendData() in order to call
820 // sendDataWithCallback()
Ed Tanous02cad962022-06-30 16:50:15 -0700821 static void genericResHandler(const Response& res)
Carson Labrado039a47e2022-04-05 16:03:20 +0000822 {
823 BMCWEB_LOG_DEBUG << "Response handled with return code: "
824 << std::to_string(res.resultInt());
Ed Tanous4ee8e212022-05-28 09:42:51 -0700825 }
Carson Labrado039a47e2022-04-05 16:03:20 +0000826
Carson Labradof52c03c2022-03-23 18:50:15 +0000827 public:
828 HttpClient(const HttpClient&) = delete;
829 HttpClient& operator=(const HttpClient&) = delete;
830 HttpClient(HttpClient&&) = delete;
831 HttpClient& operator=(HttpClient&&) = delete;
832 ~HttpClient() = default;
833
834 static HttpClient& getInstance()
835 {
836 static HttpClient handler;
837 return handler;
838 }
839
Carson Labrado039a47e2022-04-05 16:03:20 +0000840 // Send a request to destIP:destPort where additional processing of the
841 // result is not required
Carson Labradof52c03c2022-03-23 18:50:15 +0000842 void sendData(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000843 const std::string& destIP, uint16_t destPort,
844 const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000845 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000846 const boost::beast::http::verb verb,
847 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000848 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000849 const std::function<void(Response&)> cb = genericResHandler;
850 sendDataWithCallback(data, id, destIP, destPort, destUri, useSSL,
851 httpHeader, verb, retryPolicyName, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000852 }
853
854 // Send request to destIP:destPort and use the provided callback to
855 // handle the response
856 void sendDataWithCallback(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000857 const std::string& destIP, uint16_t destPort,
858 const std::string& destUri, bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000859 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000860 const boost::beast::http::verb verb,
861 const std::string& retryPolicyName,
Ed Tanous6b3db602022-06-28 19:41:44 -0700862 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000863 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000864 std::string clientKey = useSSL ? "https" : "http";
865 clientKey += destIP;
866 clientKey += ":";
867 clientKey += std::to_string(destPort);
Carson Labradof52c03c2022-03-23 18:50:15 +0000868 // Use nullptr to avoid creating a ConnectionPool each time
AppaRao Pulie38778a2022-06-27 23:09:03 +0000869 std::shared_ptr<ConnectionPool>& conn = connectionPools[clientKey];
870 if (conn == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000871 {
872 // Now actually create the ConnectionPool shared_ptr since it does
873 // not already exist
AppaRao Pulie38778a2022-06-27 23:09:03 +0000874 conn = std::make_shared<ConnectionPool>(ioc, id, destIP, destPort,
875 useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000876 BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
877 }
878 else
879 {
880 BMCWEB_LOG_DEBUG << "Using existing connection pool for "
881 << clientKey;
882 }
883
884 // Get the associated retry policy
885 auto policy = retryInfo.try_emplace(retryPolicyName);
886 if (policy.second)
887 {
888 BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
889 << "\" with default values";
Carson Labradof52c03c2022-03-23 18:50:15 +0000890 }
891
892 // Send the data using either the existing connection pool or the newly
893 // created connection pool
AppaRao Pulie38778a2022-06-27 23:09:03 +0000894 conn->sendData(data, destUri, httpHeader, verb, policy.first->second,
895 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000896 }
897
Carson Labradoa7a80292022-06-01 16:01:52 +0000898 void setRetryConfig(
899 const uint32_t retryAttempts, const uint32_t retryTimeoutInterval,
900 const std::function<boost::system::error_code(unsigned int respCode)>&
901 invalidResp,
902 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000903 {
904 // We need to create the retry policy if one does not already exist for
905 // the given retryPolicyName
906 auto result = retryInfo.try_emplace(retryPolicyName);
907 if (result.second)
908 {
909 BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
910 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000911 }
912 else
913 {
914 BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
915 << retryPolicyName << "\"";
916 }
917
918 result.first->second.maxRetryAttempts = retryAttempts;
919 result.first->second.retryIntervalSecs =
920 std::chrono::seconds(retryTimeoutInterval);
Carson Labradoa7a80292022-06-01 16:01:52 +0000921 result.first->second.invalidResp = invalidResp;
Carson Labradof52c03c2022-03-23 18:50:15 +0000922 }
923
924 void setRetryPolicy(const std::string& retryPolicy,
925 const std::string& retryPolicyName)
926 {
927 // We need to create the retry policy if one does not already exist for
928 // the given retryPolicyName
929 auto result = retryInfo.try_emplace(retryPolicyName);
930 if (result.second)
931 {
932 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
933 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000934 }
935 else
936 {
937 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
938 << retryPolicyName << "\"";
939 }
940
941 result.first->second.retryPolicyAction = retryPolicy;
942 }
943};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530944} // namespace crow