blob: cceb00761ac6799ef472699f9edfcc76346b2148 [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
Nan Zhou77665bd2022-10-12 20:28:58 +000017
18#include "async_resolve.hpp"
19#include "http_response.hpp"
20
Ed Tanous0d5f5cf2022-03-12 15:30:55 -080021#include <boost/asio/connect.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070022#include <boost/asio/io_context.hpp>
Sunitha Harish29a82b02021-02-18 15:54:16 +053023#include <boost/asio/ip/address.hpp>
24#include <boost/asio/ip/basic_endpoint.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070025#include <boost/asio/ip/tcp.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000026#include <boost/asio/ssl/context.hpp>
27#include <boost/asio/ssl/error.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070028#include <boost/asio/steady_timer.hpp>
29#include <boost/beast/core/flat_buffer.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070030#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070031#include <boost/beast/http/message.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070032#include <boost/beast/http/parser.hpp>
33#include <boost/beast/http/read.hpp>
34#include <boost/beast/http/string_body.hpp>
35#include <boost/beast/http/write.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000036#include <boost/beast/ssl/ssl_stream.hpp>
AppaRao Pulibd030d02020-03-20 03:34:29 +053037#include <boost/beast/version.hpp>
Carson Labradof52c03c2022-03-23 18:50:15 +000038#include <boost/container/devector.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070039#include <boost/system/error_code.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070040#include <logging.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000041#include <ssl_key_handler.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042
AppaRao Pulibd030d02020-03-20 03:34:29 +053043#include <cstdlib>
44#include <functional>
45#include <iostream>
46#include <memory>
AppaRao Puli2a5689a2020-04-29 15:24:31 +053047#include <queue>
AppaRao Pulibd030d02020-03-20 03:34:29 +053048#include <string>
49
50namespace crow
51{
52
Carson Labradof52c03c2022-03-23 18:50:15 +000053// It is assumed that the BMC should be able to handle 4 parallel connections
54constexpr uint8_t maxPoolSize = 4;
55constexpr uint8_t maxRequestQueueSize = 50;
Carson Labrado17dcc312022-07-28 22:17:28 +000056constexpr unsigned int httpReadBodyLimit = 131072;
Carson Labrado4d942722022-06-22 22:16:10 +000057constexpr unsigned int httpReadBufferSize = 4096;
AppaRao Puli2a5689a2020-04-29 15:24:31 +053058
AppaRao Pulibd030d02020-03-20 03:34:29 +053059enum class ConnState
60{
AppaRao Puli2a5689a2020-04-29 15:24:31 +053061 initialized,
Sunitha Harish29a82b02021-02-18 15:54:16 +053062 resolveInProgress,
63 resolveFailed,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053064 connectInProgress,
65 connectFailed,
AppaRao Pulibd030d02020-03-20 03:34:29 +053066 connected,
AppaRao Pulie38778a2022-06-27 23:09:03 +000067 handshakeInProgress,
68 handshakeFailed,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053069 sendInProgress,
70 sendFailed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053071 recvInProgress,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053072 recvFailed,
73 idle,
Ayushi Smritife44eb02020-05-15 15:24:45 +053074 closed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053075 suspended,
76 terminated,
77 abortConnection,
AppaRao Pulie38778a2022-06-27 23:09:03 +000078 sslInitFailed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053079 retry
AppaRao Pulibd030d02020-03-20 03:34:29 +053080};
81
Carson Labradoa7a80292022-06-01 16:01:52 +000082static inline boost::system::error_code
83 defaultRetryHandler(unsigned int respCode)
84{
85 // As a default, assume 200X is alright
86 BMCWEB_LOG_DEBUG << "Using default check for response code validity";
87 if ((respCode < 200) || (respCode >= 300))
88 {
89 return boost::system::errc::make_error_code(
90 boost::system::errc::result_out_of_range);
91 }
92
93 // Return 0 if the response code is valid
94 return boost::system::errc::make_error_code(boost::system::errc::success);
95};
96
Carson Labradof52c03c2022-03-23 18:50:15 +000097// We need to allow retry information to be set before a message has been sent
98// and a connection pool has been created
99struct RetryPolicyData
100{
101 uint32_t maxRetryAttempts = 5;
102 std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
103 std::string retryPolicyAction = "TerminateAfterRetries";
Carson Labradoa7a80292022-06-01 16:01:52 +0000104 std::function<boost::system::error_code(unsigned int respCode)>
105 invalidResp = defaultRetryHandler;
Carson Labradof52c03c2022-03-23 18:50:15 +0000106};
107
108struct PendingRequest
109{
Carson Labrado244256c2022-04-27 17:16:32 +0000110 boost::beast::http::request<boost::beast::http::string_body> req;
Carson Labrado039a47e2022-04-05 16:03:20 +0000111 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labradof52c03c2022-03-23 18:50:15 +0000112 RetryPolicyData retryPolicy;
Carson Labrado039a47e2022-04-05 16:03:20 +0000113 PendingRequest(
Ed Tanous8a592812022-06-04 09:06:59 -0700114 boost::beast::http::request<boost::beast::http::string_body>&& reqIn,
115 const std::function<void(bool, uint32_t, Response&)>& callbackIn,
116 const RetryPolicyData& retryPolicyIn) :
117 req(std::move(reqIn)),
118 callback(callbackIn), retryPolicy(retryPolicyIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000119 {}
120};
121
122class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
AppaRao Pulibd030d02020-03-20 03:34:29 +0530123{
124 private:
Carson Labradof52c03c2022-03-23 18:50:15 +0000125 ConnState state = ConnState::initialized;
126 uint32_t retryCount = 0;
Carson Labradof52c03c2022-03-23 18:50:15 +0000127 std::string subId;
128 std::string host;
129 uint16_t port;
130 uint32_t connId;
131
132 // Retry policy information
133 // This should be updated before each message is sent
134 RetryPolicyData retryPolicy;
135
136 // Data buffers
AppaRao Pulibd030d02020-03-20 03:34:29 +0530137 boost::beast::http::request<boost::beast::http::string_body> req;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530138 std::optional<
139 boost::beast::http::response_parser<boost::beast::http::string_body>>
140 parser;
Carson Labrado4d942722022-06-22 22:16:10 +0000141 boost::beast::flat_static_buffer<httpReadBufferSize> buffer;
Carson Labrado039a47e2022-04-05 16:03:20 +0000142 Response res;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530143
Carson Labradof52c03c2022-03-23 18:50:15 +0000144 // Ascync callables
Carson Labrado039a47e2022-04-05 16:03:20 +0000145 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labradof52c03c2022-03-23 18:50:15 +0000146 crow::async_resolve::Resolver resolver;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800147 boost::asio::ip::tcp::socket conn;
148 std::optional<boost::beast::ssl_stream<boost::asio::ip::tcp::socket&>>
149 sslConn;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000150
Carson Labradof52c03c2022-03-23 18:50:15 +0000151 boost::asio::steady_timer timer;
Ed Tanous84b35602021-09-08 20:06:32 -0700152
Carson Labradof52c03c2022-03-23 18:50:15 +0000153 friend class ConnectionPool;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530154
Sunitha Harish29a82b02021-02-18 15:54:16 +0530155 void doResolve()
156 {
Sunitha Harish29a82b02021-02-18 15:54:16 +0530157 state = ConnState::resolveInProgress;
Carson Labradof52c03c2022-03-23 18:50:15 +0000158 BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
159 << std::to_string(port)
160 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530161
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700162 resolver.asyncResolve(host, port,
163 std::bind_front(&ConnectionInfo::afterResolve,
164 this, shared_from_this()));
Sunitha Harish29a82b02021-02-18 15:54:16 +0530165 }
166
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700167 void afterResolve(
168 const std::shared_ptr<ConnectionInfo>& /*self*/,
169 const boost::beast::error_code ec,
Sunitha Harish29a82b02021-02-18 15:54:16 +0530170 const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530171 {
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700172 if (ec || (endpointList.empty()))
173 {
174 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
175 state = ConnState::resolveFailed;
176 waitAndRetry();
177 return;
178 }
179 BMCWEB_LOG_DEBUG << "Resolved " << host << ":" << std::to_string(port)
180 << ", id: " << std::to_string(connId);
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530181 state = ConnState::connectInProgress;
182
Carson Labradof52c03c2022-03-23 18:50:15 +0000183 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
184 << std::to_string(port)
185 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530186
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800187 timer.expires_after(std::chrono::seconds(30));
188 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
189
190 boost::asio::async_connect(
191 conn, endpointList,
192 std::bind_front(&ConnectionInfo::afterConnect, this,
193 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000194 }
195
196 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
197 boost::beast::error_code ec,
198 const boost::asio::ip::tcp::endpoint& endpoint)
199 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800200 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000201 if (ec)
202 {
203 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
204 << ":" << std::to_string(endpoint.port())
205 << ", id: " << std::to_string(connId)
206 << " failed: " << ec.message();
207 state = ConnState::connectFailed;
208 waitAndRetry();
209 return;
210 }
211 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
212 << ":" << std::to_string(endpoint.port())
213 << ", id: " << std::to_string(connId);
214 if (sslConn)
215 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800216 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000217 return;
218 }
219 state = ConnState::connected;
220 sendMessage();
221 }
222
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800223 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000224 {
225 if (!sslConn)
226 {
227 return;
228 }
229 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800230 timer.expires_after(std::chrono::seconds(30));
231 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000232 sslConn->async_handshake(
233 boost::asio::ssl::stream_base::client,
234 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
235 shared_from_this()));
236 }
237
238 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
239 boost::beast::error_code ec)
240 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800241 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000242 if (ec)
243 {
244 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
245 << " id: " << std::to_string(connId)
246 << " error: " << ec.message();
247 state = ConnState::handshakeFailed;
248 waitAndRetry();
249 return;
250 }
251 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
252 << " id: " << std::to_string(connId);
253 state = ConnState::connected;
254 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530255 }
256
Carson Labradof52c03c2022-03-23 18:50:15 +0000257 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530258 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530259 state = ConnState::sendInProgress;
260
AppaRao Pulibd030d02020-03-20 03:34:29 +0530261 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800262 timer.expires_after(std::chrono::seconds(30));
263 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530264
265 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000266 if (sslConn)
267 {
268 boost::beast::http::async_write(
269 *sslConn, req,
270 std::bind_front(&ConnectionInfo::afterWrite, this,
271 shared_from_this()));
272 }
273 else
274 {
275 boost::beast::http::async_write(
276 conn, req,
277 std::bind_front(&ConnectionInfo::afterWrite, this,
278 shared_from_this()));
279 }
280 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530281
AppaRao Pulie38778a2022-06-27 23:09:03 +0000282 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
283 const boost::beast::error_code& ec, size_t bytesTransferred)
284 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800285 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000286 if (ec)
287 {
288 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
289 state = ConnState::sendFailed;
290 waitAndRetry();
291 return;
292 }
293 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
294 << bytesTransferred;
295
296 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530297 }
298
299 void recvMessage()
300 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530301 state = ConnState::recvInProgress;
302
303 parser.emplace(std::piecewise_construct, std::make_tuple());
304 parser->body_limit(httpReadBodyLimit);
305
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800306 timer.expires_after(std::chrono::seconds(30));
307 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
308
AppaRao Pulibd030d02020-03-20 03:34:29 +0530309 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000310 if (sslConn)
311 {
312 boost::beast::http::async_read(
313 *sslConn, buffer, *parser,
314 std::bind_front(&ConnectionInfo::afterRead, this,
315 shared_from_this()));
316 }
317 else
318 {
319 boost::beast::http::async_read(
320 conn, buffer, *parser,
321 std::bind_front(&ConnectionInfo::afterRead, this,
322 shared_from_this()));
323 }
324 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530325
AppaRao Pulie38778a2022-06-27 23:09:03 +0000326 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
327 const boost::beast::error_code& ec,
328 const std::size_t& bytesTransferred)
329 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800330 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000331 if (ec && ec != boost::asio::ssl::error::stream_truncated)
332 {
333 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
334 state = ConnState::recvFailed;
335 waitAndRetry();
336 return;
337 }
338 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
339 << bytesTransferred;
340 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
341
342 unsigned int respCode = parser->get().result_int();
343 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
344
345 // Make sure the received response code is valid as defined by
346 // the associated retry policy
347 if (retryPolicy.invalidResp(respCode))
348 {
349 // The listener failed to receive the Sent-Event
350 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
351 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700352 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000353 state = ConnState::recvFailed;
354 waitAndRetry();
355 return;
356 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700357
AppaRao Pulie38778a2022-06-27 23:09:03 +0000358 // Send is successful
359 // Reset the counter just in case this was after retrying
360 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530361
AppaRao Pulie38778a2022-06-27 23:09:03 +0000362 // Keep the connection alive if server supports it
363 // Else close the connection
364 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
365 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530366
AppaRao Pulie38778a2022-06-27 23:09:03 +0000367 // Copy the response into a Response object so that it can be
368 // processed by the callback function.
369 res.clear();
370 res.stringResponse = parser->release();
371 callback(parser->keep_alive(), connId, res);
AppaRao Pulibd030d02020-03-20 03:34:29 +0530372 }
373
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800374 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
375 const boost::system::error_code ec)
376 {
377 if (ec == boost::asio::error::operation_aborted)
378 {
379 BMCWEB_LOG_DEBUG
380 << "async_wait failed since the operation is aborted"
381 << ec.message();
382 return;
383 }
384 if (ec)
385 {
386 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
387 // If the timer fails, we need to close the socket anyway, same as
388 // if it expired.
389 }
390 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
391 if (self == nullptr)
392 {
393 return;
394 }
395 self->waitAndRetry();
396 }
397
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530398 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530399 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000400 if ((retryCount >= retryPolicy.maxRetryAttempts) ||
401 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530402 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530403 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000404 BMCWEB_LOG_DEBUG << "Retry policy: "
405 << retryPolicy.retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000406
407 // We want to return a 502 to indicate there was an error with the
408 // external server
409 res.clear();
Ed Tanous40d799e2022-06-28 12:07:22 -0700410 res.result(boost::beast::http::status::bad_gateway);
Carson Labrado039a47e2022-04-05 16:03:20 +0000411
Carson Labradof52c03c2022-03-23 18:50:15 +0000412 if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530413 {
414 // TODO: delete subscription
415 state = ConnState::terminated;
Carson Labrado039a47e2022-04-05 16:03:20 +0000416 callback(false, connId, res);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530417 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000418 if (retryPolicy.retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530419 {
420 state = ConnState::suspended;
Carson Labrado039a47e2022-04-05 16:03:20 +0000421 callback(false, connId, res);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530422 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530423 // Reset the retrycount to zero so that client can try connecting
424 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700425 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530426 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530427 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530428
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530429 retryCount++;
430
Carson Labradof52c03c2022-03-23 18:50:15 +0000431 BMCWEB_LOG_DEBUG << "Attempt retry after "
432 << std::to_string(
433 retryPolicy.retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530434 << " seconds. RetryCount = " << retryCount;
Carson Labradof52c03c2022-03-23 18:50:15 +0000435 timer.expires_after(retryPolicy.retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700436 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
437 shared_from_this()));
438 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530439
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700440 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
441 const boost::system::error_code& ec)
442 {
443 if (ec == boost::asio::error::operation_aborted)
444 {
445 BMCWEB_LOG_DEBUG
446 << "async_wait failed since the operation is aborted"
447 << ec.message();
448 }
449 else if (ec)
450 {
451 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
452 // Ignore the error and continue the retry loop to attempt
453 // sending the event as per the retry policy
454 }
455
456 // Let's close the connection and restart from resolve.
457 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530458 }
459
AppaRao Pulie38778a2022-06-27 23:09:03 +0000460 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530461 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000462 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800463 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000464 conn.close();
465
466 // not_connected happens sometimes so don't bother reporting it.
467 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530468 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000469 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
470 << ", id: " << std::to_string(connId)
471 << "shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000472 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000473 else
474 {
475 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
476 << ", id: " << std::to_string(connId)
477 << " closed gracefully";
478 }
Ed Tanousca723762022-06-28 19:40:39 -0700479
AppaRao Pulie38778a2022-06-27 23:09:03 +0000480 if ((state != ConnState::suspended) && (state != ConnState::terminated))
481 {
482 if (retry)
483 {
484 // Now let's try to resend the data
485 state = ConnState::retry;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800486 doResolve();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000487 }
488 else
489 {
490 state = ConnState::closed;
491 }
492 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000493 }
494
AppaRao Pulie38778a2022-06-27 23:09:03 +0000495 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000496 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000497 if (!sslConn)
498 {
499 shutdownConn(retry);
500 return;
501 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000502
AppaRao Pulie38778a2022-06-27 23:09:03 +0000503 sslConn->async_shutdown(
504 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
505 shared_from_this(), retry));
506 }
507
508 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
509 bool retry, const boost::system::error_code& ec)
510 {
511
512 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000513 {
514 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
515 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000516 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000517 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000518 else
519 {
520 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
521 << ", id: " << std::to_string(connId)
522 << " closed gracefully";
523 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000524 shutdownConn(retry);
525 }
Ed Tanousca723762022-06-28 19:40:39 -0700526
AppaRao Pulie38778a2022-06-27 23:09:03 +0000527 void setCipherSuiteTLSext()
528 {
529 if (!sslConn)
530 {
531 return;
532 }
533 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
534 // file but its having old style casting (name is cast to void*).
535 // Since bmcweb compiler treats all old-style-cast as error, its
536 // causing the build failure. So replaced the same macro inline and
537 // did corrected the code by doing static_cast to viod*. This has to
538 // be fixed in openssl library in long run. Set SNI Hostname (many
539 // hosts need this to handshake successfully)
540 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
541 TLSEXT_NAMETYPE_host_name,
542 static_cast<void*>(&host.front())) == 0)
543
544 {
545 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
546 boost::asio::error::get_ssl_category()};
547
548 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
549 << port << ", id: " << std::to_string(connId)
550 << " failed: " << ec.message();
551 // Set state as sslInit failed so that we close the connection
552 // and take appropriate action as per retry configuration.
553 state = ConnState::sslInitFailed;
554 waitAndRetry();
555 return;
556 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530557 }
558
559 public:
AppaRao Pulie38778a2022-06-27 23:09:03 +0000560 explicit ConnectionInfo(boost::asio::io_context& iocIn,
561 const std::string& idIn,
562 const std::string& destIPIn, uint16_t destPortIn,
563 bool useSSL, unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700564 subId(idIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000565 host(destIPIn), port(destPortIn), connId(connIdIn), conn(iocIn),
566 timer(iocIn)
567 {
568 if (useSSL)
569 {
570 std::optional<boost::asio::ssl::context> sslCtx =
571 ensuressl::getSSLClientContext();
572
573 if (!sslCtx)
574 {
575 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
576 << port << ", id: " << std::to_string(connId);
577 // Don't retry if failure occurs while preparing SSL context
578 // such as certificate is invalid or set cipher failure or set
579 // host name failure etc... Setting conn state to sslInitFailed
580 // and connection state will be transitioned to next state
581 // depending on retry policy set by subscription.
582 state = ConnState::sslInitFailed;
583 waitAndRetry();
584 return;
585 }
586 sslConn.emplace(conn, *sslCtx);
587 setCipherSuiteTLSext();
588 }
589 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000590};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530591
Carson Labradof52c03c2022-03-23 18:50:15 +0000592class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
593{
594 private:
595 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000596 std::string id;
597 std::string destIP;
598 uint16_t destPort;
599 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000600 std::vector<std::shared_ptr<ConnectionInfo>> connections;
601 boost::container::devector<PendingRequest> requestQueue;
602
603 friend class HttpClient;
604
Carson Labrado244256c2022-04-27 17:16:32 +0000605 // Configure a connections's request, callback, and retry info in
606 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000607 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530608 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000609 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530610 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000611 BMCWEB_LOG_ERROR
612 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530613 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530614 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530615
Carson Labrado244256c2022-04-27 17:16:32 +0000616 auto nextReq = requestQueue.front();
617 conn.retryPolicy = std::move(nextReq.retryPolicy);
618 conn.req = std::move(nextReq.req);
619 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000620
621 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
622 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000623 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000624
625 // We can remove the request from the queue at this point
626 requestQueue.pop_front();
627 }
628
629 // Configures a connection to use the specific retry policy.
630 inline void setConnRetryPolicy(ConnectionInfo& conn,
631 const RetryPolicyData& retryPolicy)
632 {
633 BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
Carson Labradoa7a80292022-06-01 16:01:52 +0000634 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000635
636 conn.retryPolicy = retryPolicy;
637 }
638
639 // Gets called as part of callback after request is sent
640 // Reuses the connection if there are any requests waiting to be sent
641 // Otherwise closes the connection if it is not a keep-alive
642 void sendNext(bool keepAlive, uint32_t connId)
643 {
644 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000645
646 // Allow the connection's handler to be deleted
647 // This is needed because of Redfish Aggregation passing an
648 // AsyncResponse shared_ptr to this callback
649 conn->callback = nullptr;
650
Carson Labradof52c03c2022-03-23 18:50:15 +0000651 // Reuse the connection to send the next request in the queue
652 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530653 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000654 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
655 << " requests remaining in queue for " << destIP
656 << ":" << std::to_string(destPort)
657 << ", reusing connnection "
658 << std::to_string(connId);
659
660 setConnProps(*conn);
661
662 if (keepAlive)
663 {
664 conn->sendMessage();
665 }
666 else
667 {
668 // Server is not keep-alive enabled so we need to close the
669 // connection and then start over from resolve
670 conn->doClose();
671 conn->doResolve();
672 }
673 return;
674 }
675
676 // No more messages to send so close the connection if necessary
677 if (keepAlive)
678 {
679 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530680 }
681 else
682 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000683 // Abort the connection since server is not keep-alive enabled
684 conn->state = ConnState::abortConnection;
685 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530686 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530687 }
688
Carson Labrado244256c2022-04-27 17:16:32 +0000689 void sendData(std::string& data, const std::string& destUri,
690 const boost::beast::http::fields& httpHeader,
691 const boost::beast::http::verb verb,
692 const RetryPolicyData& retryPolicy,
Ed Tanous6b3db602022-06-28 19:41:44 -0700693 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530694 {
Carson Labrado244256c2022-04-27 17:16:32 +0000695 // Construct the request to be sent
696 boost::beast::http::request<boost::beast::http::string_body> thisReq(
697 verb, destUri, 11, "", httpHeader);
698 thisReq.set(boost::beast::http::field::host, destIP);
699 thisReq.keep_alive(true);
700 thisReq.body() = std::move(data);
701 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700702 auto cb = std::bind_front(&ConnectionPool::afterSendData,
703 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000704 // Reuse an existing connection if one is available
705 for (unsigned int i = 0; i < connections.size(); i++)
706 {
707 auto conn = connections[i];
708 if ((conn->state == ConnState::idle) ||
709 (conn->state == ConnState::initialized) ||
710 (conn->state == ConnState::closed))
711 {
Carson Labrado244256c2022-04-27 17:16:32 +0000712 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000713 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000714 setConnRetryPolicy(*conn, retryPolicy);
715 std::string commonMsg = std::to_string(i) + " from pool " +
716 destIP + ":" + std::to_string(destPort);
717
718 if (conn->state == ConnState::idle)
719 {
720 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
721 << commonMsg;
722 conn->sendMessage();
723 }
724 else
725 {
726 BMCWEB_LOG_DEBUG << "Reusing existing connection "
727 << commonMsg;
728 conn->doResolve();
729 }
730 return;
731 }
732 }
733
734 // All connections in use so create a new connection or add request to
735 // the queue
736 if (connections.size() < maxPoolSize)
737 {
738 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
739 << ":" << std::to_string(destPort);
740 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000741 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000742 conn->callback = std::move(cb);
743 setConnRetryPolicy(*conn, retryPolicy);
744 conn->doResolve();
745 }
746 else if (requestQueue.size() < maxRequestQueueSize)
747 {
748 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labrado244256c2022-04-27 17:16:32 +0000749 requestQueue.emplace_back(std::move(thisReq), std::move(cb),
Carson Labradof52c03c2022-03-23 18:50:15 +0000750 retryPolicy);
751 }
752 else
753 {
754 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
755 << " request queue full. Dropping request.";
756 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530757 }
758
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700759 // Callback to be called once the request has been sent
760 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
761 const std::function<void(Response&)>& resHandler,
762 bool keepAlive, uint32_t connId, Response& res)
763 {
764 // Allow provided callback to perform additional processing of the
765 // request
766 resHandler(res);
767
768 // If requests remain in the queue then we want to reuse this
769 // connection to send the next request
770 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
771 if (!self)
772 {
773 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
774 return;
775 }
776
777 self->sendNext(keepAlive, connId);
778 }
779
Carson Labradof52c03c2022-03-23 18:50:15 +0000780 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530781 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000782 unsigned int newId = static_cast<unsigned int>(connections.size());
783
AppaRao Pulie38778a2022-06-27 23:09:03 +0000784 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
785 ioc, id, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000786
787 BMCWEB_LOG_DEBUG << "Added connection "
788 << std::to_string(connections.size() - 1)
789 << " to pool " << destIP << ":"
790 << std::to_string(destPort);
791
792 return ret;
793 }
794
795 public:
Ed Tanous8a592812022-06-04 09:06:59 -0700796 explicit ConnectionPool(boost::asio::io_context& iocIn,
797 const std::string& idIn,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000798 const std::string& destIPIn, uint16_t destPortIn,
799 bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700800 ioc(iocIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000801 id(idIn), destIP(destIPIn), destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000802 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000803 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
804 << std::to_string(destPort);
805
806 // Initialize the pool with a single connection
807 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530808 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530809};
810
Carson Labradof52c03c2022-03-23 18:50:15 +0000811class HttpClient
812{
813 private:
814 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
815 connectionPools;
816 boost::asio::io_context& ioc =
817 crow::connections::systemBus->get_io_context();
818 std::unordered_map<std::string, RetryPolicyData> retryInfo;
819 HttpClient() = default;
820
Carson Labrado039a47e2022-04-05 16:03:20 +0000821 // Used as a dummy callback by sendData() in order to call
822 // sendDataWithCallback()
Ed Tanous02cad962022-06-30 16:50:15 -0700823 static void genericResHandler(const Response& res)
Carson Labrado039a47e2022-04-05 16:03:20 +0000824 {
825 BMCWEB_LOG_DEBUG << "Response handled with return code: "
826 << std::to_string(res.resultInt());
Ed Tanous4ee8e212022-05-28 09:42:51 -0700827 }
Carson Labrado039a47e2022-04-05 16:03:20 +0000828
Carson Labradof52c03c2022-03-23 18:50:15 +0000829 public:
830 HttpClient(const HttpClient&) = delete;
831 HttpClient& operator=(const HttpClient&) = delete;
832 HttpClient(HttpClient&&) = delete;
833 HttpClient& operator=(HttpClient&&) = delete;
834 ~HttpClient() = default;
835
836 static HttpClient& getInstance()
837 {
838 static HttpClient handler;
839 return handler;
840 }
841
Carson Labrado039a47e2022-04-05 16:03:20 +0000842 // Send a request to destIP:destPort where additional processing of the
843 // result is not required
Carson Labradof52c03c2022-03-23 18:50:15 +0000844 void sendData(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000845 const std::string& destIP, uint16_t destPort,
846 const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000847 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000848 const boost::beast::http::verb verb,
849 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000850 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000851 const std::function<void(Response&)> cb = genericResHandler;
852 sendDataWithCallback(data, id, destIP, destPort, destUri, useSSL,
853 httpHeader, verb, retryPolicyName, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000854 }
855
856 // Send request to destIP:destPort and use the provided callback to
857 // handle the response
858 void sendDataWithCallback(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000859 const std::string& destIP, uint16_t destPort,
860 const std::string& destUri, bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000861 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000862 const boost::beast::http::verb verb,
863 const std::string& retryPolicyName,
Ed Tanous6b3db602022-06-28 19:41:44 -0700864 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000865 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000866 std::string clientKey = useSSL ? "https" : "http";
867 clientKey += destIP;
868 clientKey += ":";
869 clientKey += std::to_string(destPort);
Carson Labradof52c03c2022-03-23 18:50:15 +0000870 // Use nullptr to avoid creating a ConnectionPool each time
AppaRao Pulie38778a2022-06-27 23:09:03 +0000871 std::shared_ptr<ConnectionPool>& conn = connectionPools[clientKey];
872 if (conn == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000873 {
874 // Now actually create the ConnectionPool shared_ptr since it does
875 // not already exist
AppaRao Pulie38778a2022-06-27 23:09:03 +0000876 conn = std::make_shared<ConnectionPool>(ioc, id, destIP, destPort,
877 useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000878 BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
879 }
880 else
881 {
882 BMCWEB_LOG_DEBUG << "Using existing connection pool for "
883 << clientKey;
884 }
885
886 // Get the associated retry policy
887 auto policy = retryInfo.try_emplace(retryPolicyName);
888 if (policy.second)
889 {
890 BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
891 << "\" with default values";
Carson Labradof52c03c2022-03-23 18:50:15 +0000892 }
893
894 // Send the data using either the existing connection pool or the newly
895 // created connection pool
AppaRao Pulie38778a2022-06-27 23:09:03 +0000896 conn->sendData(data, destUri, httpHeader, verb, policy.first->second,
897 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000898 }
899
Carson Labradoa7a80292022-06-01 16:01:52 +0000900 void setRetryConfig(
901 const uint32_t retryAttempts, const uint32_t retryTimeoutInterval,
902 const std::function<boost::system::error_code(unsigned int respCode)>&
903 invalidResp,
904 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000905 {
906 // We need to create the retry policy if one does not already exist for
907 // the given retryPolicyName
908 auto result = retryInfo.try_emplace(retryPolicyName);
909 if (result.second)
910 {
911 BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
912 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000913 }
914 else
915 {
916 BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
917 << retryPolicyName << "\"";
918 }
919
920 result.first->second.maxRetryAttempts = retryAttempts;
921 result.first->second.retryIntervalSecs =
922 std::chrono::seconds(retryTimeoutInterval);
Carson Labradoa7a80292022-06-01 16:01:52 +0000923 result.first->second.invalidResp = invalidResp;
Carson Labradof52c03c2022-03-23 18:50:15 +0000924 }
925
926 void setRetryPolicy(const std::string& retryPolicy,
927 const std::string& retryPolicyName)
928 {
929 // We need to create the retry policy if one does not already exist for
930 // the given retryPolicyName
931 auto result = retryInfo.try_emplace(retryPolicyName);
932 if (result.second)
933 {
934 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
935 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000936 }
937 else
938 {
939 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
940 << retryPolicyName << "\"";
941 }
942
943 result.first->second.retryPolicyAction = retryPolicy;
944 }
945};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530946} // namespace crow