blob: c902e1a66bcd8ab8ec2fcdc8e1c9d5bfcfc3714e [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 Labrado66d90c22022-12-07 22:34:33 +000053// With Redfish Aggregation it is assumed we will connect to another instance
54// of BMCWeb which can handle 100 simultaneous connections.
55constexpr size_t maxPoolSize = 20;
56constexpr size_t maxRequestQueueSize = 500;
Carson Labrado17dcc312022-07-28 22:17:28 +000057constexpr unsigned int httpReadBodyLimit = 131072;
Carson Labrado4d942722022-06-22 22:16:10 +000058constexpr unsigned int httpReadBufferSize = 4096;
AppaRao Puli2a5689a2020-04-29 15:24:31 +053059
AppaRao Pulibd030d02020-03-20 03:34:29 +053060enum class ConnState
61{
AppaRao Puli2a5689a2020-04-29 15:24:31 +053062 initialized,
Sunitha Harish29a82b02021-02-18 15:54:16 +053063 resolveInProgress,
64 resolveFailed,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053065 connectInProgress,
66 connectFailed,
AppaRao Pulibd030d02020-03-20 03:34:29 +053067 connected,
AppaRao Pulie38778a2022-06-27 23:09:03 +000068 handshakeInProgress,
69 handshakeFailed,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053070 sendInProgress,
71 sendFailed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053072 recvInProgress,
AppaRao Puli2a5689a2020-04-29 15:24:31 +053073 recvFailed,
74 idle,
Ayushi Smritife44eb02020-05-15 15:24:45 +053075 closed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053076 suspended,
77 terminated,
78 abortConnection,
AppaRao Pulie38778a2022-06-27 23:09:03 +000079 sslInitFailed,
Sunitha Harish6eaa1d22021-02-19 13:38:31 +053080 retry
AppaRao Pulibd030d02020-03-20 03:34:29 +053081};
82
Carson Labradoa7a80292022-06-01 16:01:52 +000083static inline boost::system::error_code
84 defaultRetryHandler(unsigned int respCode)
85{
86 // As a default, assume 200X is alright
87 BMCWEB_LOG_DEBUG << "Using default check for response code validity";
88 if ((respCode < 200) || (respCode >= 300))
89 {
90 return boost::system::errc::make_error_code(
91 boost::system::errc::result_out_of_range);
92 }
93
94 // Return 0 if the response code is valid
95 return boost::system::errc::make_error_code(boost::system::errc::success);
96};
97
Carson Labradof52c03c2022-03-23 18:50:15 +000098// We need to allow retry information to be set before a message has been sent
99// and a connection pool has been created
100struct RetryPolicyData
101{
102 uint32_t maxRetryAttempts = 5;
103 std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
104 std::string retryPolicyAction = "TerminateAfterRetries";
Carson Labradoa7a80292022-06-01 16:01:52 +0000105 std::function<boost::system::error_code(unsigned int respCode)>
106 invalidResp = defaultRetryHandler;
Carson Labradof52c03c2022-03-23 18:50:15 +0000107};
108
109struct PendingRequest
110{
Carson Labrado244256c2022-04-27 17:16:32 +0000111 boost::beast::http::request<boost::beast::http::string_body> req;
Carson Labrado039a47e2022-04-05 16:03:20 +0000112 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labradof52c03c2022-03-23 18:50:15 +0000113 RetryPolicyData retryPolicy;
Carson Labrado039a47e2022-04-05 16:03:20 +0000114 PendingRequest(
Ed Tanous8a592812022-06-04 09:06:59 -0700115 boost::beast::http::request<boost::beast::http::string_body>&& reqIn,
116 const std::function<void(bool, uint32_t, Response&)>& callbackIn,
117 const RetryPolicyData& retryPolicyIn) :
118 req(std::move(reqIn)),
119 callback(callbackIn), retryPolicy(retryPolicyIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000120 {}
121};
122
123class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
AppaRao Pulibd030d02020-03-20 03:34:29 +0530124{
125 private:
Carson Labradof52c03c2022-03-23 18:50:15 +0000126 ConnState state = ConnState::initialized;
127 uint32_t retryCount = 0;
Carson Labradof52c03c2022-03-23 18:50:15 +0000128 std::string subId;
129 std::string host;
130 uint16_t port;
131 uint32_t connId;
132
133 // Retry policy information
134 // This should be updated before each message is sent
135 RetryPolicyData retryPolicy;
136
137 // Data buffers
AppaRao Pulibd030d02020-03-20 03:34:29 +0530138 boost::beast::http::request<boost::beast::http::string_body> req;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530139 std::optional<
140 boost::beast::http::response_parser<boost::beast::http::string_body>>
141 parser;
Carson Labrado4d942722022-06-22 22:16:10 +0000142 boost::beast::flat_static_buffer<httpReadBufferSize> buffer;
Carson Labrado039a47e2022-04-05 16:03:20 +0000143 Response res;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530144
Carson Labradof52c03c2022-03-23 18:50:15 +0000145 // Ascync callables
Carson Labrado039a47e2022-04-05 16:03:20 +0000146 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labradof52c03c2022-03-23 18:50:15 +0000147 crow::async_resolve::Resolver resolver;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800148 boost::asio::ip::tcp::socket conn;
149 std::optional<boost::beast::ssl_stream<boost::asio::ip::tcp::socket&>>
150 sslConn;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000151
Carson Labradof52c03c2022-03-23 18:50:15 +0000152 boost::asio::steady_timer timer;
Ed Tanous84b35602021-09-08 20:06:32 -0700153
Carson Labradof52c03c2022-03-23 18:50:15 +0000154 friend class ConnectionPool;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530155
Sunitha Harish29a82b02021-02-18 15:54:16 +0530156 void doResolve()
157 {
Sunitha Harish29a82b02021-02-18 15:54:16 +0530158 state = ConnState::resolveInProgress;
Carson Labradof52c03c2022-03-23 18:50:15 +0000159 BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
160 << std::to_string(port)
161 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530162
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700163 resolver.asyncResolve(host, port,
164 std::bind_front(&ConnectionInfo::afterResolve,
165 this, shared_from_this()));
Sunitha Harish29a82b02021-02-18 15:54:16 +0530166 }
167
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700168 void afterResolve(
169 const std::shared_ptr<ConnectionInfo>& /*self*/,
170 const boost::beast::error_code ec,
Sunitha Harish29a82b02021-02-18 15:54:16 +0530171 const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530172 {
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700173 if (ec || (endpointList.empty()))
174 {
175 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
176 state = ConnState::resolveFailed;
177 waitAndRetry();
178 return;
179 }
180 BMCWEB_LOG_DEBUG << "Resolved " << host << ":" << std::to_string(port)
181 << ", id: " << std::to_string(connId);
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530182 state = ConnState::connectInProgress;
183
Carson Labradof52c03c2022-03-23 18:50:15 +0000184 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
185 << std::to_string(port)
186 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530187
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800188 timer.expires_after(std::chrono::seconds(30));
189 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
190
191 boost::asio::async_connect(
192 conn, endpointList,
193 std::bind_front(&ConnectionInfo::afterConnect, this,
194 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000195 }
196
197 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
198 boost::beast::error_code ec,
199 const boost::asio::ip::tcp::endpoint& endpoint)
200 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000201 // The operation already timed out. We don't want do continue down
202 // this branch
203 if (ec && ec == boost::asio::error::operation_aborted)
204 {
205 return;
206 }
207
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800208 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000209 if (ec)
210 {
211 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
212 << ":" << std::to_string(endpoint.port())
213 << ", id: " << std::to_string(connId)
214 << " failed: " << ec.message();
215 state = ConnState::connectFailed;
216 waitAndRetry();
217 return;
218 }
219 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
220 << ":" << std::to_string(endpoint.port())
221 << ", id: " << std::to_string(connId);
222 if (sslConn)
223 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800224 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000225 return;
226 }
227 state = ConnState::connected;
228 sendMessage();
229 }
230
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800231 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000232 {
233 if (!sslConn)
234 {
235 return;
236 }
237 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800238 timer.expires_after(std::chrono::seconds(30));
239 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000240 sslConn->async_handshake(
241 boost::asio::ssl::stream_base::client,
242 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
243 shared_from_this()));
244 }
245
246 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
247 boost::beast::error_code ec)
248 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000249 // The operation already timed out. We don't want do continue down
250 // this branch
251 if (ec && ec == boost::asio::error::operation_aborted)
252 {
253 return;
254 }
255
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800256 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000257 if (ec)
258 {
259 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
260 << " id: " << std::to_string(connId)
261 << " error: " << ec.message();
262 state = ConnState::handshakeFailed;
263 waitAndRetry();
264 return;
265 }
266 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
267 << " id: " << std::to_string(connId);
268 state = ConnState::connected;
269 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530270 }
271
Carson Labradof52c03c2022-03-23 18:50:15 +0000272 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530273 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530274 state = ConnState::sendInProgress;
275
AppaRao Pulibd030d02020-03-20 03:34:29 +0530276 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800277 timer.expires_after(std::chrono::seconds(30));
278 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530279
280 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000281 if (sslConn)
282 {
283 boost::beast::http::async_write(
284 *sslConn, req,
285 std::bind_front(&ConnectionInfo::afterWrite, this,
286 shared_from_this()));
287 }
288 else
289 {
290 boost::beast::http::async_write(
291 conn, req,
292 std::bind_front(&ConnectionInfo::afterWrite, this,
293 shared_from_this()));
294 }
295 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530296
AppaRao Pulie38778a2022-06-27 23:09:03 +0000297 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
298 const boost::beast::error_code& ec, size_t bytesTransferred)
299 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000300 // The operation already timed out. We don't want do continue down
301 // this branch
302 if (ec && ec == boost::asio::error::operation_aborted)
303 {
304 return;
305 }
306
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800307 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000308 if (ec)
309 {
310 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
311 state = ConnState::sendFailed;
312 waitAndRetry();
313 return;
314 }
315 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
316 << bytesTransferred;
317
318 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530319 }
320
321 void recvMessage()
322 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530323 state = ConnState::recvInProgress;
324
325 parser.emplace(std::piecewise_construct, std::make_tuple());
326 parser->body_limit(httpReadBodyLimit);
327
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800328 timer.expires_after(std::chrono::seconds(30));
329 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
330
AppaRao Pulibd030d02020-03-20 03:34:29 +0530331 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000332 if (sslConn)
333 {
334 boost::beast::http::async_read(
335 *sslConn, buffer, *parser,
336 std::bind_front(&ConnectionInfo::afterRead, this,
337 shared_from_this()));
338 }
339 else
340 {
341 boost::beast::http::async_read(
342 conn, buffer, *parser,
343 std::bind_front(&ConnectionInfo::afterRead, this,
344 shared_from_this()));
345 }
346 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530347
AppaRao Pulie38778a2022-06-27 23:09:03 +0000348 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
349 const boost::beast::error_code& ec,
350 const std::size_t& bytesTransferred)
351 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000352 // The operation already timed out. We don't want do continue down
353 // this branch
354 if (ec && ec == boost::asio::error::operation_aborted)
355 {
356 return;
357 }
358
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800359 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000360 if (ec && ec != boost::asio::ssl::error::stream_truncated)
361 {
362 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
363 state = ConnState::recvFailed;
364 waitAndRetry();
365 return;
366 }
367 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
368 << bytesTransferred;
369 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
370
371 unsigned int respCode = parser->get().result_int();
372 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
373
374 // Make sure the received response code is valid as defined by
375 // the associated retry policy
376 if (retryPolicy.invalidResp(respCode))
377 {
378 // The listener failed to receive the Sent-Event
379 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
380 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700381 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000382 state = ConnState::recvFailed;
383 waitAndRetry();
384 return;
385 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700386
AppaRao Pulie38778a2022-06-27 23:09:03 +0000387 // Send is successful
388 // Reset the counter just in case this was after retrying
389 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530390
AppaRao Pulie38778a2022-06-27 23:09:03 +0000391 // Keep the connection alive if server supports it
392 // Else close the connection
393 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
394 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530395
AppaRao Pulie38778a2022-06-27 23:09:03 +0000396 // Copy the response into a Response object so that it can be
397 // processed by the callback function.
AppaRao Pulie38778a2022-06-27 23:09:03 +0000398 res.stringResponse = parser->release();
399 callback(parser->keep_alive(), connId, res);
Carson Labrado513d1ff2022-07-19 00:38:15 +0000400 res.clear();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530401 }
402
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800403 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
404 const boost::system::error_code ec)
405 {
406 if (ec == boost::asio::error::operation_aborted)
407 {
408 BMCWEB_LOG_DEBUG
Carson Labrado513d1ff2022-07-19 00:38:15 +0000409 << "async_wait failed since the operation is aborted";
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800410 return;
411 }
412 if (ec)
413 {
414 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
415 // If the timer fails, we need to close the socket anyway, same as
416 // if it expired.
417 }
418 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
419 if (self == nullptr)
420 {
421 return;
422 }
423 self->waitAndRetry();
424 }
425
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530426 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530427 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000428 if ((retryCount >= retryPolicy.maxRetryAttempts) ||
429 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530430 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530431 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000432 BMCWEB_LOG_DEBUG << "Retry policy: "
433 << retryPolicy.retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000434
Carson Labradof52c03c2022-03-23 18:50:15 +0000435 if (retryPolicy.retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530436 {
437 // TODO: delete subscription
438 state = ConnState::terminated;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530439 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000440 if (retryPolicy.retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530441 {
442 state = ConnState::suspended;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530443 }
Carson Labrado513d1ff2022-07-19 00:38:15 +0000444
445 // We want to return a 502 to indicate there was an error with
446 // the external server
447 res.result(boost::beast::http::status::bad_gateway);
448 callback(false, connId, res);
449 res.clear();
450
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530451 // Reset the retrycount to zero so that client can try connecting
452 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700453 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530454 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530455 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530456
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530457 retryCount++;
458
Carson Labradof52c03c2022-03-23 18:50:15 +0000459 BMCWEB_LOG_DEBUG << "Attempt retry after "
460 << std::to_string(
461 retryPolicy.retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530462 << " seconds. RetryCount = " << retryCount;
Carson Labradof52c03c2022-03-23 18:50:15 +0000463 timer.expires_after(retryPolicy.retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700464 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
465 shared_from_this()));
466 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530467
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700468 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
469 const boost::system::error_code& ec)
470 {
471 if (ec == boost::asio::error::operation_aborted)
472 {
473 BMCWEB_LOG_DEBUG
474 << "async_wait failed since the operation is aborted"
475 << ec.message();
476 }
477 else if (ec)
478 {
479 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
480 // Ignore the error and continue the retry loop to attempt
481 // sending the event as per the retry policy
482 }
483
484 // Let's close the connection and restart from resolve.
485 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530486 }
487
AppaRao Pulie38778a2022-06-27 23:09:03 +0000488 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530489 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000490 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800491 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000492 conn.close();
493
494 // not_connected happens sometimes so don't bother reporting it.
495 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530496 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000497 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
498 << ", id: " << std::to_string(connId)
Carson Labrado513d1ff2022-07-19 00:38:15 +0000499 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000500 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000501 else
502 {
503 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
504 << ", id: " << std::to_string(connId)
505 << " closed gracefully";
506 }
Ed Tanousca723762022-06-28 19:40:39 -0700507
Carson Labrado513d1ff2022-07-19 00:38:15 +0000508 if (retry)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000509 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000510 // Now let's try to resend the data
511 state = ConnState::retry;
512 doResolve();
513 }
514 else
515 {
516 state = ConnState::closed;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000517 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000518 }
519
AppaRao Pulie38778a2022-06-27 23:09:03 +0000520 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000521 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000522 if (!sslConn)
523 {
524 shutdownConn(retry);
525 return;
526 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000527
AppaRao Pulie38778a2022-06-27 23:09:03 +0000528 sslConn->async_shutdown(
529 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
530 shared_from_this(), retry));
531 }
532
533 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
534 bool retry, const boost::system::error_code& ec)
535 {
536
537 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000538 {
539 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
540 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000541 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000542 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000543 else
544 {
545 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
546 << ", id: " << std::to_string(connId)
547 << " closed gracefully";
548 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000549 shutdownConn(retry);
550 }
Ed Tanousca723762022-06-28 19:40:39 -0700551
AppaRao Pulie38778a2022-06-27 23:09:03 +0000552 void setCipherSuiteTLSext()
553 {
554 if (!sslConn)
555 {
556 return;
557 }
558 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
559 // file but its having old style casting (name is cast to void*).
560 // Since bmcweb compiler treats all old-style-cast as error, its
561 // causing the build failure. So replaced the same macro inline and
562 // did corrected the code by doing static_cast to viod*. This has to
563 // be fixed in openssl library in long run. Set SNI Hostname (many
564 // hosts need this to handshake successfully)
565 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
566 TLSEXT_NAMETYPE_host_name,
567 static_cast<void*>(&host.front())) == 0)
568
569 {
570 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
571 boost::asio::error::get_ssl_category()};
572
573 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
574 << port << ", id: " << std::to_string(connId)
575 << " failed: " << ec.message();
576 // Set state as sslInit failed so that we close the connection
577 // and take appropriate action as per retry configuration.
578 state = ConnState::sslInitFailed;
579 waitAndRetry();
580 return;
581 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530582 }
583
584 public:
AppaRao Pulie38778a2022-06-27 23:09:03 +0000585 explicit ConnectionInfo(boost::asio::io_context& iocIn,
586 const std::string& idIn,
587 const std::string& destIPIn, uint16_t destPortIn,
588 bool useSSL, unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700589 subId(idIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000590 host(destIPIn), port(destPortIn), connId(connIdIn), conn(iocIn),
591 timer(iocIn)
592 {
593 if (useSSL)
594 {
595 std::optional<boost::asio::ssl::context> sslCtx =
596 ensuressl::getSSLClientContext();
597
598 if (!sslCtx)
599 {
600 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
601 << port << ", id: " << std::to_string(connId);
602 // Don't retry if failure occurs while preparing SSL context
603 // such as certificate is invalid or set cipher failure or set
604 // host name failure etc... Setting conn state to sslInitFailed
605 // and connection state will be transitioned to next state
606 // depending on retry policy set by subscription.
607 state = ConnState::sslInitFailed;
608 waitAndRetry();
609 return;
610 }
611 sslConn.emplace(conn, *sslCtx);
612 setCipherSuiteTLSext();
613 }
614 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000615};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530616
Carson Labradof52c03c2022-03-23 18:50:15 +0000617class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
618{
619 private:
620 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000621 std::string id;
622 std::string destIP;
623 uint16_t destPort;
624 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000625 std::vector<std::shared_ptr<ConnectionInfo>> connections;
626 boost::container::devector<PendingRequest> requestQueue;
627
628 friend class HttpClient;
629
Carson Labrado244256c2022-04-27 17:16:32 +0000630 // Configure a connections's request, callback, and retry info in
631 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000632 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530633 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000634 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530635 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000636 BMCWEB_LOG_ERROR
637 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530638 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530639 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530640
Carson Labrado244256c2022-04-27 17:16:32 +0000641 auto nextReq = requestQueue.front();
642 conn.retryPolicy = std::move(nextReq.retryPolicy);
643 conn.req = std::move(nextReq.req);
644 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000645
646 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
647 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000648 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000649
650 // We can remove the request from the queue at this point
651 requestQueue.pop_front();
652 }
653
654 // Configures a connection to use the specific retry policy.
655 inline void setConnRetryPolicy(ConnectionInfo& conn,
656 const RetryPolicyData& retryPolicy)
657 {
658 BMCWEB_LOG_DEBUG << destIP << ":" << std::to_string(destPort)
Carson Labradoa7a80292022-06-01 16:01:52 +0000659 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000660
661 conn.retryPolicy = retryPolicy;
662 }
663
664 // Gets called as part of callback after request is sent
665 // Reuses the connection if there are any requests waiting to be sent
666 // Otherwise closes the connection if it is not a keep-alive
667 void sendNext(bool keepAlive, uint32_t connId)
668 {
669 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000670
671 // Allow the connection's handler to be deleted
672 // This is needed because of Redfish Aggregation passing an
673 // AsyncResponse shared_ptr to this callback
674 conn->callback = nullptr;
675
Carson Labradof52c03c2022-03-23 18:50:15 +0000676 // Reuse the connection to send the next request in the queue
677 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530678 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000679 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
680 << " requests remaining in queue for " << destIP
681 << ":" << std::to_string(destPort)
682 << ", reusing connnection "
683 << std::to_string(connId);
684
685 setConnProps(*conn);
686
687 if (keepAlive)
688 {
689 conn->sendMessage();
690 }
691 else
692 {
693 // Server is not keep-alive enabled so we need to close the
694 // connection and then start over from resolve
695 conn->doClose();
696 conn->doResolve();
697 }
698 return;
699 }
700
701 // No more messages to send so close the connection if necessary
702 if (keepAlive)
703 {
704 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530705 }
706 else
707 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000708 // Abort the connection since server is not keep-alive enabled
709 conn->state = ConnState::abortConnection;
710 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530711 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530712 }
713
Carson Labrado244256c2022-04-27 17:16:32 +0000714 void sendData(std::string& data, const std::string& destUri,
715 const boost::beast::http::fields& httpHeader,
716 const boost::beast::http::verb verb,
717 const RetryPolicyData& retryPolicy,
Ed Tanous6b3db602022-06-28 19:41:44 -0700718 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530719 {
Carson Labrado244256c2022-04-27 17:16:32 +0000720 // Construct the request to be sent
721 boost::beast::http::request<boost::beast::http::string_body> thisReq(
722 verb, destUri, 11, "", httpHeader);
723 thisReq.set(boost::beast::http::field::host, destIP);
724 thisReq.keep_alive(true);
725 thisReq.body() = std::move(data);
726 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700727 auto cb = std::bind_front(&ConnectionPool::afterSendData,
728 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000729 // Reuse an existing connection if one is available
730 for (unsigned int i = 0; i < connections.size(); i++)
731 {
732 auto conn = connections[i];
733 if ((conn->state == ConnState::idle) ||
734 (conn->state == ConnState::initialized) ||
735 (conn->state == ConnState::closed))
736 {
Carson Labrado244256c2022-04-27 17:16:32 +0000737 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000738 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000739 setConnRetryPolicy(*conn, retryPolicy);
740 std::string commonMsg = std::to_string(i) + " from pool " +
741 destIP + ":" + std::to_string(destPort);
742
743 if (conn->state == ConnState::idle)
744 {
745 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
746 << commonMsg;
747 conn->sendMessage();
748 }
749 else
750 {
751 BMCWEB_LOG_DEBUG << "Reusing existing connection "
752 << commonMsg;
753 conn->doResolve();
754 }
755 return;
756 }
757 }
758
759 // All connections in use so create a new connection or add request to
760 // the queue
761 if (connections.size() < maxPoolSize)
762 {
763 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
764 << ":" << std::to_string(destPort);
765 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000766 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000767 conn->callback = std::move(cb);
768 setConnRetryPolicy(*conn, retryPolicy);
769 conn->doResolve();
770 }
771 else if (requestQueue.size() < maxRequestQueueSize)
772 {
773 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labrado244256c2022-04-27 17:16:32 +0000774 requestQueue.emplace_back(std::move(thisReq), std::move(cb),
Carson Labradof52c03c2022-03-23 18:50:15 +0000775 retryPolicy);
776 }
777 else
778 {
Carson Labrado43e14d32022-11-09 00:25:20 +0000779 // If we can't buffer the request then we should let the callback
780 // handle a 429 Too Many Requests dummy response
Carson Labradof52c03c2022-03-23 18:50:15 +0000781 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
782 << " request queue full. Dropping request.";
Carson Labrado43e14d32022-11-09 00:25:20 +0000783 Response dummyRes;
784 dummyRes.result(boost::beast::http::status::too_many_requests);
785 resHandler(dummyRes);
Carson Labradof52c03c2022-03-23 18:50:15 +0000786 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530787 }
788
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700789 // Callback to be called once the request has been sent
790 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
791 const std::function<void(Response&)>& resHandler,
792 bool keepAlive, uint32_t connId, Response& res)
793 {
794 // Allow provided callback to perform additional processing of the
795 // request
796 resHandler(res);
797
798 // If requests remain in the queue then we want to reuse this
799 // connection to send the next request
800 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
801 if (!self)
802 {
803 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
804 return;
805 }
806
807 self->sendNext(keepAlive, connId);
808 }
809
Carson Labradof52c03c2022-03-23 18:50:15 +0000810 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530811 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000812 unsigned int newId = static_cast<unsigned int>(connections.size());
813
AppaRao Pulie38778a2022-06-27 23:09:03 +0000814 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
815 ioc, id, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000816
817 BMCWEB_LOG_DEBUG << "Added connection "
818 << std::to_string(connections.size() - 1)
819 << " to pool " << destIP << ":"
820 << std::to_string(destPort);
821
822 return ret;
823 }
824
825 public:
Ed Tanous8a592812022-06-04 09:06:59 -0700826 explicit ConnectionPool(boost::asio::io_context& iocIn,
827 const std::string& idIn,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000828 const std::string& destIPIn, uint16_t destPortIn,
829 bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700830 ioc(iocIn),
AppaRao Pulie38778a2022-06-27 23:09:03 +0000831 id(idIn), destIP(destIPIn), destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000832 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000833 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
834 << std::to_string(destPort);
835
836 // Initialize the pool with a single connection
837 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530838 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530839};
840
Carson Labradof52c03c2022-03-23 18:50:15 +0000841class HttpClient
842{
843 private:
844 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
845 connectionPools;
846 boost::asio::io_context& ioc =
847 crow::connections::systemBus->get_io_context();
848 std::unordered_map<std::string, RetryPolicyData> retryInfo;
849 HttpClient() = default;
850
Carson Labrado039a47e2022-04-05 16:03:20 +0000851 // Used as a dummy callback by sendData() in order to call
852 // sendDataWithCallback()
Ed Tanous02cad962022-06-30 16:50:15 -0700853 static void genericResHandler(const Response& res)
Carson Labrado039a47e2022-04-05 16:03:20 +0000854 {
855 BMCWEB_LOG_DEBUG << "Response handled with return code: "
856 << std::to_string(res.resultInt());
Ed Tanous4ee8e212022-05-28 09:42:51 -0700857 }
Carson Labrado039a47e2022-04-05 16:03:20 +0000858
Carson Labradof52c03c2022-03-23 18:50:15 +0000859 public:
860 HttpClient(const HttpClient&) = delete;
861 HttpClient& operator=(const HttpClient&) = delete;
862 HttpClient(HttpClient&&) = delete;
863 HttpClient& operator=(HttpClient&&) = delete;
864 ~HttpClient() = default;
865
866 static HttpClient& getInstance()
867 {
868 static HttpClient handler;
869 return handler;
870 }
871
Carson Labrado039a47e2022-04-05 16:03:20 +0000872 // Send a request to destIP:destPort where additional processing of the
873 // result is not required
Carson Labradof52c03c2022-03-23 18:50:15 +0000874 void sendData(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000875 const std::string& destIP, uint16_t destPort,
876 const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000877 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000878 const boost::beast::http::verb verb,
879 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000880 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000881 const std::function<void(Response&)> cb = genericResHandler;
882 sendDataWithCallback(data, id, destIP, destPort, destUri, useSSL,
883 httpHeader, verb, retryPolicyName, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000884 }
885
886 // Send request to destIP:destPort and use the provided callback to
887 // handle the response
888 void sendDataWithCallback(std::string& data, const std::string& id,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000889 const std::string& destIP, uint16_t destPort,
890 const std::string& destUri, bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000891 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000892 const boost::beast::http::verb verb,
893 const std::string& retryPolicyName,
Ed Tanous6b3db602022-06-28 19:41:44 -0700894 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000895 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000896 std::string clientKey = useSSL ? "https" : "http";
897 clientKey += destIP;
898 clientKey += ":";
899 clientKey += std::to_string(destPort);
Carson Labradof52c03c2022-03-23 18:50:15 +0000900 // Use nullptr to avoid creating a ConnectionPool each time
AppaRao Pulie38778a2022-06-27 23:09:03 +0000901 std::shared_ptr<ConnectionPool>& conn = connectionPools[clientKey];
902 if (conn == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000903 {
904 // Now actually create the ConnectionPool shared_ptr since it does
905 // not already exist
AppaRao Pulie38778a2022-06-27 23:09:03 +0000906 conn = std::make_shared<ConnectionPool>(ioc, id, destIP, destPort,
907 useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000908 BMCWEB_LOG_DEBUG << "Created connection pool for " << clientKey;
909 }
910 else
911 {
912 BMCWEB_LOG_DEBUG << "Using existing connection pool for "
913 << clientKey;
914 }
915
916 // Get the associated retry policy
917 auto policy = retryInfo.try_emplace(retryPolicyName);
918 if (policy.second)
919 {
920 BMCWEB_LOG_DEBUG << "Creating retry policy \"" << retryPolicyName
921 << "\" with default values";
Carson Labradof52c03c2022-03-23 18:50:15 +0000922 }
923
924 // Send the data using either the existing connection pool or the newly
925 // created connection pool
AppaRao Pulie38778a2022-06-27 23:09:03 +0000926 conn->sendData(data, destUri, httpHeader, verb, policy.first->second,
927 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000928 }
929
Carson Labradoa7a80292022-06-01 16:01:52 +0000930 void setRetryConfig(
931 const uint32_t retryAttempts, const uint32_t retryTimeoutInterval,
932 const std::function<boost::system::error_code(unsigned int respCode)>&
933 invalidResp,
934 const std::string& retryPolicyName)
Carson Labradof52c03c2022-03-23 18:50:15 +0000935 {
936 // We need to create the retry policy if one does not already exist for
937 // the given retryPolicyName
938 auto result = retryInfo.try_emplace(retryPolicyName);
939 if (result.second)
940 {
941 BMCWEB_LOG_DEBUG << "setRetryConfig(): Creating new retry policy \""
942 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000943 }
944 else
945 {
946 BMCWEB_LOG_DEBUG << "setRetryConfig(): Updating retry info for \""
947 << retryPolicyName << "\"";
948 }
949
950 result.first->second.maxRetryAttempts = retryAttempts;
951 result.first->second.retryIntervalSecs =
952 std::chrono::seconds(retryTimeoutInterval);
Carson Labradoa7a80292022-06-01 16:01:52 +0000953 result.first->second.invalidResp = invalidResp;
Carson Labradof52c03c2022-03-23 18:50:15 +0000954 }
955
956 void setRetryPolicy(const std::string& retryPolicy,
957 const std::string& retryPolicyName)
958 {
959 // We need to create the retry policy if one does not already exist for
960 // the given retryPolicyName
961 auto result = retryInfo.try_emplace(retryPolicyName);
962 if (result.second)
963 {
964 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Creating new retry policy \""
965 << retryPolicyName << "\"";
Carson Labradof52c03c2022-03-23 18:50:15 +0000966 }
967 else
968 {
969 BMCWEB_LOG_DEBUG << "setRetryPolicy(): Updating retry policy for \""
970 << retryPolicyName << "\"";
971 }
972
973 result.first->second.retryPolicyAction = retryPolicy;
974 }
975};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530976} // namespace crow