blob: 8161eb1c5fb60a14fc24776bead975d3abc2bbaf [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;
Carson Labradof52c03c2022-03-23 18:50:15 +0000149 crow::async_resolve::Resolver resolver;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800150 boost::asio::ip::tcp::socket conn;
151 std::optional<boost::beast::ssl_stream<boost::asio::ip::tcp::socket&>>
152 sslConn;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000153
Carson Labradof52c03c2022-03-23 18:50:15 +0000154 boost::asio::steady_timer timer;
Ed Tanous84b35602021-09-08 20:06:32 -0700155
Carson Labradof52c03c2022-03-23 18:50:15 +0000156 friend class ConnectionPool;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530157
Sunitha Harish29a82b02021-02-18 15:54:16 +0530158 void doResolve()
159 {
Sunitha Harish29a82b02021-02-18 15:54:16 +0530160 state = ConnState::resolveInProgress;
Carson Labradof52c03c2022-03-23 18:50:15 +0000161 BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":"
162 << std::to_string(port)
163 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530164
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700165 resolver.asyncResolve(host, port,
166 std::bind_front(&ConnectionInfo::afterResolve,
167 this, shared_from_this()));
Sunitha Harish29a82b02021-02-18 15:54:16 +0530168 }
169
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700170 void afterResolve(
171 const std::shared_ptr<ConnectionInfo>& /*self*/,
172 const boost::beast::error_code ec,
Sunitha Harish29a82b02021-02-18 15:54:16 +0530173 const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530174 {
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700175 if (ec || (endpointList.empty()))
176 {
177 BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
178 state = ConnState::resolveFailed;
179 waitAndRetry();
180 return;
181 }
182 BMCWEB_LOG_DEBUG << "Resolved " << host << ":" << std::to_string(port)
183 << ", id: " << std::to_string(connId);
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530184 state = ConnState::connectInProgress;
185
Carson Labradof52c03c2022-03-23 18:50:15 +0000186 BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":"
187 << std::to_string(port)
188 << ", id: " << std::to_string(connId);
Sunitha Harish29a82b02021-02-18 15:54:16 +0530189
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800190 timer.expires_after(std::chrono::seconds(30));
191 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
192
193 boost::asio::async_connect(
194 conn, endpointList,
195 std::bind_front(&ConnectionInfo::afterConnect, this,
196 shared_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000197 }
198
199 void afterConnect(const std::shared_ptr<ConnectionInfo>& /*self*/,
200 boost::beast::error_code ec,
201 const boost::asio::ip::tcp::endpoint& endpoint)
202 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000203 // The operation already timed out. We don't want do continue down
204 // this branch
205 if (ec && ec == boost::asio::error::operation_aborted)
206 {
207 return;
208 }
209
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800210 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000211 if (ec)
212 {
213 BMCWEB_LOG_ERROR << "Connect " << endpoint.address().to_string()
214 << ":" << std::to_string(endpoint.port())
215 << ", id: " << std::to_string(connId)
216 << " failed: " << ec.message();
217 state = ConnState::connectFailed;
218 waitAndRetry();
219 return;
220 }
221 BMCWEB_LOG_DEBUG << "Connected to: " << endpoint.address().to_string()
222 << ":" << std::to_string(endpoint.port())
223 << ", id: " << std::to_string(connId);
224 if (sslConn)
225 {
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800226 doSslHandshake();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000227 return;
228 }
229 state = ConnState::connected;
230 sendMessage();
231 }
232
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800233 void doSslHandshake()
AppaRao Pulie38778a2022-06-27 23:09:03 +0000234 {
235 if (!sslConn)
236 {
237 return;
238 }
239 state = ConnState::handshakeInProgress;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800240 timer.expires_after(std::chrono::seconds(30));
241 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulie38778a2022-06-27 23:09:03 +0000242 sslConn->async_handshake(
243 boost::asio::ssl::stream_base::client,
244 std::bind_front(&ConnectionInfo::afterSslHandshake, this,
245 shared_from_this()));
246 }
247
248 void afterSslHandshake(const std::shared_ptr<ConnectionInfo>& /*self*/,
249 boost::beast::error_code ec)
250 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000251 // The operation already timed out. We don't want do continue down
252 // this branch
253 if (ec && ec == boost::asio::error::operation_aborted)
254 {
255 return;
256 }
257
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800258 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000259 if (ec)
260 {
261 BMCWEB_LOG_ERROR << "SSL Handshake failed -"
262 << " id: " << std::to_string(connId)
263 << " error: " << ec.message();
264 state = ConnState::handshakeFailed;
265 waitAndRetry();
266 return;
267 }
268 BMCWEB_LOG_DEBUG << "SSL Handshake successful -"
269 << " id: " << std::to_string(connId);
270 state = ConnState::connected;
271 sendMessage();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530272 }
273
Carson Labradof52c03c2022-03-23 18:50:15 +0000274 void sendMessage()
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530275 {
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530276 state = ConnState::sendInProgress;
277
AppaRao Pulibd030d02020-03-20 03:34:29 +0530278 // Set a timeout on the operation
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800279 timer.expires_after(std::chrono::seconds(30));
280 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
AppaRao Pulibd030d02020-03-20 03:34:29 +0530281
282 // Send the HTTP request to the remote host
AppaRao Pulie38778a2022-06-27 23:09:03 +0000283 if (sslConn)
284 {
285 boost::beast::http::async_write(
286 *sslConn, req,
287 std::bind_front(&ConnectionInfo::afterWrite, this,
288 shared_from_this()));
289 }
290 else
291 {
292 boost::beast::http::async_write(
293 conn, req,
294 std::bind_front(&ConnectionInfo::afterWrite, this,
295 shared_from_this()));
296 }
297 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530298
AppaRao Pulie38778a2022-06-27 23:09:03 +0000299 void afterWrite(const std::shared_ptr<ConnectionInfo>& /*self*/,
300 const boost::beast::error_code& ec, size_t bytesTransferred)
301 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000302 // The operation already timed out. We don't want do continue down
303 // this branch
304 if (ec && ec == boost::asio::error::operation_aborted)
305 {
306 return;
307 }
308
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800309 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000310 if (ec)
311 {
312 BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
313 state = ConnState::sendFailed;
314 waitAndRetry();
315 return;
316 }
317 BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
318 << bytesTransferred;
319
320 recvMessage();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530321 }
322
323 void recvMessage()
324 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530325 state = ConnState::recvInProgress;
326
327 parser.emplace(std::piecewise_construct, std::make_tuple());
Carson Labradod14a48f2023-02-22 00:24:54 +0000328
329 parser->body_limit(connPolicy->requestByteLimit);
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530330
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800331 timer.expires_after(std::chrono::seconds(30));
332 timer.async_wait(std::bind_front(onTimeout, weak_from_this()));
333
AppaRao Pulibd030d02020-03-20 03:34:29 +0530334 // Receive the HTTP response
AppaRao Pulie38778a2022-06-27 23:09:03 +0000335 if (sslConn)
336 {
337 boost::beast::http::async_read(
338 *sslConn, buffer, *parser,
339 std::bind_front(&ConnectionInfo::afterRead, this,
340 shared_from_this()));
341 }
342 else
343 {
344 boost::beast::http::async_read(
345 conn, buffer, *parser,
346 std::bind_front(&ConnectionInfo::afterRead, this,
347 shared_from_this()));
348 }
349 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530350
AppaRao Pulie38778a2022-06-27 23:09:03 +0000351 void afterRead(const std::shared_ptr<ConnectionInfo>& /*self*/,
352 const boost::beast::error_code& ec,
353 const std::size_t& bytesTransferred)
354 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000355 // The operation already timed out. We don't want do continue down
356 // this branch
357 if (ec && ec == boost::asio::error::operation_aborted)
358 {
359 return;
360 }
361
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800362 timer.cancel();
AppaRao Pulie38778a2022-06-27 23:09:03 +0000363 if (ec && ec != boost::asio::ssl::error::stream_truncated)
364 {
365 BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
366 state = ConnState::recvFailed;
367 waitAndRetry();
368 return;
369 }
370 BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
371 << bytesTransferred;
372 BMCWEB_LOG_DEBUG << "recvMessage() data: " << parser->get().body();
373
374 unsigned int respCode = parser->get().result_int();
375 BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " << respCode;
376
377 // Make sure the received response code is valid as defined by
378 // the associated retry policy
Carson Labradod14a48f2023-02-22 00:24:54 +0000379 if (connPolicy->invalidResp(respCode))
AppaRao Pulie38778a2022-06-27 23:09:03 +0000380 {
381 // The listener failed to receive the Sent-Event
382 BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
383 "receive Sent-Event. Header Response Code: "
Ed Tanous002d39b2022-05-31 08:59:27 -0700384 << respCode;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000385 state = ConnState::recvFailed;
386 waitAndRetry();
387 return;
388 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700389
AppaRao Pulie38778a2022-06-27 23:09:03 +0000390 // Send is successful
391 // Reset the counter just in case this was after retrying
392 retryCount = 0;
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530393
AppaRao Pulie38778a2022-06-27 23:09:03 +0000394 // Keep the connection alive if server supports it
395 // Else close the connection
396 BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
397 << parser->keep_alive();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530398
AppaRao Pulie38778a2022-06-27 23:09:03 +0000399 // Copy the response into a Response object so that it can be
400 // processed by the callback function.
AppaRao Pulie38778a2022-06-27 23:09:03 +0000401 res.stringResponse = parser->release();
402 callback(parser->keep_alive(), connId, res);
Carson Labrado513d1ff2022-07-19 00:38:15 +0000403 res.clear();
AppaRao Pulibd030d02020-03-20 03:34:29 +0530404 }
405
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800406 static void onTimeout(const std::weak_ptr<ConnectionInfo>& weakSelf,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800407 const boost::system::error_code& ec)
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800408 {
409 if (ec == boost::asio::error::operation_aborted)
410 {
411 BMCWEB_LOG_DEBUG
Carson Labrado513d1ff2022-07-19 00:38:15 +0000412 << "async_wait failed since the operation is aborted";
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800413 return;
414 }
415 if (ec)
416 {
417 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
418 // If the timer fails, we need to close the socket anyway, same as
419 // if it expired.
420 }
421 std::shared_ptr<ConnectionInfo> self = weakSelf.lock();
422 if (self == nullptr)
423 {
424 return;
425 }
426 self->waitAndRetry();
427 }
428
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530429 void waitAndRetry()
AppaRao Pulibd030d02020-03-20 03:34:29 +0530430 {
Carson Labradod14a48f2023-02-22 00:24:54 +0000431 if ((retryCount >= connPolicy->maxRetryAttempts) ||
AppaRao Pulie38778a2022-06-27 23:09:03 +0000432 (state == ConnState::sslInitFailed))
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530433 {
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530434 BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
Carson Labradof52c03c2022-03-23 18:50:15 +0000435 BMCWEB_LOG_DEBUG << "Retry policy: "
Carson Labradod14a48f2023-02-22 00:24:54 +0000436 << connPolicy->retryPolicyAction;
Carson Labrado039a47e2022-04-05 16:03:20 +0000437
Carson Labradod14a48f2023-02-22 00:24:54 +0000438 if (connPolicy->retryPolicyAction == "TerminateAfterRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530439 {
440 // TODO: delete subscription
441 state = ConnState::terminated;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530442 }
Carson Labradod14a48f2023-02-22 00:24:54 +0000443 if (connPolicy->retryPolicyAction == "SuspendRetries")
Ayushi Smritife44eb02020-05-15 15:24:45 +0530444 {
445 state = ConnState::suspended;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530446 }
Carson Labrado513d1ff2022-07-19 00:38:15 +0000447
448 // We want to return a 502 to indicate there was an error with
449 // the external server
450 res.result(boost::beast::http::status::bad_gateway);
451 callback(false, connId, res);
452 res.clear();
453
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530454 // Reset the retrycount to zero so that client can try connecting
455 // again if needed
Ed Tanous3174e4d2020-10-07 11:41:22 -0700456 retryCount = 0;
Ayushi Smritife44eb02020-05-15 15:24:45 +0530457 return;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530458 }
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530459
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530460 retryCount++;
461
Carson Labradof52c03c2022-03-23 18:50:15 +0000462 BMCWEB_LOG_DEBUG << "Attempt retry after "
463 << std::to_string(
Carson Labradod14a48f2023-02-22 00:24:54 +0000464 connPolicy->retryIntervalSecs.count())
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530465 << " seconds. RetryCount = " << retryCount;
Carson Labradod14a48f2023-02-22 00:24:54 +0000466 timer.expires_after(connPolicy->retryIntervalSecs);
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700467 timer.async_wait(std::bind_front(&ConnectionInfo::onTimerDone, this,
468 shared_from_this()));
469 }
Sunitha Harish6eaa1d22021-02-19 13:38:31 +0530470
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700471 void onTimerDone(const std::shared_ptr<ConnectionInfo>& /*self*/,
472 const boost::system::error_code& ec)
473 {
474 if (ec == boost::asio::error::operation_aborted)
475 {
476 BMCWEB_LOG_DEBUG
477 << "async_wait failed since the operation is aborted"
478 << ec.message();
479 }
480 else if (ec)
481 {
482 BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
483 // Ignore the error and continue the retry loop to attempt
484 // sending the event as per the retry policy
485 }
486
487 // Let's close the connection and restart from resolve.
488 doClose(true);
Ayushi Smritife44eb02020-05-15 15:24:45 +0530489 }
490
AppaRao Pulie38778a2022-06-27 23:09:03 +0000491 void shutdownConn(bool retry)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530492 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000493 boost::beast::error_code ec;
Ed Tanous0d5f5cf2022-03-12 15:30:55 -0800494 conn.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
Carson Labradof52c03c2022-03-23 18:50:15 +0000495 conn.close();
496
497 // not_connected happens sometimes so don't bother reporting it.
498 if (ec && ec != boost::beast::errc::not_connected)
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530499 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000500 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
501 << ", id: " << std::to_string(connId)
Carson Labrado513d1ff2022-07-19 00:38:15 +0000502 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000503 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000504 else
505 {
506 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
507 << ", id: " << std::to_string(connId)
508 << " closed gracefully";
509 }
Ed Tanousca723762022-06-28 19:40:39 -0700510
Carson Labrado513d1ff2022-07-19 00:38:15 +0000511 if (retry)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000512 {
Carson Labrado513d1ff2022-07-19 00:38:15 +0000513 // Now let's try to resend the data
514 state = ConnState::retry;
515 doResolve();
516 }
517 else
518 {
519 state = ConnState::closed;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000520 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000521 }
522
AppaRao Pulie38778a2022-06-27 23:09:03 +0000523 void doClose(bool retry = false)
Carson Labradof52c03c2022-03-23 18:50:15 +0000524 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000525 if (!sslConn)
526 {
527 shutdownConn(retry);
528 return;
529 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000530
AppaRao Pulie38778a2022-06-27 23:09:03 +0000531 sslConn->async_shutdown(
532 std::bind_front(&ConnectionInfo::afterSslShutdown, this,
533 shared_from_this(), retry));
534 }
535
536 void afterSslShutdown(const std::shared_ptr<ConnectionInfo>& /*self*/,
537 bool retry, const boost::system::error_code& ec)
538 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000539 if (ec)
Carson Labradof52c03c2022-03-23 18:50:15 +0000540 {
541 BMCWEB_LOG_ERROR << host << ":" << std::to_string(port)
542 << ", id: " << std::to_string(connId)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000543 << " shutdown failed: " << ec.message();
Carson Labradof52c03c2022-03-23 18:50:15 +0000544 }
Carson Labrado5cab68f2022-07-11 22:26:21 +0000545 else
546 {
547 BMCWEB_LOG_DEBUG << host << ":" << std::to_string(port)
548 << ", id: " << std::to_string(connId)
549 << " closed gracefully";
550 }
AppaRao Pulie38778a2022-06-27 23:09:03 +0000551 shutdownConn(retry);
552 }
Ed Tanousca723762022-06-28 19:40:39 -0700553
AppaRao Pulie38778a2022-06-27 23:09:03 +0000554 void setCipherSuiteTLSext()
555 {
556 if (!sslConn)
557 {
558 return;
559 }
560 // NOTE: The SSL_set_tlsext_host_name is defined in tlsv1.h header
561 // file but its having old style casting (name is cast to void*).
562 // Since bmcweb compiler treats all old-style-cast as error, its
563 // causing the build failure. So replaced the same macro inline and
564 // did corrected the code by doing static_cast to viod*. This has to
565 // be fixed in openssl library in long run. Set SNI Hostname (many
566 // hosts need this to handshake successfully)
567 if (SSL_ctrl(sslConn->native_handle(), SSL_CTRL_SET_TLSEXT_HOSTNAME,
568 TLSEXT_NAMETYPE_host_name,
569 static_cast<void*>(&host.front())) == 0)
570
571 {
572 boost::beast::error_code ec{static_cast<int>(::ERR_get_error()),
573 boost::asio::error::get_ssl_category()};
574
575 BMCWEB_LOG_ERROR << "SSL_set_tlsext_host_name " << host << ":"
576 << port << ", id: " << std::to_string(connId)
577 << " failed: " << ec.message();
578 // Set state as sslInit failed so that we close the connection
579 // and take appropriate action as per retry configuration.
580 state = ConnState::sslInitFailed;
581 waitAndRetry();
582 return;
583 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530584 }
585
586 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000587 explicit ConnectionInfo(
588 boost::asio::io_context& iocIn, const std::string& idIn,
589 const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
590 const std::string& destIPIn, uint16_t destPortIn, bool useSSL,
591 unsigned int connIdIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700592 subId(idIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000593 connPolicy(connPolicyIn), host(destIPIn), port(destPortIn),
594 connId(connIdIn), conn(iocIn), timer(iocIn)
AppaRao Pulie38778a2022-06-27 23:09:03 +0000595 {
596 if (useSSL)
597 {
598 std::optional<boost::asio::ssl::context> sslCtx =
599 ensuressl::getSSLClientContext();
600
601 if (!sslCtx)
602 {
603 BMCWEB_LOG_ERROR << "prepareSSLContext failed - " << host << ":"
604 << port << ", id: " << std::to_string(connId);
605 // Don't retry if failure occurs while preparing SSL context
606 // such as certificate is invalid or set cipher failure or set
607 // host name failure etc... Setting conn state to sslInitFailed
608 // and connection state will be transitioned to next state
609 // depending on retry policy set by subscription.
610 state = ConnState::sslInitFailed;
611 waitAndRetry();
612 return;
613 }
614 sslConn.emplace(conn, *sslCtx);
615 setCipherSuiteTLSext();
616 }
617 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000618};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530619
Carson Labradof52c03c2022-03-23 18:50:15 +0000620class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
621{
622 private:
623 boost::asio::io_context& ioc;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000624 std::string id;
Carson Labradod14a48f2023-02-22 00:24:54 +0000625 std::shared_ptr<ConnectionPolicy> connPolicy;
AppaRao Pulie38778a2022-06-27 23:09:03 +0000626 std::string destIP;
627 uint16_t destPort;
628 bool useSSL;
Carson Labradof52c03c2022-03-23 18:50:15 +0000629 std::vector<std::shared_ptr<ConnectionInfo>> connections;
630 boost::container::devector<PendingRequest> requestQueue;
631
632 friend class HttpClient;
633
Carson Labrado244256c2022-04-27 17:16:32 +0000634 // Configure a connections's request, callback, and retry info in
635 // preparation to begin sending the request
Carson Labradof52c03c2022-03-23 18:50:15 +0000636 void setConnProps(ConnectionInfo& conn)
AppaRao Pulibd030d02020-03-20 03:34:29 +0530637 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000638 if (requestQueue.empty())
AppaRao Pulibd030d02020-03-20 03:34:29 +0530639 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000640 BMCWEB_LOG_ERROR
641 << "setConnProps() should not have been called when requestQueue is empty";
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530642 return;
AppaRao Pulibd030d02020-03-20 03:34:29 +0530643 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530644
Carson Labrado244256c2022-04-27 17:16:32 +0000645 auto nextReq = requestQueue.front();
Carson Labrado244256c2022-04-27 17:16:32 +0000646 conn.req = std::move(nextReq.req);
647 conn.callback = std::move(nextReq.callback);
Carson Labradof52c03c2022-03-23 18:50:15 +0000648
649 BMCWEB_LOG_DEBUG << "Setting properties for connection " << conn.host
650 << ":" << std::to_string(conn.port)
Carson Labradoa7a80292022-06-01 16:01:52 +0000651 << ", id: " << std::to_string(conn.connId);
Carson Labradof52c03c2022-03-23 18:50:15 +0000652
653 // We can remove the request from the queue at this point
654 requestQueue.pop_front();
655 }
656
Carson Labradof52c03c2022-03-23 18:50:15 +0000657 // Gets called as part of callback after request is sent
658 // Reuses the connection if there are any requests waiting to be sent
659 // Otherwise closes the connection if it is not a keep-alive
660 void sendNext(bool keepAlive, uint32_t connId)
661 {
662 auto conn = connections[connId];
Carson Labrado46a81462022-04-27 21:11:37 +0000663
664 // Allow the connection's handler to be deleted
665 // This is needed because of Redfish Aggregation passing an
666 // AsyncResponse shared_ptr to this callback
667 conn->callback = nullptr;
668
Carson Labradof52c03c2022-03-23 18:50:15 +0000669 // Reuse the connection to send the next request in the queue
670 if (!requestQueue.empty())
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530671 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000672 BMCWEB_LOG_DEBUG << std::to_string(requestQueue.size())
673 << " requests remaining in queue for " << destIP
674 << ":" << std::to_string(destPort)
675 << ", reusing connnection "
676 << std::to_string(connId);
677
678 setConnProps(*conn);
679
680 if (keepAlive)
681 {
682 conn->sendMessage();
683 }
684 else
685 {
686 // Server is not keep-alive enabled so we need to close the
687 // connection and then start over from resolve
688 conn->doClose();
689 conn->doResolve();
690 }
691 return;
692 }
693
694 // No more messages to send so close the connection if necessary
695 if (keepAlive)
696 {
697 conn->state = ConnState::idle;
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530698 }
699 else
700 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000701 // Abort the connection since server is not keep-alive enabled
702 conn->state = ConnState::abortConnection;
703 conn->doClose();
AppaRao Puli2a5689a2020-04-29 15:24:31 +0530704 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530705 }
706
Carson Labrado244256c2022-04-27 17:16:32 +0000707 void sendData(std::string& data, const std::string& destUri,
708 const boost::beast::http::fields& httpHeader,
709 const boost::beast::http::verb verb,
Ed Tanous6b3db602022-06-28 19:41:44 -0700710 const std::function<void(Response&)>& resHandler)
Ayushi Smritife44eb02020-05-15 15:24:45 +0530711 {
Carson Labrado244256c2022-04-27 17:16:32 +0000712 // Construct the request to be sent
713 boost::beast::http::request<boost::beast::http::string_body> thisReq(
714 verb, destUri, 11, "", httpHeader);
715 thisReq.set(boost::beast::http::field::host, destIP);
716 thisReq.keep_alive(true);
717 thisReq.body() = std::move(data);
718 thisReq.prepare_payload();
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700719 auto cb = std::bind_front(&ConnectionPool::afterSendData,
720 weak_from_this(), resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000721 // Reuse an existing connection if one is available
722 for (unsigned int i = 0; i < connections.size(); i++)
723 {
724 auto conn = connections[i];
725 if ((conn->state == ConnState::idle) ||
726 (conn->state == ConnState::initialized) ||
727 (conn->state == ConnState::closed))
728 {
Carson Labrado244256c2022-04-27 17:16:32 +0000729 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000730 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000731 std::string commonMsg = std::to_string(i) + " from pool " +
732 destIP + ":" + std::to_string(destPort);
733
734 if (conn->state == ConnState::idle)
735 {
736 BMCWEB_LOG_DEBUG << "Grabbing idle connection "
737 << commonMsg;
738 conn->sendMessage();
739 }
740 else
741 {
742 BMCWEB_LOG_DEBUG << "Reusing existing connection "
743 << commonMsg;
744 conn->doResolve();
745 }
746 return;
747 }
748 }
749
750 // All connections in use so create a new connection or add request to
751 // the queue
Carson Labradod14a48f2023-02-22 00:24:54 +0000752 if (connections.size() < connPolicy->maxConnections)
Carson Labradof52c03c2022-03-23 18:50:15 +0000753 {
754 BMCWEB_LOG_DEBUG << "Adding new connection to pool " << destIP
755 << ":" << std::to_string(destPort);
756 auto conn = addConnection();
Carson Labrado244256c2022-04-27 17:16:32 +0000757 conn->req = std::move(thisReq);
Carson Labradof52c03c2022-03-23 18:50:15 +0000758 conn->callback = std::move(cb);
Carson Labradof52c03c2022-03-23 18:50:15 +0000759 conn->doResolve();
760 }
761 else if (requestQueue.size() < maxRequestQueueSize)
762 {
763 BMCWEB_LOG_ERROR << "Max pool size reached. Adding data to queue.";
Carson Labradod14a48f2023-02-22 00:24:54 +0000764 requestQueue.emplace_back(std::move(thisReq), std::move(cb));
Carson Labradof52c03c2022-03-23 18:50:15 +0000765 }
766 else
767 {
Carson Labrado43e14d32022-11-09 00:25:20 +0000768 // If we can't buffer the request then we should let the callback
769 // handle a 429 Too Many Requests dummy response
Carson Labradof52c03c2022-03-23 18:50:15 +0000770 BMCWEB_LOG_ERROR << destIP << ":" << std::to_string(destPort)
771 << " request queue full. Dropping request.";
Carson Labrado43e14d32022-11-09 00:25:20 +0000772 Response dummyRes;
773 dummyRes.result(boost::beast::http::status::too_many_requests);
774 resHandler(dummyRes);
Carson Labradof52c03c2022-03-23 18:50:15 +0000775 }
Ayushi Smritife44eb02020-05-15 15:24:45 +0530776 }
777
Ed Tanous3d36e3a2022-08-19 15:54:04 -0700778 // Callback to be called once the request has been sent
779 static void afterSendData(const std::weak_ptr<ConnectionPool>& weakSelf,
780 const std::function<void(Response&)>& resHandler,
781 bool keepAlive, uint32_t connId, Response& res)
782 {
783 // Allow provided callback to perform additional processing of the
784 // request
785 resHandler(res);
786
787 // If requests remain in the queue then we want to reuse this
788 // connection to send the next request
789 std::shared_ptr<ConnectionPool> self = weakSelf.lock();
790 if (!self)
791 {
792 BMCWEB_LOG_CRITICAL << self << " Failed to capture connection";
793 return;
794 }
795
796 self->sendNext(keepAlive, connId);
797 }
798
Carson Labradof52c03c2022-03-23 18:50:15 +0000799 std::shared_ptr<ConnectionInfo>& addConnection()
Ayushi Smritife44eb02020-05-15 15:24:45 +0530800 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000801 unsigned int newId = static_cast<unsigned int>(connections.size());
802
AppaRao Pulie38778a2022-06-27 23:09:03 +0000803 auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
Carson Labradod14a48f2023-02-22 00:24:54 +0000804 ioc, id, connPolicy, destIP, destPort, useSSL, newId));
Carson Labradof52c03c2022-03-23 18:50:15 +0000805
806 BMCWEB_LOG_DEBUG << "Added connection "
807 << std::to_string(connections.size() - 1)
808 << " to pool " << destIP << ":"
809 << std::to_string(destPort);
810
811 return ret;
812 }
813
814 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000815 explicit ConnectionPool(
816 boost::asio::io_context& iocIn, const std::string& idIn,
817 const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
818 const std::string& destIPIn, uint16_t destPortIn, bool useSSLIn) :
Ed Tanous8a592812022-06-04 09:06:59 -0700819 ioc(iocIn),
Carson Labradod14a48f2023-02-22 00:24:54 +0000820 id(idIn), connPolicy(connPolicyIn), destIP(destIPIn),
821 destPort(destPortIn), useSSL(useSSLIn)
Carson Labradof52c03c2022-03-23 18:50:15 +0000822 {
Carson Labradof52c03c2022-03-23 18:50:15 +0000823 BMCWEB_LOG_DEBUG << "Initializing connection pool for " << destIP << ":"
824 << std::to_string(destPort);
825
826 // Initialize the pool with a single connection
827 addConnection();
Ayushi Smritife44eb02020-05-15 15:24:45 +0530828 }
AppaRao Pulibd030d02020-03-20 03:34:29 +0530829};
830
Carson Labradof52c03c2022-03-23 18:50:15 +0000831class HttpClient
832{
833 private:
834 std::unordered_map<std::string, std::shared_ptr<ConnectionPool>>
835 connectionPools;
836 boost::asio::io_context& ioc =
837 crow::connections::systemBus->get_io_context();
Carson Labradod14a48f2023-02-22 00:24:54 +0000838 std::shared_ptr<ConnectionPolicy> connPolicy;
Carson Labradof52c03c2022-03-23 18:50:15 +0000839
Carson Labrado039a47e2022-04-05 16:03:20 +0000840 // Used as a dummy callback by sendData() in order to call
841 // sendDataWithCallback()
Ed Tanous02cad962022-06-30 16:50:15 -0700842 static void genericResHandler(const Response& res)
Carson Labrado039a47e2022-04-05 16:03:20 +0000843 {
844 BMCWEB_LOG_DEBUG << "Response handled with return code: "
845 << std::to_string(res.resultInt());
Ed Tanous4ee8e212022-05-28 09:42:51 -0700846 }
Carson Labrado039a47e2022-04-05 16:03:20 +0000847
Carson Labradof52c03c2022-03-23 18:50:15 +0000848 public:
Carson Labradod14a48f2023-02-22 00:24:54 +0000849 HttpClient() = delete;
850 explicit HttpClient(const std::shared_ptr<ConnectionPolicy>& connPolicyIn) :
851 connPolicy(connPolicyIn)
852 {}
Carson Labradof52c03c2022-03-23 18:50:15 +0000853 HttpClient(const HttpClient&) = delete;
854 HttpClient& operator=(const HttpClient&) = delete;
855 HttpClient(HttpClient&&) = delete;
856 HttpClient& operator=(HttpClient&&) = delete;
857 ~HttpClient() = default;
858
Carson Labrado039a47e2022-04-05 16:03:20 +0000859 // Send a request to destIP:destPort where additional processing of the
860 // result is not required
Carson Labradod14a48f2023-02-22 00:24:54 +0000861 void sendData(std::string& data, const std::string& destIP,
862 uint16_t destPort, const std::string& destUri, bool useSSL,
Carson Labradof52c03c2022-03-23 18:50:15 +0000863 const boost::beast::http::fields& httpHeader,
Carson Labradod14a48f2023-02-22 00:24:54 +0000864 const boost::beast::http::verb verb)
Carson Labradof52c03c2022-03-23 18:50:15 +0000865 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000866 const std::function<void(Response&)> cb = genericResHandler;
Carson Labradod14a48f2023-02-22 00:24:54 +0000867 sendDataWithCallback(data, destIP, destPort, destUri, useSSL,
868 httpHeader, verb, cb);
Carson Labrado039a47e2022-04-05 16:03:20 +0000869 }
870
871 // Send request to destIP:destPort and use the provided callback to
872 // handle the response
Carson Labradod14a48f2023-02-22 00:24:54 +0000873 void sendDataWithCallback(std::string& data, const std::string& destIP,
874 uint16_t destPort, const std::string& destUri,
875 bool useSSL,
Carson Labrado039a47e2022-04-05 16:03:20 +0000876 const boost::beast::http::fields& httpHeader,
Carson Labrado244256c2022-04-27 17:16:32 +0000877 const boost::beast::http::verb verb,
Ed Tanous6b3db602022-06-28 19:41:44 -0700878 const std::function<void(Response&)>& resHandler)
Carson Labrado039a47e2022-04-05 16:03:20 +0000879 {
AppaRao Pulie38778a2022-06-27 23:09:03 +0000880 std::string clientKey = useSSL ? "https" : "http";
881 clientKey += destIP;
882 clientKey += ":";
883 clientKey += std::to_string(destPort);
Carson Labradod14a48f2023-02-22 00:24:54 +0000884 auto pool = connectionPools.try_emplace(clientKey);
885 if (pool.first->second == nullptr)
Carson Labradof52c03c2022-03-23 18:50:15 +0000886 {
Carson Labradod14a48f2023-02-22 00:24:54 +0000887 pool.first->second = std::make_shared<ConnectionPool>(
888 ioc, clientKey, connPolicy, destIP, destPort, useSSL);
Carson Labradof52c03c2022-03-23 18:50:15 +0000889 }
Carson Labradof52c03c2022-03-23 18:50:15 +0000890 // Send the data using either the existing connection pool or the newly
891 // created connection pool
Carson Labradod14a48f2023-02-22 00:24:54 +0000892 pool.first->second->sendData(data, destUri, httpHeader, verb,
893 resHandler);
Carson Labradof52c03c2022-03-23 18:50:15 +0000894 }
895};
AppaRao Pulibd030d02020-03-20 03:34:29 +0530896} // namespace crow