blob: 07fa85d6f934a8e04a6241b0494dc7f65ee5199e [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
Carson Labradod14a48f2023-02-22 00:24:54 +0000100struct ConnectionPolicy
Carson Labradof52c03c2022-03-23 18:50:15 +0000101{
102 uint32_t maxRetryAttempts = 5;
Carson Labradod14a48f2023-02-22 00:24:54 +0000103
104 // the max size of requests in bytes. 0 for unlimited
105 boost::optional<uint64_t> requestByteLimit = httpReadBodyLimit;
106
107 size_t maxConnections = 1;
108
Carson Labradof52c03c2022-03-23 18:50:15 +0000109 std::string retryPolicyAction = "TerminateAfterRetries";
Carson Labradod14a48f2023-02-22 00:24:54 +0000110
111 std::chrono::seconds retryIntervalSecs = std::chrono::seconds(0);
Carson Labradoa7a80292022-06-01 16:01:52 +0000112 std::function<boost::system::error_code(unsigned int respCode)>
113 invalidResp = defaultRetryHandler;
Carson Labradof52c03c2022-03-23 18:50:15 +0000114};
115
116struct PendingRequest
117{
Carson Labrado244256c2022-04-27 17:16:32 +0000118 boost::beast::http::request<boost::beast::http::string_body> req;
Carson Labrado039a47e2022-04-05 16:03:20 +0000119 std::function<void(bool, uint32_t, Response&)> callback;
Carson Labrado039a47e2022-04-05 16:03:20 +0000120 PendingRequest(
Ed Tanous8a592812022-06-04 09:06:59 -0700121 boost::beast::http::request<boost::beast::http::string_body>&& reqIn,
Carson Labradod14a48f2023-02-22 00:24:54 +0000122 const std::function<void(bool, uint32_t, Response&)>& callbackIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700123 req(std::move(reqIn)),
Carson Labradod14a48f2023-02-22 00:24:54 +0000124 callback(callbackIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000125 {}
126};
127
128class ConnectionInfo : public std::enable_shared_from_this<ConnectionInfo>
AppaRao Pulibd030d02020-03-20 03:34:29 +0530129{
130 private:
Carson Labradof52c03c2022-03-23 18:50:15 +0000131 ConnState state = ConnState::initialized;
132 uint32_t retryCount = 0;
Carson Labradof52c03c2022-03-23 18:50:15 +0000133 std::string subId;
Carson Labradod14a48f2023-02-22 00:24:54 +0000134 std::shared_ptr<ConnectionPolicy> connPolicy;
Carson Labradof52c03c2022-03-23 18:50:15 +0000135 std::string host;
136 uint16_t port;
137 uint32_t connId;
138
Carson Labradof52c03c2022-03-23 18:50:15 +0000139 // Data buffers
AppaRao Pulibd030d02020-03-20 03:34:29 +0530140 boost::beast::http::request<boost::beast::http::string_body> req;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530141 std::optional<
142 boost::beast::http::response_parser<boost::beast::http::string_body>>
143 parser;
Carson Labrado4d942722022-06-22 22:16:10 +0000144 boost::beast::flat_static_buffer<httpReadBufferSize> buffer;
Carson Labrado039a47e2022-04-05 16:03:20 +0000145 Response res;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530146
Carson Labradof52c03c2022-03-23 18:50:15 +0000147 // Ascync callables
Carson Labrado039a47e2022-04-05 16:03:20 +0000148 std::function<void(bool, uint32_t, Response&)> callback;
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700149
150#ifdef BMCWEB_DBUS_DNS_RESOLVER
Ed Tanouse1452be2021-10-04 17:03:52 -0700151 using Resolver = async_resolve::Resolver;
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700152#else
153 using Resolver = boost::asio::ip::tcp::resolver;
154#endif
155 Resolver resolver;
156
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800157 boost::asio::ip::tcp::socket conn;
158 std::optional<boost::beast::ssl_stream<boost::asio::ip::tcp::socket&>>
159 sslConn;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000160
Carson Labradof52c03c2022-03-23 18:50:15 +0000161 boost::asio::steady_timer timer;
Ed Tanous84b35602021-09-08 20:06:32 -0700162
Carson Labradof52c03c2022-03-23 18:50:15 +0000163 friend class ConnectionPool;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530164
Sunitha Harish29a82b02021-02-18 15:54:16 +0530165 void doResolve()
166 {
Sunitha Harish29a82b02021-02-18 15:54:16 +0530167 state = ConnState::resolveInProgress;
Carson Labradof52c03c2022-03-23 18:50:15 +0000168 BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
169 << std::to_string(port)
170 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530171
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700172 resolver.async_resolve(host, std::to_string(port),
173 std::bind_front(&ConnectionInfo::afterResolve,
174 this, shared_from_this()));
Sunitha Harish29a82b02021-02-18 15:54:16 +0530175 }
176
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700177 void afterResolve(const std::shared_ptr<ConnectionInfo>& /*self*/,
178 const boost::system::error_code& ec,
179 const Resolver::results_type& endpointList)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530180 {
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700181 if (ec || (endpointList.empty()))
182 {
Sunitha Harishd7043b32023-07-11 04:49:03 -0500183 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message() << " "
184 << host << ":" << std::to_string(port);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700185 state = ConnState::resolveFailed;
186 waitAndRetry();
187 return;
188 }
189 BMCWEB_LOG_DEBUG << "Resolved " << host << ":" << std::to_string(port)
190 << ", id: " << std::to_string(connId);
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530191 state = ConnState::connectInProgress;
192
Carson Labradof52c03c2022-03-23 18:50:15 +0000193 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
194 << std::to_string(port)
195 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530196
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800197 timer.expires_after(std::chrono::seconds(30));
198 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
199
200 boost::asio::async_connect(
201 conn, endpointList,
202 std::bind_front(&ConnectionInfo::afterConnect, this,
203 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000204 }
205
206 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
Ed Tanous81c4e332023-05-18 10:30:34 -0700207 const boost::beast::error_code& ec,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000208 const boost::asio::ip::tcp::endpoint& endpoint)
209 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000210 // The operation already timed out. We don't want do continue down
211 // this branch
212 if (ec && ec == boost::asio::error::operation_aborted)
213 {
214 return;
215 }
216
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800217 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000218 if (ec)
219 {
220 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
221 << ":" << std::to_string(endpoint.port())
222 << ", id: " << std::to_string(connId)
223 << " failed: " << ec.message();
224 state = ConnState::connectFailed;
225 waitAndRetry();
226 return;
227 }
228 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
229 << ":" << std::to_string(endpoint.port())
230 << ", id: " << std::to_string(connId);
231 if (sslConn)
232 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800233 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000234 return;
235 }
236 state = ConnState::connected;
237 sendMessage();
238 }
239
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800240 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000241 {
242 if (!sslConn)
243 {
244 return;
245 }
246 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800247 timer.expires_after(std::chrono::seconds(30));
248 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000249 sslConn->async_handshake(
250 boost::asio::ssl::stream_base::client,
251 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
252 shared_from_this()));
253 }
254
255 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
Ed Tanous81c4e332023-05-18 10:30:34 -0700256 const boost::beast::error_code& ec)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000257 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000258 // The operation already timed out. We don't want do continue down
259 // this branch
260 if (ec && ec == boost::asio::error::operation_aborted)
261 {
262 return;
263 }
264
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800265 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000266 if (ec)
267 {
268 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
269 << " id: " << std::to_string(connId)
270 << " error: " << ec.message();
271 state = ConnState::handshakeFailed;
272 waitAndRetry();
273 return;
274 }
275 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
276 << " id: " << std::to_string(connId);
277 state = ConnState::connected;
278 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530279 }
280
Carson Labradof52c03c2022-03-23 18:50:15 +0000281 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530282 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530283 state = ConnState::sendInProgress;
284
AppaRao Pulibd030d02020-03-20 03:34:29 +0530285 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800286 timer.expires_after(std::chrono::seconds(30));
287 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530288
289 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000290 if (sslConn)
291 {
292 boost::beast::http::async_write(
293 *sslConn, req,
294 std::bind_front(&ConnectionInfo::afterWrite, this,
295 shared_from_this()));
296 }
297 else
298 {
299 boost::beast::http::async_write(
300 conn, req,
301 std::bind_front(&ConnectionInfo::afterWrite, this,
302 shared_from_this()));
303 }
304 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530305
AppaRao Pulie38778a2022-06-27 23:09:03 +0000306 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
307 const boost::beast::error_code& ec, size_t bytesTransferred)
308 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000309 // The operation already timed out. We don't want do continue down
310 // this branch
311 if (ec && ec == boost::asio::error::operation_aborted)
312 {
313 return;
314 }
315
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800316 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000317 if (ec)
318 {
Sunitha Harishd7043b32023-07-11 04:49:03 -0500319 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message() << " "
320 << host << ":" << std::to_string(port);
AppaRao Pulie38778a2022-06-27 23:09:03 +0000321 state = ConnState::sendFailed;
322 waitAndRetry();
323 return;
324 }
325 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
326 << bytesTransferred;
327
328 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530329 }
330
331 void recvMessage()
332 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530333 state = ConnState::recvInProgress;
334
335 parser.emplace(std::piecewise_construct, std::make_tuple());
Carson Labradod14a48f2023-02-22 00:24:54 +0000336
337 parser->body_limit(connPolicy->requestByteLimit);
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530338
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800339 timer.expires_after(std::chrono::seconds(30));
340 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
341
AppaRao Pulibd030d02020-03-20 03:34:29 +0530342 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000343 if (sslConn)
344 {
345 boost::beast::http::async_read(
346 *sslConn, buffer, *parser,
347 std::bind_front(&ConnectionInfo::afterRead, this,
348 shared_from_this()));
349 }
350 else
351 {
352 boost::beast::http::async_read(
353 conn, buffer, *parser,
354 std::bind_front(&ConnectionInfo::afterRead, this,
355 shared_from_this()));
356 }
357 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530358
AppaRao Pulie38778a2022-06-27 23:09:03 +0000359 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
360 const boost::beast::error_code& ec,
361 const std::size_t& bytesTransferred)
362 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000363 // The operation already timed out. We don't want do continue down
364 // this branch
365 if (ec && ec == boost::asio::error::operation_aborted)
366 {
367 return;
368 }
369
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800370 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000371 if (ec && ec != boost::asio::ssl::error::stream_truncated)
372 {
Sunitha Harishd7043b32023-07-11 04:49:03 -0500373 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message()
374 << " from " << host << ":" << std::to_string(port);
AppaRao Pulie38778a2022-06-27 23:09:03 +0000375 state = ConnState::recvFailed;
376 waitAndRetry();
377 return;
378 }
379 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
380 << bytesTransferred;
381 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
382
383 unsigned int respCode = parser->get().result_int();
384 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
385
386 // Make sure the received response code is valid as defined by
387 // the associated retry policy
Carson Labradod14a48f2023-02-22 00:24:54 +0000388 if (connPolicy->invalidResp(respCode))
AppaRao Pulie38778a2022-06-27 23:09:03 +0000389 {
390 // The listener failed to receive the Sent-Event
391 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
392 "receive Sent-Event. Header Response Code: "
Sunitha Harishd7043b32023-07-11 04:49:03 -0500393 << respCode << " from " << host << ":"
394 << std::to_string(port);
AppaRao Pulie38778a2022-06-27 23:09:03 +0000395 state = ConnState::recvFailed;
396 waitAndRetry();
397 return;
398 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700399
AppaRao Pulie38778a2022-06-27 23:09:03 +0000400 // Send is successful
401 // Reset the counter just in case this was after retrying
402 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530403
AppaRao Pulie38778a2022-06-27 23:09:03 +0000404 // Keep the connection alive if server supports it
405 // Else close the connection
406 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
407 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530408
AppaRao Pulie38778a2022-06-27 23:09:03 +0000409 // Copy the response into a Response object so that it can be
410 // processed by the callback function.
AppaRao Pulie38778a2022-06-27 23:09:03 +0000411 res.stringResponse = parser->release();
412 callback(parser->keep_alive(), connId, res);
Carson Labrado513d1ff2022-07-19 00:38:15 +0000413 res.clear();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530414 }
415
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800416 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800417 const boost::system::error_code& ec)
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800418 {
419 if (ec == boost::asio::error::operation_aborted)
420 {
421 BMCWEB_LOG_DEBUG
Carson Labrado513d1ff2022-07-19 00:38:15 +0000422 << "async_wait failed since the operation is aborted";
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800423 return;
424 }
425 if (ec)
426 {
427 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
428 // If the timer fails, we need to close the socket anyway, same as
429 // if it expired.
430 }
431 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
432 if (self == nullptr)
433 {
434 return;
435 }
436 self->waitAndRetry();
437 }
438
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530439 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530440 {
Carson Labradod14a48f2023-02-22 00:24:54 +0000441 if ((retryCount >= connPolicy->maxRetryAttempts) ||
AppaRao Pulie38778a2022-06-27 23:09:03 +0000442 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530443 {
Sunitha Harishd7043b32023-07-11 04:49:03 -0500444 BMCWEB_LOG_ERROR << "Maximum number of retries reached."
445 << " " << host << ":" << std::to_string(port);
Carson Labradof52c03c2022-03-23 18:50:15 +0000446 BMCWEB_LOG_DEBUG << "Retry policy: "
Carson Labradod14a48f2023-02-22 00:24:54 +0000447 << connPolicy->retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000448
Carson Labradod14a48f2023-02-22 00:24:54 +0000449 if (connPolicy->retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530450 {
451 // TODO: delete subscription
452 state = ConnState::terminated;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530453 }
Carson Labradod14a48f2023-02-22 00:24:54 +0000454 if (connPolicy->retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530455 {
456 state = ConnState::suspended;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530457 }
Carson Labrado513d1ff2022-07-19 00:38:15 +0000458
459 // We want to return a 502 to indicate there was an error with
460 // the external server
461 res.result(boost::beast::http::status::bad_gateway);
462 callback(false, connId, res);
463 res.clear();
464
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530465 // Reset the retrycount to zero so that client can try connecting
466 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700467 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530468 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530469 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530470
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530471 retryCount++;
472
Carson Labradof52c03c2022-03-23 18:50:15 +0000473 BMCWEB_LOG_DEBUG << "Attempt retry after "
474 << std::to_string(
Carson Labradod14a48f2023-02-22 00:24:54 +0000475 connPolicy->retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530476 << " seconds. RetryCount = " << retryCount;
Carson Labradod14a48f2023-02-22 00:24:54 +0000477 timer.expires_after(connPolicy->retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700478 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
479 shared_from_this()));
480 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530481
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700482 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
483 const boost::system::error_code& ec)
484 {
485 if (ec == boost::asio::error::operation_aborted)
486 {
487 BMCWEB_LOG_DEBUG
488 << "async_wait failed since the operation is aborted"
489 << ec.message();
490 }
491 else if (ec)
492 {
493 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
494 // Ignore the error and continue the retry loop to attempt
495 // sending the event as per the retry policy
496 }
497
498 // Let's close the connection and restart from resolve.
499 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530500 }
501
AppaRao Pulie38778a2022-06-27 23:09:03 +0000502 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530503 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000504 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800505 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000506 conn.close();
507
508 // not_connected happens sometimes so don't bother reporting it.
509 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530510 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000511 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
512 << ", id: " << std::to_string(connId)
Carson Labrado513d1ff2022-07-19 00:38:15 +0000513 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000514 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000515 else
516 {
517 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
518 << ", id: " << std::to_string(connId)
519 << " closed gracefully";
520 }
Ed Tanousca723762022-06-28 19:40:39 -0700521
Carson Labrado513d1ff2022-07-19 00:38:15 +0000522 if (retry)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000523 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000524 // Now let's try to resend the data
525 state = ConnState::retry;
526 doResolve();
527 }
528 else
529 {
530 state = ConnState::closed;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000531 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000532 }
533
AppaRao Pulie38778a2022-06-27 23:09:03 +0000534 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000535 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000536 if (!sslConn)
537 {
538 shutdownConn(retry);
539 return;
540 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000541
AppaRao Pulie38778a2022-06-27 23:09:03 +0000542 sslConn->async_shutdown(
543 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
544 shared_from_this(), retry));
545 }
546
547 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
548 bool retry, const boost::system::error_code& ec)
549 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000550 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000551 {
552 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
553 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000554 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000555 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000556 else
557 {
558 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
559 << ", id: " << std::to_string(connId)
560 << " closed gracefully";
561 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000562 shutdownConn(retry);
563 }
Ed Tanousca723762022-06-28 19:40:39 -0700564
AppaRao Pulie38778a2022-06-27 23:09:03 +0000565 void setCipherSuiteTLSext()
566 {
567 if (!sslConn)
568 {
569 return;
570 }
571 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
572 // file but its having old style casting (name is cast to void*).
573 // Since bmcweb compiler treats all old-style-cast as error, its
574 // causing the build failure. So replaced the same macro inline and
575 // did corrected the code by doing static_cast to viod*. This has to
576 // be fixed in openssl library in long run. Set SNI Hostname (many
577 // hosts need this to handshake successfully)
578 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
579 TLSEXT_NAMETYPE_host_name,
580 static_cast<void*>(&host.front())) == 0)
581
582 {
583 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
584 boost::asio::error::get_ssl_category()};
585
586 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
587 << port << ", id: " << std::to_string(connId)
588 << " failed: " << ec.message();
589 // Set state as sslInit failed so that we close the connection
590 // and take appropriate action as per retry configuration.
591 state = ConnState::sslInitFailed;
592 waitAndRetry();
593 return;
594 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530595 }
596
597 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000598 explicit ConnectionInfo(
599 boost::asio::io_context& iocIn, const std::string& idIn,
600 const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
601 const std::string& destIPIn, uint16_t destPortIn, bool useSSL,
602 unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700603 subId(idIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000604 connPolicy(connPolicyIn), host(destIPIn), port(destPortIn),
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700605 connId(connIdIn), resolver(iocIn), conn(iocIn), timer(iocIn)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000606 {
607 if (useSSL)
608 {
609 std::optional<boost::asio::ssl::context> sslCtx =
610 ensuressl::getSSLClientContext();
611
612 if (!sslCtx)
613 {
614 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
615 << port << ", id: " << std::to_string(connId);
616 // Don't retry if failure occurs while preparing SSL context
617 // such as certificate is invalid or set cipher failure or set
618 // host name failure etc... Setting conn state to sslInitFailed
619 // and connection state will be transitioned to next state
620 // depending on retry policy set by subscription.
621 state = ConnState::sslInitFailed;
622 waitAndRetry();
623 return;
624 }
625 sslConn.emplace(conn, *sslCtx);
626 setCipherSuiteTLSext();
627 }
628 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000629};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530630
Carson Labradof52c03c2022-03-23 18:50:15 +0000631class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
632{
633 private:
634 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000635 std::string id;
Carson Labradod14a48f2023-02-22 00:24:54 +0000636 std::shared_ptr<ConnectionPolicy> connPolicy;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000637 std::string destIP;
638 uint16_t destPort;
639 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000640 std::vector<std::shared_ptr<ConnectionInfo>> connections;
641 boost::container::devector<PendingRequest> requestQueue;
642
643 friend class HttpClient;
644
Carson Labrado244256c2022-04-27 17:16:32 +0000645 // Configure a connections's request, callback, and retry info in
646 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000647 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530648 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000649 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530650 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000651 BMCWEB_LOG_ERROR
652 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530653 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530654 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530655
Carson Labrado244256c2022-04-27 17:16:32 +0000656 auto nextReq = requestQueue.front();
Carson Labrado244256c2022-04-27 17:16:32 +0000657 conn.req = std::move(nextReq.req);
658 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000659
660 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
661 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000662 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000663
664 // We can remove the request from the queue at this point
665 requestQueue.pop_front();
666 }
667
Carson Labradof52c03c2022-03-23 18:50:15 +0000668 // Gets called as part of callback after request is sent
669 // Reuses the connection if there are any requests waiting to be sent
670 // Otherwise closes the connection if it is not a keep-alive
671 void sendNext(bool keepAlive, uint32_t connId)
672 {
673 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000674
675 // Allow the connection's handler to be deleted
676 // This is needed because of Redfish Aggregation passing an
677 // AsyncResponse shared_ptr to this callback
678 conn->callback = nullptr;
679
Carson Labradof52c03c2022-03-23 18:50:15 +0000680 // Reuse the connection to send the next request in the queue
681 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530682 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000683 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
684 << " requests remaining in queue for " << destIP
685 << ":" << std::to_string(destPort)
686 << ", reusing connnection "
687 << std::to_string(connId);
688
689 setConnProps(*conn);
690
691 if (keepAlive)
692 {
693 conn->sendMessage();
694 }
695 else
696 {
697 // Server is not keep-alive enabled so we need to close the
698 // connection and then start over from resolve
699 conn->doClose();
700 conn->doResolve();
701 }
702 return;
703 }
704
705 // No more messages to send so close the connection if necessary
706 if (keepAlive)
707 {
708 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530709 }
710 else
711 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000712 // Abort the connection since server is not keep-alive enabled
713 conn->state = ConnState::abortConnection;
714 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530715 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530716 }
717
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000718 void sendData(std::string&& data, const std::string& destUri,
Carson Labrado244256c2022-04-27 17:16:32 +0000719 const boost::beast::http::fields& httpHeader,
720 const boost::beast::http::verb verb,
Ed Tanous6b3db602022-06-28 19:41:44 -0700721 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530722 {
Carson Labrado244256c2022-04-27 17:16:32 +0000723 // Construct the request to be sent
724 boost::beast::http::request<boost::beast::http::string_body> thisReq(
725 verb, destUri, 11, "", httpHeader);
726 thisReq.set(boost::beast::http::field::host, destIP);
727 thisReq.keep_alive(true);
728 thisReq.body() = std::move(data);
729 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700730 auto cb = std::bind_front(&ConnectionPool::afterSendData,
731 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000732 // Reuse an existing connection if one is available
733 for (unsigned int i = 0; i < connections.size(); i++)
734 {
735 auto conn = connections[i];
736 if ((conn->state == ConnState::idle) ||
737 (conn->state == ConnState::initialized) ||
738 (conn->state == ConnState::closed))
739 {
Carson Labrado244256c2022-04-27 17:16:32 +0000740 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000741 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000742 std::string commonMsg = std::to_string(i) + " from pool " +
743 destIP + ":" + std::to_string(destPort);
744
745 if (conn->state == ConnState::idle)
746 {
747 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
748 << commonMsg;
749 conn->sendMessage();
750 }
751 else
752 {
753 BMCWEB_LOG_DEBUG << "Reusing existing connection "
754 << commonMsg;
755 conn->doResolve();
756 }
757 return;
758 }
759 }
760
761 // All connections in use so create a new connection or add request to
762 // the queue
Carson Labradod14a48f2023-02-22 00:24:54 +0000763 if (connections.size() < connPolicy->maxConnections)
Carson Labradof52c03c2022-03-23 18:50:15 +0000764 {
765 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
766 << ":" << std::to_string(destPort);
767 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000768 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000769 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000770 conn->doResolve();
771 }
772 else if (requestQueue.size() < maxRequestQueueSize)
773 {
Sunitha Harishd7043b32023-07-11 04:49:03 -0500774 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue."
775 << destIP << ":" << std::to_string(destPort);
Carson Labradod14a48f2023-02-22 00:24:54 +0000776 requestQueue.emplace_back(std::move(thisReq), std::move(cb));
Carson Labradof52c03c2022-03-23 18:50:15 +0000777 }
778 else
779 {
Carson Labrado43e14d32022-11-09 00:25:20 +0000780 // If we can't buffer the request then we should let the callback
781 // handle a 429 Too Many Requests dummy response
Carson Labradof52c03c2022-03-23 18:50:15 +0000782 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
783 << " request queue full. Dropping request.";
Carson Labrado43e14d32022-11-09 00:25:20 +0000784 Response dummyRes;
785 dummyRes.result(boost::beast::http::status::too_many_requests);
786 resHandler(dummyRes);
Carson Labradof52c03c2022-03-23 18:50:15 +0000787 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530788 }
789
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700790 // Callback to be called once the request has been sent
791 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
792 const std::function<void(Response&)>& resHandler,
793 bool keepAlive, uint32_t connId, Response& res)
794 {
795 // Allow provided callback to perform additional processing of the
796 // request
797 resHandler(res);
798
799 // If requests remain in the queue then we want to reuse this
800 // connection to send the next request
801 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
802 if (!self)
803 {
804 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
805 return;
806 }
807
808 self->sendNext(keepAlive, connId);
809 }
810
Carson Labradof52c03c2022-03-23 18:50:15 +0000811 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530812 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000813 unsigned int newId = static_cast<unsigned int>(connections.size());
814
AppaRao Pulie38778a2022-06-27 23:09:03 +0000815 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
Carson Labradod14a48f2023-02-22 00:24:54 +0000816 ioc, id, connPolicy, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000817
818 BMCWEB_LOG_DEBUG << "Added connection "
819 << std::to_string(connections.size() - 1)
820 << " to pool " << destIP << ":"
821 << std::to_string(destPort);
822
823 return ret;
824 }
825
826 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000827 explicit ConnectionPool(
828 boost::asio::io_context& iocIn, const std::string& idIn,
829 const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
830 const std::string& destIPIn, uint16_t destPortIn, bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700831 ioc(iocIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000832 id(idIn), connPolicy(connPolicyIn), destIP(destIPIn),
833 destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000834 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000835 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
836 << std::to_string(destPort);
837
838 // Initialize the pool with a single connection
839 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530840 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530841};
842
Carson Labradof52c03c2022-03-23 18:50:15 +0000843class HttpClient
844{
845 private:
846 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
847 connectionPools;
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700848 boost::asio::io_context& ioc;
Carson Labradod14a48f2023-02-22 00:24:54 +0000849 std::shared_ptr<ConnectionPolicy> connPolicy;
Carson Labradof52c03c2022-03-23 18:50:15 +0000850
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:
Carson Labradod14a48f2023-02-22 00:24:54 +0000860 HttpClient() = delete;
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700861 explicit HttpClient(boost::asio::io_context& iocIn,
862 const std::shared_ptr<ConnectionPolicy>& connPolicyIn) :
863 ioc(iocIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000864 connPolicy(connPolicyIn)
865 {}
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700866
Carson Labradof52c03c2022-03-23 18:50:15 +0000867 HttpClient(const HttpClient&) = delete;
868 HttpClient& operator=(const HttpClient&) = delete;
869 HttpClient(HttpClient&&) = delete;
870 HttpClient& operator=(HttpClient&&) = delete;
871 ~HttpClient() = default;
872
Carson Labrado039a47e2022-04-05 16:03:20 +0000873 // Send a request to destIP:destPort where additional processing of the
874 // result is not required
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000875 void sendData(std::string&& data, const std::string& destIP,
Carson Labradod14a48f2023-02-22 00:24:54 +0000876 uint16_t destPort, const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000877 const boost::beast::http::fields& httpHeader,
Carson Labradod14a48f2023-02-22 00:24:54 +0000878 const boost::beast::http::verb verb)
Carson Labradof52c03c2022-03-23 18:50:15 +0000879 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000880 const std::function<void(Response&)> cb = genericResHandler;
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000881 sendDataWithCallback(std::move(data), destIP, destPort, destUri, useSSL,
Carson Labradod14a48f2023-02-22 00:24:54 +0000882 httpHeader, verb, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000883 }
884
885 // Send request to destIP:destPort and use the provided callback to
886 // handle the response
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000887 void sendDataWithCallback(std::string&& data, const std::string& destIP,
Carson Labradod14a48f2023-02-22 00:24:54 +0000888 uint16_t destPort, const std::string& destUri,
889 bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000890 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000891 const boost::beast::http::verb verb,
Ed Tanous6b3db602022-06-28 19:41:44 -0700892 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000893 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000894 std::string clientKey = useSSL ? "https" : "http";
895 clientKey += destIP;
896 clientKey += ":";
897 clientKey += std::to_string(destPort);
Carson Labradod14a48f2023-02-22 00:24:54 +0000898 auto pool = connectionPools.try_emplace(clientKey);
899 if (pool.first->second == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000900 {
Carson Labradod14a48f2023-02-22 00:24:54 +0000901 pool.first->second = std::make_shared<ConnectionPool>(
902 ioc, clientKey, connPolicy, destIP, destPort, useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000903 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000904 // Send the data using either the existing connection pool or the newly
905 // created connection pool
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000906 pool.first->second->sendData(std::move(data), destUri, httpHeader, verb,
Carson Labradod14a48f2023-02-22 00:24:54 +0000907 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000908 }
909};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530910} // namespace crow