blob: 48e8cfdfacdf64f9c3672a717116f6dc35965471 [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"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "logging.hpp"
21#include "ssl_key_handler.hpp"
Nan Zhou77665bd2022-10-12 20:28:58 +000022
Ed Tanous0d5f5cf2022-03-12 15:30:55 -080023#include <boost/asio/connect.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070024#include <boost/asio/io_context.hpp>
Sunitha Harish29a82b02021-02-18 15:54:16 +053025#include <boost/asio/ip/address.hpp>
26#include <boost/asio/ip/basic_endpoint.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070027#include <boost/asio/ip/tcp.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000028#include <boost/asio/ssl/context.hpp>
29#include <boost/asio/ssl/error.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070030#include <boost/asio/steady_timer.hpp>
31#include <boost/beast/core/flat_buffer.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070032#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070033#include <boost/beast/http/message.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070034#include <boost/beast/http/parser.hpp>
35#include <boost/beast/http/read.hpp>
36#include <boost/beast/http/string_body.hpp>
37#include <boost/beast/http/write.hpp>
AppaRao Pulie38778a2022-06-27 23:09:03 +000038#include <boost/beast/ssl/ssl_stream.hpp>
AppaRao Pulibd030d02020-03-20 03:34:29 +053039#include <boost/beast/version.hpp>
Carson Labradof52c03c2022-03-23 18:50:15 +000040#include <boost/container/devector.hpp>
Ed Tanousbb49eb52022-06-28 12:02:42 -070041#include <boost/system/error_code.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 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000200 // The operation already timed out. We don't want do continue down
201 // this branch
202 if (ec && ec == boost::asio::error::operation_aborted)
203 {
204 return;
205 }
206
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800207 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000208 if (ec)
209 {
210 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
211 << ":" << std::to_string(endpoint.port())
212 << ", id: " << std::to_string(connId)
213 << " failed: " << ec.message();
214 state = ConnState::connectFailed;
215 waitAndRetry();
216 return;
217 }
218 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
219 << ":" << std::to_string(endpoint.port())
220 << ", id: " << std::to_string(connId);
221 if (sslConn)
222 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800223 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000224 return;
225 }
226 state = ConnState::connected;
227 sendMessage();
228 }
229
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800230 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000231 {
232 if (!sslConn)
233 {
234 return;
235 }
236 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800237 timer.expires_after(std::chrono::seconds(30));
238 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000239 sslConn->async_handshake(
240 boost::asio::ssl::stream_base::client,
241 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
242 shared_from_this()));
243 }
244
245 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
246 boost::beast::error_code ec)
247 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000248 // The operation already timed out. We don't want do continue down
249 // this branch
250 if (ec && ec == boost::asio::error::operation_aborted)
251 {
252 return;
253 }
254
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800255 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000256 if (ec)
257 {
258 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
259 << " id: " << std::to_string(connId)
260 << " error: " << ec.message();
261 state = ConnState::handshakeFailed;
262 waitAndRetry();
263 return;
264 }
265 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
266 << " id: " << std::to_string(connId);
267 state = ConnState::connected;
268 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530269 }
270
Carson Labradof52c03c2022-03-23 18:50:15 +0000271 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530272 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530273 state = ConnState::sendInProgress;
274
AppaRao Pulibd030d02020-03-20 03:34:29 +0530275 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800276 timer.expires_after(std::chrono::seconds(30));
277 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530278
279 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000280 if (sslConn)
281 {
282 boost::beast::http::async_write(
283 *sslConn, req,
284 std::bind_front(&ConnectionInfo::afterWrite, this,
285 shared_from_this()));
286 }
287 else
288 {
289 boost::beast::http::async_write(
290 conn, req,
291 std::bind_front(&ConnectionInfo::afterWrite, this,
292 shared_from_this()));
293 }
294 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530295
AppaRao Pulie38778a2022-06-27 23:09:03 +0000296 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
297 const boost::beast::error_code& ec, size_t bytesTransferred)
298 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000299 // The operation already timed out. We don't want do continue down
300 // this branch
301 if (ec && ec == boost::asio::error::operation_aborted)
302 {
303 return;
304 }
305
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800306 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000307 if (ec)
308 {
309 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
310 state = ConnState::sendFailed;
311 waitAndRetry();
312 return;
313 }
314 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
315 << bytesTransferred;
316
317 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530318 }
319
320 void recvMessage()
321 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530322 state = ConnState::recvInProgress;
323
324 parser.emplace(std::piecewise_construct, std::make_tuple());
325 parser->body_limit(httpReadBodyLimit);
326
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800327 timer.expires_after(std::chrono::seconds(30));
328 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
329
AppaRao Pulibd030d02020-03-20 03:34:29 +0530330 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000331 if (sslConn)
332 {
333 boost::beast::http::async_read(
334 *sslConn, buffer, *parser,
335 std::bind_front(&ConnectionInfo::afterRead, this,
336 shared_from_this()));
337 }
338 else
339 {
340 boost::beast::http::async_read(
341 conn, buffer, *parser,
342 std::bind_front(&ConnectionInfo::afterRead, this,
343 shared_from_this()));
344 }
345 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530346
AppaRao Pulie38778a2022-06-27 23:09:03 +0000347 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
348 const boost::beast::error_code& ec,
349 const std::size_t& bytesTransferred)
350 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000351 // The operation already timed out. We don't want do continue down
352 // this branch
353 if (ec && ec == boost::asio::error::operation_aborted)
354 {
355 return;
356 }
357
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800358 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000359 if (ec && ec != boost::asio::ssl::error::stream_truncated)
360 {
361 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
362 state = ConnState::recvFailed;
363 waitAndRetry();
364 return;
365 }
366 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
367 << bytesTransferred;
368 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
369
370 unsigned int respCode = parser->get().result_int();
371 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
372
373 // Make sure the received response code is valid as defined by
374 // the associated retry policy
375 if (retryPolicy.invalidResp(respCode))
376 {
377 // The listener failed to receive the Sent-Event
378 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
379 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700380 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000381 state = ConnState::recvFailed;
382 waitAndRetry();
383 return;
384 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700385
AppaRao Pulie38778a2022-06-27 23:09:03 +0000386 // Send is successful
387 // Reset the counter just in case this was after retrying
388 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530389
AppaRao Pulie38778a2022-06-27 23:09:03 +0000390 // Keep the connection alive if server supports it
391 // Else close the connection
392 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
393 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530394
AppaRao Pulie38778a2022-06-27 23:09:03 +0000395 // Copy the response into a Response object so that it can be
396 // processed by the callback function.
AppaRao Pulie38778a2022-06-27 23:09:03 +0000397 res.stringResponse = parser->release();
398 callback(parser->keep_alive(), connId, res);
Carson Labrado513d1ff2022-07-19 00:38:15 +0000399 res.clear();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530400 }
401
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800402 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
403 const boost::system::error_code ec)
404 {
405 if (ec == boost::asio::error::operation_aborted)
406 {
407 BMCWEB_LOG_DEBUG
Carson Labrado513d1ff2022-07-19 00:38:15 +0000408 << "async_wait failed since the operation is aborted";
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800409 return;
410 }
411 if (ec)
412 {
413 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
414 // If the timer fails, we need to close the socket anyway, same as
415 // if it expired.
416 }
417 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
418 if (self == nullptr)
419 {
420 return;
421 }
422 self->waitAndRetry();
423 }
424
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530425 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530426 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000427 if ((retryCount >= retryPolicy.maxRetryAttempts) ||
428 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530429 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530430 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000431 BMCWEB_LOG_DEBUG << "Retry policy: "
432 << retryPolicy.retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000433
Carson Labradof52c03c2022-03-23 18:50:15 +0000434 if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530435 {
436 // TODO: delete subscription
437 state = ConnState::terminated;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530438 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000439 if (retryPolicy.retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530440 {
441 state = ConnState::suspended;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530442 }
Carson Labrado513d1ff2022-07-19 00:38:15 +0000443
444 // We want to return a 502 to indicate there was an error with
445 // the external server
446 res.result(boost::beast::http::status::bad_gateway);
447 callback(false, connId, res);
448 res.clear();
449
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530450 // Reset the retrycount to zero so that client can try connecting
451 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700452 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530453 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530454 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530455
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530456 retryCount++;
457
Carson Labradof52c03c2022-03-23 18:50:15 +0000458 BMCWEB_LOG_DEBUG << "Attempt retry after "
459 << std::to_string(
460 retryPolicy.retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530461 << " seconds. RetryCount = " << retryCount;
Carson Labradof52c03c2022-03-23 18:50:15 +0000462 timer.expires_after(retryPolicy.retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700463 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
464 shared_from_this()));
465 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530466
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700467 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
468 const boost::system::error_code& ec)
469 {
470 if (ec == boost::asio::error::operation_aborted)
471 {
472 BMCWEB_LOG_DEBUG
473 << "async_wait failed since the operation is aborted"
474 << ec.message();
475 }
476 else if (ec)
477 {
478 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
479 // Ignore the error and continue the retry loop to attempt
480 // sending the event as per the retry policy
481 }
482
483 // Let's close the connection and restart from resolve.
484 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530485 }
486
AppaRao Pulie38778a2022-06-27 23:09:03 +0000487 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530488 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000489 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800490 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000491 conn.close();
492
493 // not_connected happens sometimes so don't bother reporting it.
494 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530495 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000496 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
497 << ", id: " << std::to_string(connId)
Carson Labrado513d1ff2022-07-19 00:38:15 +0000498 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000499 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000500 else
501 {
502 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
503 << ", id: " << std::to_string(connId)
504 << " closed gracefully";
505 }
Ed Tanousca723762022-06-28 19:40:39 -0700506
Carson Labrado513d1ff2022-07-19 00:38:15 +0000507 if (retry)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000508 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000509 // Now let's try to resend the data
510 state = ConnState::retry;
511 doResolve();
512 }
513 else
514 {
515 state = ConnState::closed;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000516 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000517 }
518
AppaRao Pulie38778a2022-06-27 23:09:03 +0000519 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000520 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000521 if (!sslConn)
522 {
523 shutdownConn(retry);
524 return;
525 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000526
AppaRao Pulie38778a2022-06-27 23:09:03 +0000527 sslConn->async_shutdown(
528 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
529 shared_from_this(), retry));
530 }
531
532 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
533 bool retry, const boost::system::error_code& ec)
534 {
535
536 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000537 {
538 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
539 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000540 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000541 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000542 else
543 {
544 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
545 << ", id: " << std::to_string(connId)
546 << " closed gracefully";
547 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000548 shutdownConn(retry);
549 }
Ed Tanousca723762022-06-28 19:40:39 -0700550
AppaRao Pulie38778a2022-06-27 23:09:03 +0000551 void setCipherSuiteTLSext()
552 {
553 if (!sslConn)
554 {
555 return;
556 }
557 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
558 // file but its having old style casting (name is cast to void*).
559 // Since bmcweb compiler treats all old-style-cast as error, its
560 // causing the build failure. So replaced the same macro inline and
561 // did corrected the code by doing static_cast to viod*. This has to
562 // be fixed in openssl library in long run. Set SNI Hostname (many
563 // hosts need this to handshake successfully)
564 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
565 TLSEXT_NAMETYPE_host_name,
566 static_cast<void*>(&host.front())) == 0)
567
568 {
569 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
570 boost::asio::error::get_ssl_category()};
571
572 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
573 << port << ", id: " << std::to_string(connId)
574 << " failed: " << ec.message();
575 // Set state as sslInit failed so that we close the connection
576 // and take appropriate action as per retry configuration.
577 state = ConnState::sslInitFailed;
578 waitAndRetry();
579 return;
580 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530581 }
582
583 public:
AppaRao Pulie38778a2022-06-27 23:09:03 +0000584 explicit ConnectionInfo(boost::asio::io_context& iocIn,
585 const std::string& idIn,
586 const std::string& destIPIn, uint16_t destPortIn,
587 bool useSSL, unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700588 subId(idIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000589 host(destIPIn), port(destPortIn), connId(connIdIn), conn(iocIn),
590 timer(iocIn)
591 {
592 if (useSSL)
593 {
594 std::optional<boost::asio::ssl::context> sslCtx =
595 ensuressl::getSSLClientContext();
596
597 if (!sslCtx)
598 {
599 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
600 << port << ", id: " << std::to_string(connId);
601 // Don't retry if failure occurs while preparing SSL context
602 // such as certificate is invalid or set cipher failure or set
603 // host name failure etc... Setting conn state to sslInitFailed
604 // and connection state will be transitioned to next state
605 // depending on retry policy set by subscription.
606 state = ConnState::sslInitFailed;
607 waitAndRetry();
608 return;
609 }
610 sslConn.emplace(conn, *sslCtx);
611 setCipherSuiteTLSext();
612 }
613 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000614};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530615
Carson Labradof52c03c2022-03-23 18:50:15 +0000616class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
617{
618 private:
619 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000620 std::string id;
621 std::string destIP;
622 uint16_t destPort;
623 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000624 std::vector<std::shared_ptr<ConnectionInfo>> connections;
625 boost::container::devector<PendingRequest> requestQueue;
626
627 friend class HttpClient;
628
Carson Labrado244256c2022-04-27 17:16:32 +0000629 // Configure a connections's request, callback, and retry info in
630 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000631 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530632 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000633 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530634 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000635 BMCWEB_LOG_ERROR
636 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530637 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530638 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530639
Carson Labrado244256c2022-04-27 17:16:32 +0000640 auto nextReq = requestQueue.front();
641 conn.retryPolicy = std::move(nextReq.retryPolicy);
642 conn.req = std::move(nextReq.req);
643 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000644
645 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
646 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000647 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000648
649 // We can remove the request from the queue at this point
650 requestQueue.pop_front();
651 }
652
653 // Configures a connection to use the specific retry policy.
654 inline void setConnRetryPolicy(ConnectionInfo& conn,
655 const RetryPolicyData& retryPolicy)
656 {
657 BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
Carson Labradoa7a80292022-06-01 16:01:52 +0000658 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000659
660 conn.retryPolicy = retryPolicy;
661 }
662
663 // Gets called as part of callback after request is sent
664 // Reuses the connection if there are any requests waiting to be sent
665 // Otherwise closes the connection if it is not a keep-alive
666 void sendNext(bool keepAlive, uint32_t connId)
667 {
668 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000669
670 // Allow the connection's handler to be deleted
671 // This is needed because of Redfish Aggregation passing an
672 // AsyncResponse shared_ptr to this callback
673 conn->callback = nullptr;
674
Carson Labradof52c03c2022-03-23 18:50:15 +0000675 // Reuse the connection to send the next request in the queue
676 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530677 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000678 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
679 << " requests remaining in queue for " << destIP
680 << ":" << std::to_string(destPort)
681 << ", reusing connnection "
682 << std::to_string(connId);
683
684 setConnProps(*conn);
685
686 if (keepAlive)
687 {
688 conn->sendMessage();
689 }
690 else
691 {
692 // Server is not keep-alive enabled so we need to close the
693 // connection and then start over from resolve
694 conn->doClose();
695 conn->doResolve();
696 }
697 return;
698 }
699
700 // No more messages to send so close the connection if necessary
701 if (keepAlive)
702 {
703 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530704 }
705 else
706 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000707 // Abort the connection since server is not keep-alive enabled
708 conn->state = ConnState::abortConnection;
709 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530710 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530711 }
712
Carson Labrado244256c2022-04-27 17:16:32 +0000713 void sendData(std::string& data, const std::string& destUri,
714 const boost::beast::http::fields& httpHeader,
715 const boost::beast::http::verb verb,
716 const RetryPolicyData& retryPolicy,
Ed Tanous6b3db602022-06-28 19:41:44 -0700717 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530718 {
Carson Labrado244256c2022-04-27 17:16:32 +0000719 // Construct the request to be sent
720 boost::beast::http::request<boost::beast::http::string_body> thisReq(
721 verb, destUri, 11, "", httpHeader);
722 thisReq.set(boost::beast::http::field::host, destIP);
723 thisReq.keep_alive(true);
724 thisReq.body() = std::move(data);
725 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700726 auto cb = std::bind_front(&ConnectionPool::afterSendData,
727 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000728 // Reuse an existing connection if one is available
729 for (unsigned int i = 0; i < connections.size(); i++)
730 {
731 auto conn = connections[i];
732 if ((conn->state == ConnState::idle) ||
733 (conn->state == ConnState::initialized) ||
734 (conn->state == ConnState::closed))
735 {
Carson Labrado244256c2022-04-27 17:16:32 +0000736 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000737 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000738 setConnRetryPolicy(*conn, retryPolicy);
739 std::string commonMsg = std::to_string(i) + " from pool " +
740 destIP + ":" + std::to_string(destPort);
741
742 if (conn->state == ConnState::idle)
743 {
744 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
745 << commonMsg;
746 conn->sendMessage();
747 }
748 else
749 {
750 BMCWEB_LOG_DEBUG << "Reusing existing connection "
751 << commonMsg;
752 conn->doResolve();
753 }
754 return;
755 }
756 }
757
758 // All connections in use so create a new connection or add request to
759 // the queue
760 if (connections.size() < maxPoolSize)
761 {
762 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
763 << ":" << std::to_string(destPort);
764 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000765 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000766 conn->callback = std::move(cb);
767 setConnRetryPolicy(*conn, retryPolicy);
768 conn->doResolve();
769 }
770 else if (requestQueue.size() < maxRequestQueueSize)
771 {
772 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labrado244256c2022-04-27 17:16:32 +0000773 requestQueue.emplace_back(std::move(thisReq), std::move(cb),
Carson Labradof52c03c2022-03-23 18:50:15 +0000774 retryPolicy);
775 }
776 else
777 {
778 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
779 << " request queue full. Dropping request.";
780 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530781 }
782
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700783 // Callback to be called once the request has been sent
784 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
785 const std::function<void(Response&)>& resHandler,
786 bool keepAlive, uint32_t connId, Response& res)
787 {
788 // Allow provided callback to perform additional processing of the
789 // request
790 resHandler(res);
791
792 // If requests remain in the queue then we want to reuse this
793 // connection to send the next request
794 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
795 if (!self)
796 {
797 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
798 return;
799 }
800
801 self->sendNext(keepAlive, connId);
802 }
803
Carson Labradof52c03c2022-03-23 18:50:15 +0000804 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530805 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000806 unsigned int newId = static_cast<unsigned int>(connections.size());
807
AppaRao Pulie38778a2022-06-27 23:09:03 +0000808 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
809 ioc, id, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000810
811 BMCWEB_LOG_DEBUG << "Added connection "
812 << std::to_string(connections.size() - 1)
813 << " to pool " << destIP << ":"
814 << std::to_string(destPort);
815
816 return ret;
817 }
818
819 public:
Ed Tanous8a592812022-06-04 09:06:59 -0700820 explicit ConnectionPool(boost::asio::io_context& iocIn,
821 const std::string& idIn,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000822 const std::string& destIPIn, uint16_t destPortIn,
823 bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700824 ioc(iocIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000825 id(idIn), destIP(destIPIn), destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000826 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000827 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
828 << std::to_string(destPort);
829
830 // Initialize the pool with a single connection
831 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530832 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530833};
834
Carson Labradof52c03c2022-03-23 18:50:15 +0000835class HttpClient
836{
837 private:
838 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
839 connectionPools;
840 boost::asio::io_context& ioc =
841 crow::connections::systemBus->get_io_context();
842 std::unordered_map<std::string, RetryPolicyData> retryInfo;
843 HttpClient() = default;
844
Carson Labrado039a47e2022-04-05 16:03:20 +0000845 // Used as a dummy callback by sendData() in order to call
846 // sendDataWithCallback()
Ed Tanous02cad962022-06-30 16:50:15 -0700847 static void genericResHandler(const Response& res)
Carson Labrado039a47e2022-04-05 16:03:20 +0000848 {
849 BMCWEB_LOG_DEBUG << "Response handled with return code: "
850 << std::to_string(res.resultInt());
Ed Tanous4ee8e212022-05-28 09:42:51 -0700851 }
Carson Labrado039a47e2022-04-05 16:03:20 +0000852
Carson Labradof52c03c2022-03-23 18:50:15 +0000853 public:
854 HttpClient(const HttpClient&) = delete;
855 HttpClient& operator=(const HttpClient&) = delete;
856 HttpClient(HttpClient&&) = delete;
857 HttpClient& operator=(HttpClient&&) = delete;
858 ~HttpClient() = default;
859
860 static HttpClient& getInstance()
861 {
862 static HttpClient handler;
863 return handler;
864 }
865
Carson Labrado039a47e2022-04-05 16:03:20 +0000866 // Send a request to destIP:destPort where additional processing of the
867 // result is not required
Carson Labradof52c03c2022-03-23 18:50:15 +0000868 void sendData(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000869 const std::string& destIP, uint16_t destPort,
870 const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000871 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000872 const boost::beast::http::verb verb,
873 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000874 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000875 const std::function<void(Response&)> cb = genericResHandler;
876 sendDataWithCallback(data, id, destIP, destPort, destUri, useSSL,
877 httpHeader, verb, retryPolicyName, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000878 }
879
880 // Send request to destIP:destPort and use the provided callback to
881 // handle the response
882 void sendDataWithCallback(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000883 const std::string& destIP, uint16_t destPort,
884 const std::string& destUri, bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000885 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000886 const boost::beast::http::verb verb,
887 const std::string& retryPolicyName,
Ed Tanous6b3db602022-06-28 19:41:44 -0700888 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000889 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000890 std::string clientKey = useSSL ? "https" : "http";
891 clientKey += destIP;
892 clientKey += ":";
893 clientKey += std::to_string(destPort);
Carson Labradof52c03c2022-03-23 18:50:15 +0000894 // Use nullptr to avoid creating a ConnectionPool each time
AppaRao Pulie38778a2022-06-27 23:09:03 +0000895 std::shared_ptr<ConnectionPool>& conn = connectionPools[clientKey];
896 if (conn == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000897 {
898 // Now actually create the ConnectionPool shared_ptr since it does
899 // not already exist
AppaRao Pulie38778a2022-06-27 23:09:03 +0000900 conn = std::make_shared<ConnectionPool>(ioc, id, destIP, destPort,
901 useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000902 BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
903 }
904 else
905 {
906 BMCWEB_LOG_DEBUG << "Using existing connection pool for "
907 << clientKey;
908 }
909
910 // Get the associated retry policy
911 auto policy = retryInfo.try_emplace(retryPolicyName);
912 if (policy.second)
913 {
914 BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
915 << "\" with default values";
Carson Labradof52c03c2022-03-23 18:50:15 +0000916 }
917
918 // Send the data using either the existing connection pool or the newly
919 // created connection pool
AppaRao Pulie38778a2022-06-27 23:09:03 +0000920 conn->sendData(data, destUri, httpHeader, verb, policy.first->second,
921 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000922 }
923
Carson Labradoa7a80292022-06-01 16:01:52 +0000924 void setRetryConfig(
925 const uint32_t retryAttempts, const uint32_t retryTimeoutInterval,
926 const std::function<boost::system::error_code(unsigned int respCode)>&
927 invalidResp,
928 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000929 {
930 // We need to create the retry policy if one does not already exist for
931 // the given retryPolicyName
932 auto result = retryInfo.try_emplace(retryPolicyName);
933 if (result.second)
934 {
935 BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
936 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000937 }
938 else
939 {
940 BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
941 << retryPolicyName << "\"";
942 }
943
944 result.first->second.maxRetryAttempts = retryAttempts;
945 result.first->second.retryIntervalSecs =
946 std::chrono::seconds(retryTimeoutInterval);
Carson Labradoa7a80292022-06-01 16:01:52 +0000947 result.first->second.invalidResp = invalidResp;
Carson Labradof52c03c2022-03-23 18:50:15 +0000948 }
949
950 void setRetryPolicy(const std::string& retryPolicy,
951 const std::string& retryPolicyName)
952 {
953 // We need to create the retry policy if one does not already exist for
954 // the given retryPolicyName
955 auto result = retryInfo.try_emplace(retryPolicyName);
956 if (result.second)
957 {
958 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
959 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000960 }
961 else
962 {
963 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
964 << retryPolicyName << "\"";
965 }
966
967 result.first->second.retryPolicyAction = retryPolicy;
968 }
969};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530970} // namespace crow