blob: 6bb9159d0e4b8961063ae9779a5ab089b16e9a24 [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
160 auto respHandler =
161 [self(shared_from_this())](
162 const boost::beast::error_code ec,
163 const std::vector<boost::asio::ip::tcp::endpoint>&
164 endpointList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700165 if (ec || (endpointList.empty()))
166 {
167 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
168 self->state = ConnState::resolveFailed;
169 self->waitAndRetry();
170 return;
171 }
172 BMCWEB_LOG_DEBUG << "Resolved " << self->host << ":"
173 << std::to_string(self->port)
174 << ", id: " << std::to_string(self->connId);
175 self->doConnect(endpointList);
176 };
Carson Labradof52c03c2022-03-23 18:50:15 +0000177
Sunitha Harish29a82b02021-02-18 15:54:16 +0530178 resolver.asyncResolve(host, port, std::move(respHandler));
179 }
180
181 void doConnect(
182 const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530183 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530184 state = ConnState::connectInProgress;
185
Carson Labradof52c03c2022-03-23 18:50:15 +0000186 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
187 << std::to_string(port)
188 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530189
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800190 timer.expires_after(std::chrono::seconds(30));
191 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
192
193 boost::asio::async_connect(
194 conn, endpointList,
195 std::bind_front(&ConnectionInfo::afterConnect, this,
196 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000197 }
198
199 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
200 boost::beast::error_code ec,
201 const boost::asio::ip::tcp::endpoint& endpoint)
202 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800203 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000204 if (ec)
205 {
206 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
207 << ":" << std::to_string(endpoint.port())
208 << ", id: " << std::to_string(connId)
209 << " failed: " << ec.message();
210 state = ConnState::connectFailed;
211 waitAndRetry();
212 return;
213 }
214 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
215 << ":" << std::to_string(endpoint.port())
216 << ", id: " << std::to_string(connId);
217 if (sslConn)
218 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800219 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000220 return;
221 }
222 state = ConnState::connected;
223 sendMessage();
224 }
225
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800226 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000227 {
228 if (!sslConn)
229 {
230 return;
231 }
232 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800233 timer.expires_after(std::chrono::seconds(30));
234 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000235 sslConn->async_handshake(
236 boost::asio::ssl::stream_base::client,
237 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
238 shared_from_this()));
239 }
240
241 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
242 boost::beast::error_code ec)
243 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800244 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000245 if (ec)
246 {
247 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
248 << " id: " << std::to_string(connId)
249 << " error: " << ec.message();
250 state = ConnState::handshakeFailed;
251 waitAndRetry();
252 return;
253 }
254 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
255 << " id: " << std::to_string(connId);
256 state = ConnState::connected;
257 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530258 }
259
Carson Labradof52c03c2022-03-23 18:50:15 +0000260 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530261 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530262 state = ConnState::sendInProgress;
263
AppaRao Pulibd030d02020-03-20 03:34:29 +0530264 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800265 timer.expires_after(std::chrono::seconds(30));
266 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530267
268 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000269 if (sslConn)
270 {
271 boost::beast::http::async_write(
272 *sslConn, req,
273 std::bind_front(&ConnectionInfo::afterWrite, this,
274 shared_from_this()));
275 }
276 else
277 {
278 boost::beast::http::async_write(
279 conn, req,
280 std::bind_front(&ConnectionInfo::afterWrite, this,
281 shared_from_this()));
282 }
283 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530284
AppaRao Pulie38778a2022-06-27 23:09:03 +0000285 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
286 const boost::beast::error_code& ec, size_t bytesTransferred)
287 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800288 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000289 if (ec)
290 {
291 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
292 state = ConnState::sendFailed;
293 waitAndRetry();
294 return;
295 }
296 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
297 << bytesTransferred;
298
299 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530300 }
301
302 void recvMessage()
303 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530304 state = ConnState::recvInProgress;
305
306 parser.emplace(std::piecewise_construct, std::make_tuple());
307 parser->body_limit(httpReadBodyLimit);
308
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800309 timer.expires_after(std::chrono::seconds(30));
310 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
311
AppaRao Pulibd030d02020-03-20 03:34:29 +0530312 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000313 if (sslConn)
314 {
315 boost::beast::http::async_read(
316 *sslConn, buffer, *parser,
317 std::bind_front(&ConnectionInfo::afterRead, this,
318 shared_from_this()));
319 }
320 else
321 {
322 boost::beast::http::async_read(
323 conn, buffer, *parser,
324 std::bind_front(&ConnectionInfo::afterRead, this,
325 shared_from_this()));
326 }
327 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530328
AppaRao Pulie38778a2022-06-27 23:09:03 +0000329 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
330 const boost::beast::error_code& ec,
331 const std::size_t& bytesTransferred)
332 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800333 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000334 if (ec && ec != boost::asio::ssl::error::stream_truncated)
335 {
336 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
337 state = ConnState::recvFailed;
338 waitAndRetry();
339 return;
340 }
341 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
342 << bytesTransferred;
343 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
344
345 unsigned int respCode = parser->get().result_int();
346 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
347
348 // Make sure the received response code is valid as defined by
349 // the associated retry policy
350 if (retryPolicy.invalidResp(respCode))
351 {
352 // The listener failed to receive the Sent-Event
353 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
354 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700355 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000356 state = ConnState::recvFailed;
357 waitAndRetry();
358 return;
359 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700360
AppaRao Pulie38778a2022-06-27 23:09:03 +0000361 // Send is successful
362 // Reset the counter just in case this was after retrying
363 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530364
AppaRao Pulie38778a2022-06-27 23:09:03 +0000365 // Keep the connection alive if server supports it
366 // Else close the connection
367 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
368 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530369
AppaRao Pulie38778a2022-06-27 23:09:03 +0000370 // Copy the response into a Response object so that it can be
371 // processed by the callback function.
372 res.clear();
373 res.stringResponse = parser->release();
374 callback(parser->keep_alive(), connId, res);
AppaRao Pulibd030d02020-03-20 03:34:29 +0530375 }
376
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800377 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
378 const boost::system::error_code ec)
379 {
380 if (ec == boost::asio::error::operation_aborted)
381 {
382 BMCWEB_LOG_DEBUG
383 << "async_wait failed since the operation is aborted"
384 << ec.message();
385 return;
386 }
387 if (ec)
388 {
389 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
390 // If the timer fails, we need to close the socket anyway, same as
391 // if it expired.
392 }
393 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
394 if (self == nullptr)
395 {
396 return;
397 }
398 self->waitAndRetry();
399 }
400
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530401 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530402 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000403 if ((retryCount >= retryPolicy.maxRetryAttempts) ||
404 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530405 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530406 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000407 BMCWEB_LOG_DEBUG << "Retry policy: "
408 << retryPolicy.retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000409
410 // We want to return a 502 to indicate there was an error with the
411 // external server
412 res.clear();
Ed Tanous40d799e2022-06-28 12:07:22 -0700413 res.result(boost::beast::http::status::bad_gateway);
Carson Labrado039a47e2022-04-05 16:03:20 +0000414
Carson Labradof52c03c2022-03-23 18:50:15 +0000415 if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530416 {
417 // TODO: delete subscription
418 state = ConnState::terminated;
Carson Labrado039a47e2022-04-05 16:03:20 +0000419 callback(false, connId, res);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530420 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000421 if (retryPolicy.retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530422 {
423 state = ConnState::suspended;
Carson Labrado039a47e2022-04-05 16:03:20 +0000424 callback(false, connId, res);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530425 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530426 // Reset the retrycount to zero so that client can try connecting
427 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700428 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530429 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530430 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530431
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530432 retryCount++;
433
Carson Labradof52c03c2022-03-23 18:50:15 +0000434 BMCWEB_LOG_DEBUG << "Attempt retry after "
435 << std::to_string(
436 retryPolicy.retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530437 << " seconds. RetryCount = " << retryCount;
Carson Labradof52c03c2022-03-23 18:50:15 +0000438 timer.expires_after(retryPolicy.retryIntervalSecs);
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530439 timer.async_wait(
Carson Labradof52c03c2022-03-23 18:50:15 +0000440 [self(shared_from_this())](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700441 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 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530453
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 // Let's close the connection and restart from resolve.
AppaRao Pulie38778a2022-06-27 23:09:03 +0000455 self->doClose(true);
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 });
Ayushi Smritife44eb02020-05-15 15:24:45 +0530457 }
458
AppaRao Pulie38778a2022-06-27 23:09:03 +0000459 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530460 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000461 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800462 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000463 conn.close();
464
465 // not_connected happens sometimes so don't bother reporting it.
466 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530467 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000468 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
469 << ", id: " << std::to_string(connId)
470 << "shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000471 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000472 else
473 {
474 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
475 << ", id: " << std::to_string(connId)
476 << " closed gracefully";
477 }
Ed Tanousca723762022-06-28 19:40:39 -0700478
AppaRao Pulie38778a2022-06-27 23:09:03 +0000479 if ((state != ConnState::suspended) && (state != ConnState::terminated))
480 {
481 if (retry)
482 {
483 // Now let's try to resend the data
484 state = ConnState::retry;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800485 doResolve();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000486 }
487 else
488 {
489 state = ConnState::closed;
490 }
491 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000492 }
493
AppaRao Pulie38778a2022-06-27 23:09:03 +0000494 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000495 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000496 if (!sslConn)
497 {
498 shutdownConn(retry);
499 return;
500 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000501
AppaRao Pulie38778a2022-06-27 23:09:03 +0000502 sslConn->async_shutdown(
503 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
504 shared_from_this(), retry));
505 }
506
507 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
508 bool retry, const boost::system::error_code& ec)
509 {
510
511 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000512 {
513 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
514 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000515 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000516 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000517 else
518 {
519 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
520 << ", id: " << std::to_string(connId)
521 << " closed gracefully";
522 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000523 shutdownConn(retry);
524 }
Ed Tanousca723762022-06-28 19:40:39 -0700525
AppaRao Pulie38778a2022-06-27 23:09:03 +0000526 void setCipherSuiteTLSext()
527 {
528 if (!sslConn)
529 {
530 return;
531 }
532 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
533 // file but its having old style casting (name is cast to void*).
534 // Since bmcweb compiler treats all old-style-cast as error, its
535 // causing the build failure. So replaced the same macro inline and
536 // did corrected the code by doing static_cast to viod*. This has to
537 // be fixed in openssl library in long run. Set SNI Hostname (many
538 // hosts need this to handshake successfully)
539 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
540 TLSEXT_NAMETYPE_host_name,
541 static_cast<void*>(&host.front())) == 0)
542
543 {
544 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
545 boost::asio::error::get_ssl_category()};
546
547 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
548 << port << ", id: " << std::to_string(connId)
549 << " failed: " << ec.message();
550 // Set state as sslInit failed so that we close the connection
551 // and take appropriate action as per retry configuration.
552 state = ConnState::sslInitFailed;
553 waitAndRetry();
554 return;
555 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530556 }
557
558 public:
AppaRao Pulie38778a2022-06-27 23:09:03 +0000559 explicit ConnectionInfo(boost::asio::io_context& iocIn,
560 const std::string& idIn,
561 const std::string& destIPIn, uint16_t destPortIn,
562 bool useSSL, unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700563 subId(idIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000564 host(destIPIn), port(destPortIn), connId(connIdIn), conn(iocIn),
565 timer(iocIn)
566 {
567 if (useSSL)
568 {
569 std::optional<boost::asio::ssl::context> sslCtx =
570 ensuressl::getSSLClientContext();
571
572 if (!sslCtx)
573 {
574 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
575 << port << ", id: " << std::to_string(connId);
576 // Don't retry if failure occurs while preparing SSL context
577 // such as certificate is invalid or set cipher failure or set
578 // host name failure etc... Setting conn state to sslInitFailed
579 // and connection state will be transitioned to next state
580 // depending on retry policy set by subscription.
581 state = ConnState::sslInitFailed;
582 waitAndRetry();
583 return;
584 }
585 sslConn.emplace(conn, *sslCtx);
586 setCipherSuiteTLSext();
587 }
588 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000589};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530590
Carson Labradof52c03c2022-03-23 18:50:15 +0000591class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
592{
593 private:
594 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000595 std::string id;
596 std::string destIP;
597 uint16_t destPort;
598 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000599 std::vector<std::shared_ptr<ConnectionInfo>> connections;
600 boost::container::devector<PendingRequest> requestQueue;
601
602 friend class HttpClient;
603
Carson Labrado244256c2022-04-27 17:16:32 +0000604 // Configure a connections's request, callback, and retry info in
605 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000606 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530607 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000608 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530609 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000610 BMCWEB_LOG_ERROR
611 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530612 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530613 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530614
Carson Labrado244256c2022-04-27 17:16:32 +0000615 auto nextReq = requestQueue.front();
616 conn.retryPolicy = std::move(nextReq.retryPolicy);
617 conn.req = std::move(nextReq.req);
618 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000619
620 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
621 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000622 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000623
624 // We can remove the request from the queue at this point
625 requestQueue.pop_front();
626 }
627
628 // Configures a connection to use the specific retry policy.
629 inline void setConnRetryPolicy(ConnectionInfo& conn,
630 const RetryPolicyData& retryPolicy)
631 {
632 BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
Carson Labradoa7a80292022-06-01 16:01:52 +0000633 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000634
635 conn.retryPolicy = retryPolicy;
636 }
637
638 // Gets called as part of callback after request is sent
639 // Reuses the connection if there are any requests waiting to be sent
640 // Otherwise closes the connection if it is not a keep-alive
641 void sendNext(bool keepAlive, uint32_t connId)
642 {
643 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000644
645 // Allow the connection's handler to be deleted
646 // This is needed because of Redfish Aggregation passing an
647 // AsyncResponse shared_ptr to this callback
648 conn->callback = nullptr;
649
Carson Labradof52c03c2022-03-23 18:50:15 +0000650 // Reuse the connection to send the next request in the queue
651 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530652 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000653 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
654 << " requests remaining in queue for " << destIP
655 << ":" << std::to_string(destPort)
656 << ", reusing connnection "
657 << std::to_string(connId);
658
659 setConnProps(*conn);
660
661 if (keepAlive)
662 {
663 conn->sendMessage();
664 }
665 else
666 {
667 // Server is not keep-alive enabled so we need to close the
668 // connection and then start over from resolve
669 conn->doClose();
670 conn->doResolve();
671 }
672 return;
673 }
674
675 // No more messages to send so close the connection if necessary
676 if (keepAlive)
677 {
678 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530679 }
680 else
681 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000682 // Abort the connection since server is not keep-alive enabled
683 conn->state = ConnState::abortConnection;
684 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530685 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530686 }
687
Carson Labrado244256c2022-04-27 17:16:32 +0000688 void sendData(std::string& data, const std::string& destUri,
689 const boost::beast::http::fields& httpHeader,
690 const boost::beast::http::verb verb,
691 const RetryPolicyData& retryPolicy,
Ed Tanous6b3db602022-06-28 19:41:44 -0700692 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530693 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000694 std::weak_ptr<ConnectionPool> weakSelf = weak_from_this();
695
696 // Callback to be called once the request has been sent
Carson Labrado039a47e2022-04-05 16:03:20 +0000697 auto cb = [weakSelf, resHandler](bool keepAlive, uint32_t connId,
698 Response& res) {
699 // Allow provided callback to perform additional processing of the
700 // request
701 resHandler(res);
702
Carson Labradof52c03c2022-03-23 18:50:15 +0000703 // If requests remain in the queue then we want to reuse this
704 // connection to send the next request
705 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
706 if (!self)
707 {
708 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
709 return;
710 }
711
712 self->sendNext(keepAlive, connId);
713 };
714
Carson Labrado244256c2022-04-27 17:16:32 +0000715 // Construct the request to be sent
716 boost::beast::http::request<boost::beast::http::string_body> thisReq(
717 verb, destUri, 11, "", httpHeader);
718 thisReq.set(boost::beast::http::field::host, destIP);
719 thisReq.keep_alive(true);
720 thisReq.body() = std::move(data);
721 thisReq.prepare_payload();
722
Carson Labradof52c03c2022-03-23 18:50:15 +0000723 // Reuse an existing connection if one is available
724 for (unsigned int i = 0; i < connections.size(); i++)
725 {
726 auto conn = connections[i];
727 if ((conn->state == ConnState::idle) ||
728 (conn->state == ConnState::initialized) ||
729 (conn->state == ConnState::closed))
730 {
Carson Labrado244256c2022-04-27 17:16:32 +0000731 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000732 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000733 setConnRetryPolicy(*conn, retryPolicy);
734 std::string commonMsg = std::to_string(i) + " from pool " +
735 destIP + ":" + std::to_string(destPort);
736
737 if (conn->state == ConnState::idle)
738 {
739 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
740 << commonMsg;
741 conn->sendMessage();
742 }
743 else
744 {
745 BMCWEB_LOG_DEBUG << "Reusing existing connection "
746 << commonMsg;
747 conn->doResolve();
748 }
749 return;
750 }
751 }
752
753 // All connections in use so create a new connection or add request to
754 // the queue
755 if (connections.size() < maxPoolSize)
756 {
757 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
758 << ":" << std::to_string(destPort);
759 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000760 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000761 conn->callback = std::move(cb);
762 setConnRetryPolicy(*conn, retryPolicy);
763 conn->doResolve();
764 }
765 else if (requestQueue.size() < maxRequestQueueSize)
766 {
767 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labrado244256c2022-04-27 17:16:32 +0000768 requestQueue.emplace_back(std::move(thisReq), std::move(cb),
Carson Labradof52c03c2022-03-23 18:50:15 +0000769 retryPolicy);
770 }
771 else
772 {
773 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
774 << " request queue full. Dropping request.";
775 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530776 }
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