blob: dea4d50c6790c0b24c1527e418459b39f9d03ba7 [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
151 using Resolver = crow::async_resolve::Resolver;
152#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 {
183 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
184 state = ConnState::resolveFailed;
185 waitAndRetry();
186 return;
187 }
188 BMCWEB_LOG_DEBUG << "Resolved " << host << ":" << std::to_string(port)
189 << ", id: " << std::to_string(connId);
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530190 state = ConnState::connectInProgress;
191
Carson Labradof52c03c2022-03-23 18:50:15 +0000192 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
193 << std::to_string(port)
194 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530195
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800196 timer.expires_after(std::chrono::seconds(30));
197 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
198
199 boost::asio::async_connect(
200 conn, endpointList,
201 std::bind_front(&ConnectionInfo::afterConnect, this,
202 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000203 }
204
205 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
Ed Tanous81c4e332023-05-18 10:30:34 -0700206 const boost::beast::error_code& ec,
AppaRao Pulie38778a2022-06-27 23:09:03 +0000207 const boost::asio::ip::tcp::endpoint& endpoint)
208 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000209 // The operation already timed out. We don't want do continue down
210 // this branch
211 if (ec && ec == boost::asio::error::operation_aborted)
212 {
213 return;
214 }
215
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800216 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000217 if (ec)
218 {
219 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
220 << ":" << std::to_string(endpoint.port())
221 << ", id: " << std::to_string(connId)
222 << " failed: " << ec.message();
223 state = ConnState::connectFailed;
224 waitAndRetry();
225 return;
226 }
227 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
228 << ":" << std::to_string(endpoint.port())
229 << ", id: " << std::to_string(connId);
230 if (sslConn)
231 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800232 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000233 return;
234 }
235 state = ConnState::connected;
236 sendMessage();
237 }
238
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800239 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000240 {
241 if (!sslConn)
242 {
243 return;
244 }
245 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800246 timer.expires_after(std::chrono::seconds(30));
247 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000248 sslConn->async_handshake(
249 boost::asio::ssl::stream_base::client,
250 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
251 shared_from_this()));
252 }
253
254 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
Ed Tanous81c4e332023-05-18 10:30:34 -0700255 const boost::beast::error_code& ec)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000256 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000257 // The operation already timed out. We don't want do continue down
258 // this branch
259 if (ec && ec == boost::asio::error::operation_aborted)
260 {
261 return;
262 }
263
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800264 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000265 if (ec)
266 {
267 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
268 << " id: " << std::to_string(connId)
269 << " error: " << ec.message();
270 state = ConnState::handshakeFailed;
271 waitAndRetry();
272 return;
273 }
274 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
275 << " id: " << std::to_string(connId);
276 state = ConnState::connected;
277 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530278 }
279
Carson Labradof52c03c2022-03-23 18:50:15 +0000280 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530281 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530282 state = ConnState::sendInProgress;
283
AppaRao Pulibd030d02020-03-20 03:34:29 +0530284 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800285 timer.expires_after(std::chrono::seconds(30));
286 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530287
288 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000289 if (sslConn)
290 {
291 boost::beast::http::async_write(
292 *sslConn, req,
293 std::bind_front(&ConnectionInfo::afterWrite, this,
294 shared_from_this()));
295 }
296 else
297 {
298 boost::beast::http::async_write(
299 conn, req,
300 std::bind_front(&ConnectionInfo::afterWrite, this,
301 shared_from_this()));
302 }
303 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530304
AppaRao Pulie38778a2022-06-27 23:09:03 +0000305 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
306 const boost::beast::error_code& ec, size_t bytesTransferred)
307 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000308 // The operation already timed out. We don't want do continue down
309 // this branch
310 if (ec && ec == boost::asio::error::operation_aborted)
311 {
312 return;
313 }
314
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800315 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000316 if (ec)
317 {
318 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
319 state = ConnState::sendFailed;
320 waitAndRetry();
321 return;
322 }
323 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
324 << bytesTransferred;
325
326 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530327 }
328
329 void recvMessage()
330 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530331 state = ConnState::recvInProgress;
332
333 parser.emplace(std::piecewise_construct, std::make_tuple());
Carson Labradod14a48f2023-02-22 00:24:54 +0000334
335 parser->body_limit(connPolicy->requestByteLimit);
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530336
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800337 timer.expires_after(std::chrono::seconds(30));
338 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
339
AppaRao Pulibd030d02020-03-20 03:34:29 +0530340 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000341 if (sslConn)
342 {
343 boost::beast::http::async_read(
344 *sslConn, buffer, *parser,
345 std::bind_front(&ConnectionInfo::afterRead, this,
346 shared_from_this()));
347 }
348 else
349 {
350 boost::beast::http::async_read(
351 conn, buffer, *parser,
352 std::bind_front(&ConnectionInfo::afterRead, this,
353 shared_from_this()));
354 }
355 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530356
AppaRao Pulie38778a2022-06-27 23:09:03 +0000357 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
358 const boost::beast::error_code& ec,
359 const std::size_t& bytesTransferred)
360 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000361 // The operation already timed out. We don't want do continue down
362 // this branch
363 if (ec && ec == boost::asio::error::operation_aborted)
364 {
365 return;
366 }
367
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800368 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000369 if (ec && ec != boost::asio::ssl::error::stream_truncated)
370 {
371 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
372 state = ConnState::recvFailed;
373 waitAndRetry();
374 return;
375 }
376 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
377 << bytesTransferred;
378 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
379
380 unsigned int respCode = parser->get().result_int();
381 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
382
383 // Make sure the received response code is valid as defined by
384 // the associated retry policy
Carson Labradod14a48f2023-02-22 00:24:54 +0000385 if (connPolicy->invalidResp(respCode))
AppaRao Pulie38778a2022-06-27 23:09:03 +0000386 {
387 // The listener failed to receive the Sent-Event
388 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
389 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700390 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000391 state = ConnState::recvFailed;
392 waitAndRetry();
393 return;
394 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700395
AppaRao Pulie38778a2022-06-27 23:09:03 +0000396 // Send is successful
397 // Reset the counter just in case this was after retrying
398 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530399
AppaRao Pulie38778a2022-06-27 23:09:03 +0000400 // Keep the connection alive if server supports it
401 // Else close the connection
402 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
403 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530404
AppaRao Pulie38778a2022-06-27 23:09:03 +0000405 // Copy the response into a Response object so that it can be
406 // processed by the callback function.
AppaRao Pulie38778a2022-06-27 23:09:03 +0000407 res.stringResponse = parser->release();
408 callback(parser->keep_alive(), connId, res);
Carson Labrado513d1ff2022-07-19 00:38:15 +0000409 res.clear();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530410 }
411
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800412 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800413 const boost::system::error_code& ec)
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800414 {
415 if (ec == boost::asio::error::operation_aborted)
416 {
417 BMCWEB_LOG_DEBUG
Carson Labrado513d1ff2022-07-19 00:38:15 +0000418 << "async_wait failed since the operation is aborted";
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800419 return;
420 }
421 if (ec)
422 {
423 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
424 // If the timer fails, we need to close the socket anyway, same as
425 // if it expired.
426 }
427 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
428 if (self == nullptr)
429 {
430 return;
431 }
432 self->waitAndRetry();
433 }
434
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530435 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530436 {
Carson Labradod14a48f2023-02-22 00:24:54 +0000437 if ((retryCount >= connPolicy->maxRetryAttempts) ||
AppaRao Pulie38778a2022-06-27 23:09:03 +0000438 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530439 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530440 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000441 BMCWEB_LOG_DEBUG << "Retry policy: "
Carson Labradod14a48f2023-02-22 00:24:54 +0000442 << connPolicy->retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000443
Carson Labradod14a48f2023-02-22 00:24:54 +0000444 if (connPolicy->retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530445 {
446 // TODO: delete subscription
447 state = ConnState::terminated;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530448 }
Carson Labradod14a48f2023-02-22 00:24:54 +0000449 if (connPolicy->retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530450 {
451 state = ConnState::suspended;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530452 }
Carson Labrado513d1ff2022-07-19 00:38:15 +0000453
454 // We want to return a 502 to indicate there was an error with
455 // the external server
456 res.result(boost::beast::http::status::bad_gateway);
457 callback(false, connId, res);
458 res.clear();
459
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530460 // Reset the retrycount to zero so that client can try connecting
461 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700462 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530463 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530464 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530465
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530466 retryCount++;
467
Carson Labradof52c03c2022-03-23 18:50:15 +0000468 BMCWEB_LOG_DEBUG << "Attempt retry after "
469 << std::to_string(
Carson Labradod14a48f2023-02-22 00:24:54 +0000470 connPolicy->retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530471 << " seconds. RetryCount = " << retryCount;
Carson Labradod14a48f2023-02-22 00:24:54 +0000472 timer.expires_after(connPolicy->retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700473 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
474 shared_from_this()));
475 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530476
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700477 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
478 const boost::system::error_code& ec)
479 {
480 if (ec == boost::asio::error::operation_aborted)
481 {
482 BMCWEB_LOG_DEBUG
483 << "async_wait failed since the operation is aborted"
484 << ec.message();
485 }
486 else if (ec)
487 {
488 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
489 // Ignore the error and continue the retry loop to attempt
490 // sending the event as per the retry policy
491 }
492
493 // Let's close the connection and restart from resolve.
494 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530495 }
496
AppaRao Pulie38778a2022-06-27 23:09:03 +0000497 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530498 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000499 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800500 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000501 conn.close();
502
503 // not_connected happens sometimes so don't bother reporting it.
504 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530505 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000506 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
507 << ", id: " << std::to_string(connId)
Carson Labrado513d1ff2022-07-19 00:38:15 +0000508 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000509 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000510 else
511 {
512 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
513 << ", id: " << std::to_string(connId)
514 << " closed gracefully";
515 }
Ed Tanousca723762022-06-28 19:40:39 -0700516
Carson Labrado513d1ff2022-07-19 00:38:15 +0000517 if (retry)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000518 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000519 // Now let's try to resend the data
520 state = ConnState::retry;
521 doResolve();
522 }
523 else
524 {
525 state = ConnState::closed;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000526 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000527 }
528
AppaRao Pulie38778a2022-06-27 23:09:03 +0000529 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000530 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000531 if (!sslConn)
532 {
533 shutdownConn(retry);
534 return;
535 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000536
AppaRao Pulie38778a2022-06-27 23:09:03 +0000537 sslConn->async_shutdown(
538 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
539 shared_from_this(), retry));
540 }
541
542 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
543 bool retry, const boost::system::error_code& ec)
544 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000545 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000546 {
547 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
548 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000549 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000550 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000551 else
552 {
553 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
554 << ", id: " << std::to_string(connId)
555 << " closed gracefully";
556 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000557 shutdownConn(retry);
558 }
Ed Tanousca723762022-06-28 19:40:39 -0700559
AppaRao Pulie38778a2022-06-27 23:09:03 +0000560 void setCipherSuiteTLSext()
561 {
562 if (!sslConn)
563 {
564 return;
565 }
566 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
567 // file but its having old style casting (name is cast to void*).
568 // Since bmcweb compiler treats all old-style-cast as error, its
569 // causing the build failure. So replaced the same macro inline and
570 // did corrected the code by doing static_cast to viod*. This has to
571 // be fixed in openssl library in long run. Set SNI Hostname (many
572 // hosts need this to handshake successfully)
573 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
574 TLSEXT_NAMETYPE_host_name,
575 static_cast<void*>(&host.front())) == 0)
576
577 {
578 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
579 boost::asio::error::get_ssl_category()};
580
581 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
582 << port << ", id: " << std::to_string(connId)
583 << " failed: " << ec.message();
584 // Set state as sslInit failed so that we close the connection
585 // and take appropriate action as per retry configuration.
586 state = ConnState::sslInitFailed;
587 waitAndRetry();
588 return;
589 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530590 }
591
592 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000593 explicit ConnectionInfo(
594 boost::asio::io_context& iocIn, const std::string& idIn,
595 const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
596 const std::string& destIPIn, uint16_t destPortIn, bool useSSL,
597 unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700598 subId(idIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000599 connPolicy(connPolicyIn), host(destIPIn), port(destPortIn),
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700600 connId(connIdIn), resolver(iocIn), conn(iocIn), timer(iocIn)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000601 {
602 if (useSSL)
603 {
604 std::optional<boost::asio::ssl::context> sslCtx =
605 ensuressl::getSSLClientContext();
606
607 if (!sslCtx)
608 {
609 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
610 << port << ", id: " << std::to_string(connId);
611 // Don't retry if failure occurs while preparing SSL context
612 // such as certificate is invalid or set cipher failure or set
613 // host name failure etc... Setting conn state to sslInitFailed
614 // and connection state will be transitioned to next state
615 // depending on retry policy set by subscription.
616 state = ConnState::sslInitFailed;
617 waitAndRetry();
618 return;
619 }
620 sslConn.emplace(conn, *sslCtx);
621 setCipherSuiteTLSext();
622 }
623 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000624};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530625
Carson Labradof52c03c2022-03-23 18:50:15 +0000626class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
627{
628 private:
629 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000630 std::string id;
Carson Labradod14a48f2023-02-22 00:24:54 +0000631 std::shared_ptr<ConnectionPolicy> connPolicy;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000632 std::string destIP;
633 uint16_t destPort;
634 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000635 std::vector<std::shared_ptr<ConnectionInfo>> connections;
636 boost::container::devector<PendingRequest> requestQueue;
637
638 friend class HttpClient;
639
Carson Labrado244256c2022-04-27 17:16:32 +0000640 // Configure a connections's request, callback, and retry info in
641 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000642 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530643 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000644 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530645 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000646 BMCWEB_LOG_ERROR
647 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530648 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530649 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530650
Carson Labrado244256c2022-04-27 17:16:32 +0000651 auto nextReq = requestQueue.front();
Carson Labrado244256c2022-04-27 17:16:32 +0000652 conn.req = std::move(nextReq.req);
653 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000654
655 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
656 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000657 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000658
659 // We can remove the request from the queue at this point
660 requestQueue.pop_front();
661 }
662
Carson Labradof52c03c2022-03-23 18:50:15 +0000663 // 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
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000713 void sendData(std::string&& data, const std::string& destUri,
Carson Labrado244256c2022-04-27 17:16:32 +0000714 const boost::beast::http::fields& httpHeader,
715 const boost::beast::http::verb verb,
Ed Tanous6b3db602022-06-28 19:41:44 -0700716 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530717 {
Carson Labrado244256c2022-04-27 17:16:32 +0000718 // Construct the request to be sent
719 boost::beast::http::request<boost::beast::http::string_body> thisReq(
720 verb, destUri, 11, "", httpHeader);
721 thisReq.set(boost::beast::http::field::host, destIP);
722 thisReq.keep_alive(true);
723 thisReq.body() = std::move(data);
724 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700725 auto cb = std::bind_front(&ConnectionPool::afterSendData,
726 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000727 // Reuse an existing connection if one is available
728 for (unsigned int i = 0; i < connections.size(); i++)
729 {
730 auto conn = connections[i];
731 if ((conn->state == ConnState::idle) ||
732 (conn->state == ConnState::initialized) ||
733 (conn->state == ConnState::closed))
734 {
Carson Labrado244256c2022-04-27 17:16:32 +0000735 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000736 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000737 std::string commonMsg = std::to_string(i) + " from pool " +
738 destIP + ":" + std::to_string(destPort);
739
740 if (conn->state == ConnState::idle)
741 {
742 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
743 << commonMsg;
744 conn->sendMessage();
745 }
746 else
747 {
748 BMCWEB_LOG_DEBUG << "Reusing existing connection "
749 << commonMsg;
750 conn->doResolve();
751 }
752 return;
753 }
754 }
755
756 // All connections in use so create a new connection or add request to
757 // the queue
Carson Labradod14a48f2023-02-22 00:24:54 +0000758 if (connections.size() < connPolicy->maxConnections)
Carson Labradof52c03c2022-03-23 18:50:15 +0000759 {
760 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
761 << ":" << std::to_string(destPort);
762 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000763 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000764 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000765 conn->doResolve();
766 }
767 else if (requestQueue.size() < maxRequestQueueSize)
768 {
769 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labradod14a48f2023-02-22 00:24:54 +0000770 requestQueue.emplace_back(std::move(thisReq), std::move(cb));
Carson Labradof52c03c2022-03-23 18:50:15 +0000771 }
772 else
773 {
Carson Labrado43e14d32022-11-09 00:25:20 +0000774 // If we can't buffer the request then we should let the callback
775 // handle a 429 Too Many Requests dummy response
Carson Labradof52c03c2022-03-23 18:50:15 +0000776 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
777 << " request queue full. Dropping request.";
Carson Labrado43e14d32022-11-09 00:25:20 +0000778 Response dummyRes;
779 dummyRes.result(boost::beast::http::status::too_many_requests);
780 resHandler(dummyRes);
Carson Labradof52c03c2022-03-23 18:50:15 +0000781 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530782 }
783
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700784 // Callback to be called once the request has been sent
785 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
786 const std::function<void(Response&)>& resHandler,
787 bool keepAlive, uint32_t connId, Response& res)
788 {
789 // Allow provided callback to perform additional processing of the
790 // request
791 resHandler(res);
792
793 // If requests remain in the queue then we want to reuse this
794 // connection to send the next request
795 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
796 if (!self)
797 {
798 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
799 return;
800 }
801
802 self->sendNext(keepAlive, connId);
803 }
804
Carson Labradof52c03c2022-03-23 18:50:15 +0000805 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530806 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000807 unsigned int newId = static_cast<unsigned int>(connections.size());
808
AppaRao Pulie38778a2022-06-27 23:09:03 +0000809 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
Carson Labradod14a48f2023-02-22 00:24:54 +0000810 ioc, id, connPolicy, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000811
812 BMCWEB_LOG_DEBUG << "Added connection "
813 << std::to_string(connections.size() - 1)
814 << " to pool " << destIP << ":"
815 << std::to_string(destPort);
816
817 return ret;
818 }
819
820 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000821 explicit ConnectionPool(
822 boost::asio::io_context& iocIn, const std::string& idIn,
823 const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
824 const std::string& destIPIn, uint16_t destPortIn, bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700825 ioc(iocIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000826 id(idIn), connPolicy(connPolicyIn), destIP(destIPIn),
827 destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000828 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000829 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
830 << std::to_string(destPort);
831
832 // Initialize the pool with a single connection
833 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530834 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530835};
836
Carson Labradof52c03c2022-03-23 18:50:15 +0000837class HttpClient
838{
839 private:
840 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
841 connectionPools;
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700842 boost::asio::io_context& ioc;
Carson Labradod14a48f2023-02-22 00:24:54 +0000843 std::shared_ptr<ConnectionPolicy> connPolicy;
Carson Labradof52c03c2022-03-23 18:50:15 +0000844
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:
Carson Labradod14a48f2023-02-22 00:24:54 +0000854 HttpClient() = delete;
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700855 explicit HttpClient(boost::asio::io_context& iocIn,
856 const std::shared_ptr<ConnectionPolicy>& connPolicyIn) :
857 ioc(iocIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000858 connPolicy(connPolicyIn)
859 {}
Ed Tanousf8ca6d72022-06-28 12:12:03 -0700860
Carson Labradof52c03c2022-03-23 18:50:15 +0000861 HttpClient(const HttpClient&) = delete;
862 HttpClient& operator=(const HttpClient&) = delete;
863 HttpClient(HttpClient&&) = delete;
864 HttpClient& operator=(HttpClient&&) = delete;
865 ~HttpClient() = default;
866
Carson Labrado039a47e2022-04-05 16:03:20 +0000867 // Send a request to destIP:destPort where additional processing of the
868 // result is not required
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000869 void sendData(std::string&& data, const std::string& destIP,
Carson Labradod14a48f2023-02-22 00:24:54 +0000870 uint16_t destPort, const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000871 const boost::beast::http::fields& httpHeader,
Carson Labradod14a48f2023-02-22 00:24:54 +0000872 const boost::beast::http::verb verb)
Carson Labradof52c03c2022-03-23 18:50:15 +0000873 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000874 const std::function<void(Response&)> cb = genericResHandler;
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000875 sendDataWithCallback(std::move(data), destIP, destPort, destUri, useSSL,
Carson Labradod14a48f2023-02-22 00:24:54 +0000876 httpHeader, verb, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000877 }
878
879 // Send request to destIP:destPort and use the provided callback to
880 // handle the response
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000881 void sendDataWithCallback(std::string&& data, const std::string& destIP,
Carson Labradod14a48f2023-02-22 00:24:54 +0000882 uint16_t destPort, const std::string& destUri,
883 bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000884 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000885 const boost::beast::http::verb verb,
Ed Tanous6b3db602022-06-28 19:41:44 -0700886 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000887 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000888 std::string clientKey = useSSL ? "https" : "http";
889 clientKey += destIP;
890 clientKey += ":";
891 clientKey += std::to_string(destPort);
Carson Labradod14a48f2023-02-22 00:24:54 +0000892 auto pool = connectionPools.try_emplace(clientKey);
893 if (pool.first->second == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000894 {
Carson Labradod14a48f2023-02-22 00:24:54 +0000895 pool.first->second = std::make_shared<ConnectionPool>(
896 ioc, clientKey, connPolicy, destIP, destPort, useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000897 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000898 // Send the data using either the existing connection pool or the newly
899 // created connection pool
AppaRao Puli5e44e3d2021-03-16 15:37:24 +0000900 pool.first->second->sendData(std::move(data), destUri, httpHeader, verb,
Carson Labradod14a48f2023-02-22 00:24:54 +0000901 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000902 }
903};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530904} // namespace crow