Reuse connection pool depending on host and connection options (#1189)
* Reuse connection pool depending on host and connection options
This commit is contained in:
parent
5525d68a15
commit
1502468dba
@ -18,6 +18,7 @@
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed the Curl transport adapter connection pooling when setting options.
|
||||
- Fixed for setting up the default transport adapter.
|
||||
|
||||
## 1.0.0-beta.3 (2020-11-11)
|
||||
|
||||
@ -1006,11 +1006,52 @@ std::map<std::string, std::list<std::unique_ptr<CurlNetworkConnection>>>
|
||||
int32_t CurlConnectionPool::s_connectionCounter = 0;
|
||||
bool CurlConnectionPool::s_isCleanConnectionsRunning = false;
|
||||
|
||||
namespace {
|
||||
inline std::string GetConnectionKey(std::string const& host, CurlTransportOptions const& options)
|
||||
{
|
||||
std::string key(host);
|
||||
if (!options.CAInfo.empty())
|
||||
{
|
||||
key.append(options.CAInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
key.append("0");
|
||||
}
|
||||
if (!options.Proxy.empty())
|
||||
{
|
||||
key.append(options.Proxy);
|
||||
}
|
||||
else
|
||||
{
|
||||
key.append("0");
|
||||
}
|
||||
if (options.SSLOptions.NoRevoke)
|
||||
{
|
||||
key.append("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
key.append("0");
|
||||
}
|
||||
if (options.SSLVerifyPeer)
|
||||
{
|
||||
key.append("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
key.append("0");
|
||||
}
|
||||
return key;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<CurlNetworkConnection> CurlConnectionPool::GetCurlConnection(
|
||||
Request& request,
|
||||
CurlTransportOptions const& options)
|
||||
{
|
||||
std::string const& host = request.GetUrl().GetHost();
|
||||
std::string const connectionKey = GetConnectionKey(host, options);
|
||||
|
||||
{
|
||||
// Critical section. Needs to own ConnectionPoolMutex before executing
|
||||
@ -1018,7 +1059,7 @@ std::unique_ptr<CurlNetworkConnection> CurlConnectionPool::GetCurlConnection(
|
||||
std::lock_guard<std::mutex> lock(CurlConnectionPool::ConnectionPoolMutex);
|
||||
|
||||
// get a ref to the pool from the map of pools
|
||||
auto hostPoolIndex = CurlConnectionPool::ConnectionPoolIndex.find(host);
|
||||
auto hostPoolIndex = CurlConnectionPool::ConnectionPoolIndex.find(connectionKey);
|
||||
if (hostPoolIndex != CurlConnectionPool::ConnectionPoolIndex.end()
|
||||
&& hostPoolIndex->second.size() > 0)
|
||||
{
|
||||
@ -1129,7 +1170,7 @@ std::unique_ptr<CurlNetworkConnection> CurlConnectionPool::GetCurlConnection(
|
||||
+ std::string(curl_easy_strerror(performResult)));
|
||||
}
|
||||
|
||||
return std::make_unique<CurlConnection>(newHandle, host);
|
||||
return std::make_unique<CurlConnection>(newHandle, std::move(connectionKey));
|
||||
}
|
||||
|
||||
// Move the connection back to the connection pool. Push it to the front so it becomes the first
|
||||
@ -1148,7 +1189,8 @@ void CurlConnectionPool::MoveConnectionBackToPool(
|
||||
|
||||
// Lock mutex to access connection pool. mutex is unlock as soon as lock is out of scope
|
||||
std::lock_guard<std::mutex> lock(CurlConnectionPool::ConnectionPoolMutex);
|
||||
auto& hostPool = CurlConnectionPool::ConnectionPoolIndex[connection->GetHost()];
|
||||
auto& poolId = connection->GetConnectionKey();
|
||||
auto& hostPool = CurlConnectionPool::ConnectionPoolIndex[poolId];
|
||||
// update the time when connection was moved back to pool
|
||||
connection->updateLastUsageTime();
|
||||
hostPool.push_front(std::move(connection));
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
#if defined(TESTING_BUILD)
|
||||
// Define the class name that reads from ConnectionPool private members
|
||||
namespace Azure { namespace Core { namespace Test {
|
||||
class TransportAdapter_connectionPoolTest_Test;
|
||||
class CurlConnectionPool_connectionPoolTest_Test;
|
||||
}}} // namespace Azure::Core::Test
|
||||
#endif
|
||||
|
||||
@ -38,7 +38,7 @@ namespace Azure { namespace Core { namespace Http {
|
||||
class CurlConnectionPool {
|
||||
#if defined(TESTING_BUILD)
|
||||
// Give access to private to this tests class
|
||||
friend class Azure::Core::Test::TransportAdapter_connectionPoolTest_Test;
|
||||
friend class Azure::Core::Test::CurlConnectionPool_connectionPoolTest_Test;
|
||||
#endif
|
||||
public:
|
||||
/**
|
||||
|
||||
@ -48,9 +48,10 @@ namespace Azure { namespace Core { namespace Http {
|
||||
virtual ~CurlNetworkConnection() = default;
|
||||
|
||||
/**
|
||||
* @brief Get HTTP connection host.
|
||||
* @brief Get the Connection Properties Key object
|
||||
*
|
||||
*/
|
||||
virtual std::string const& GetHost() const = 0;
|
||||
virtual std::string const& GetConnectionKey() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Update last usage time for the connection.
|
||||
@ -85,8 +86,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
private:
|
||||
CURL* m_handle;
|
||||
curl_socket_t m_curlSocket;
|
||||
std::string m_host;
|
||||
std::chrono::steady_clock::time_point m_lastUseTime;
|
||||
std::string m_connectionKey;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -94,7 +95,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
*
|
||||
* @param host HTTP connection host name.
|
||||
*/
|
||||
CurlConnection(CURL* handle, std::string const& host) : m_handle(handle), m_host(host)
|
||||
CurlConnection(CURL* handle, std::string connectionPropertiesKey)
|
||||
: m_handle(handle), m_connectionKey(std::move(connectionPropertiesKey))
|
||||
{
|
||||
// Get the socket that libcurl is using from handle. Will use this to wait while
|
||||
// reading/writing
|
||||
@ -103,7 +105,7 @@ namespace Azure { namespace Core { namespace Http {
|
||||
if (result != CURLE_OK)
|
||||
{
|
||||
throw Http::TransportException(
|
||||
Details::c_DefaultFailedToGetNewConnectionTemplate + m_host + ". "
|
||||
"Broken connection. Couldn't get the active sockect for it."
|
||||
+ std::string(curl_easy_strerror(result)));
|
||||
}
|
||||
}
|
||||
@ -114,11 +116,7 @@ namespace Azure { namespace Core { namespace Http {
|
||||
*/
|
||||
~CurlConnection() override { curl_easy_cleanup(this->m_handle); }
|
||||
|
||||
/**
|
||||
* @brief Get HTTP connection host.
|
||||
* @return HTTP connection host name.
|
||||
*/
|
||||
std::string const& GetHost() const override { return this->m_host; }
|
||||
std::string const& GetConnectionKey() const override { return this->m_connectionKey; }
|
||||
|
||||
/**
|
||||
* @brief Update last usage time for the connection.
|
||||
|
||||
@ -19,6 +19,13 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#ifdef TESTING_BUILD
|
||||
// Define the class name that reads from ConnectionPool private members
|
||||
namespace Azure { namespace Core { namespace Test {
|
||||
class CurlConnectionPool_connectionPoolTest_Test;
|
||||
}}} // namespace Azure::Core::Test
|
||||
#endif
|
||||
|
||||
namespace Azure { namespace Core { namespace Http {
|
||||
|
||||
/**
|
||||
@ -33,6 +40,10 @@ namespace Azure { namespace Core { namespace Http {
|
||||
* transporter to be reusable in multiple pipelines while every call to network is unique.
|
||||
*/
|
||||
class CurlSession : public BodyStream {
|
||||
#ifdef TESTING_BUILD
|
||||
// Give access to private to this tests class
|
||||
friend class Azure::Core::Test::CurlConnectionPool_connectionPoolTest_Test;
|
||||
#endif
|
||||
private:
|
||||
/**
|
||||
* @brief This is used to set the current state of a session.
|
||||
|
||||
@ -22,7 +22,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
if(BUILD_TRANSPORT_CURL)
|
||||
SET(CURL_OPTIONS_TESTS curl_options.cpp)
|
||||
SET(CURL_SESSION_TESTS curl_session_test.cpp)
|
||||
SET(CURL_TRANSPORT_TESTS transport_adapter_curl.cpp)
|
||||
SET(CURL_CONNECTION_POOL_TESTS curl_connection_pool.cpp)
|
||||
endif()
|
||||
|
||||
include(GoogleTest)
|
||||
@ -30,6 +30,7 @@ include(GoogleTest)
|
||||
add_executable (
|
||||
azure-core-test
|
||||
context.cpp
|
||||
${CURL_CONNECTION_POOL_TESTS}
|
||||
${CURL_OPTIONS_TESTS}
|
||||
${CURL_SESSION_TESTS}
|
||||
datetime.cpp
|
||||
@ -46,7 +47,6 @@ add_executable (
|
||||
string.cpp
|
||||
telemetry_policy.cpp
|
||||
transport_adapter_base.cpp
|
||||
${CURL_TRANSPORT_TESTS}
|
||||
transport_adapter_implementation.cpp
|
||||
url.cpp
|
||||
uuid.cpp
|
||||
|
||||
172
sdk/core/azure-core/test/ut/curl_connection_pool.cpp
Normal file
172
sdk/core/azure-core/test/ut/curl_connection_pool.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "transport_adapter_base.hpp"
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/http/policy.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// The next includes are from Azure Core private headers.
|
||||
// That's why the path starts from `sdk/core/azure-core/src/`
|
||||
// They are included to test the connection pool from the curl transport adapter implementation.
|
||||
#include <http/curl/curl_connection_pool_private.hpp>
|
||||
#include <http/curl/curl_connection_private.hpp>
|
||||
#include <http/curl/curl_session_private.hpp>
|
||||
|
||||
using testing::ValuesIn;
|
||||
|
||||
namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
/*********************** Unique Tests for Libcurl ********************************/
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
|
||||
TEST(CurlConnectionPool, connectionPoolTest)
|
||||
{
|
||||
Azure::Core::Http::CurlConnectionPool::ClearIndex();
|
||||
// Make sure there are nothing in the pool
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
|
||||
// Use the same request for all connections.
|
||||
Azure::Core::Http::Request req(
|
||||
Azure::Core::Http::HttpMethod::Get, Azure::Core::Http::Url("http://httpbin.org/get"));
|
||||
std::string const expectedConnectionKey = "httpbin.org0011";
|
||||
|
||||
{
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection = Azure::Core::Http::CurlConnectionPool::GetCurlConnection(req, options);
|
||||
EXPECT_EQ(connection->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
auto session = std::make_unique<Azure::Core::Http::CurlSession>(
|
||||
req, std::move(connection), options.HttpKeepAlive);
|
||||
// Simulate connection was used already
|
||||
session->m_lastStatusCode = Azure::Core::Http::HttpStatusCode::Ok;
|
||||
session->m_sessionState = Azure::Core::Http::CurlSession::SessionState::STREAMING;
|
||||
}
|
||||
// Check that after the connection is gone, it is moved back to the pool
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
auto connectionFromPool = Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
->second.begin()
|
||||
->get();
|
||||
EXPECT_EQ(connectionFromPool->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
// Test that asking a connection with same config will re-use the same connection
|
||||
{
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection = Azure::Core::Http::CurlConnectionPool::GetCurlConnection(req, options);
|
||||
// There was just one connection in the pool, it should be empty now
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
// And the connection key for the connection we got is the expected
|
||||
EXPECT_EQ(connection->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
auto session = std::make_unique<Azure::Core::Http::CurlSession>(
|
||||
req, std::move(connection), options.HttpKeepAlive);
|
||||
// Simulate connection was used already
|
||||
session->m_lastStatusCode = Azure::Core::Http::HttpStatusCode::Ok;
|
||||
session->m_sessionState = Azure::Core::Http::CurlSession::SessionState::STREAMING;
|
||||
}
|
||||
// Check that after the connection is gone, it is moved back to the pool
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
auto values = Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
// Now test that using a different connection config won't re-use the same connection
|
||||
std::string const CAinfo = "someFakePath";
|
||||
std::string const secondExpectedKey = "httpbin.org" + CAinfo + "011";
|
||||
{
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
options.CAInfo = CAinfo;
|
||||
auto connection = Azure::Core::Http::CurlConnectionPool::GetCurlConnection(req, options);
|
||||
EXPECT_EQ(connection->GetConnectionKey(), secondExpectedKey);
|
||||
// One connection still in the pool after getting a new connection and with first expected
|
||||
// key
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
EXPECT_EQ(
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
->second.begin()
|
||||
->get()
|
||||
->GetConnectionKey(),
|
||||
expectedConnectionKey);
|
||||
|
||||
auto session = std::make_unique<Azure::Core::Http::CurlSession>(
|
||||
req, std::move(connection), options.HttpKeepAlive);
|
||||
// Simulate connection was used already
|
||||
session->m_lastStatusCode = Azure::Core::Http::HttpStatusCode::Ok;
|
||||
session->m_sessionState = Azure::Core::Http::CurlSession::SessionState::STREAMING;
|
||||
}
|
||||
|
||||
// Now there should be 2 index wit one connection each
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 2);
|
||||
values = Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), expectedConnectionKey);
|
||||
values++;
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), secondExpectedKey);
|
||||
|
||||
// Test re-using same custom config
|
||||
{
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
options.CAInfo = CAinfo;
|
||||
auto connection = Azure::Core::Http::CurlConnectionPool::GetCurlConnection(req, options);
|
||||
EXPECT_EQ(connection->GetConnectionKey(), secondExpectedKey);
|
||||
// One connection still in the pool after getting a new connection and with first expected
|
||||
// key
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
EXPECT_EQ(
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
->second.begin()
|
||||
->get()
|
||||
->GetConnectionKey(),
|
||||
expectedConnectionKey);
|
||||
|
||||
auto session = std::make_unique<Azure::Core::Http::CurlSession>(
|
||||
req, std::move(connection), options.HttpKeepAlive);
|
||||
// Simulate connection was used already
|
||||
session->m_lastStatusCode = Azure::Core::Http::HttpStatusCode::Ok;
|
||||
session->m_sessionState = Azure::Core::Http::CurlSession::SessionState::STREAMING;
|
||||
}
|
||||
// Now there should be 2 index wit one connection each
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 2);
|
||||
values = Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), expectedConnectionKey);
|
||||
values++;
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), secondExpectedKey);
|
||||
|
||||
#ifdef RUN_LONG_UNIT_TESTS
|
||||
{
|
||||
// Test pool clean routine
|
||||
std::cout << "Running Connection Pool Cleaner Test. This test can take up to 2 minutes to "
|
||||
"complete."
|
||||
<< std::endl
|
||||
<< "Add compiler option -DRUN_LONG_UNIT_TESTS=OFF when building if you want to "
|
||||
"skip this test."
|
||||
<< std::endl;
|
||||
|
||||
// Wait for 100 secs to make sure connections are removed.
|
||||
// Connection need to be in the pool for more than 60 sec to consider it expired.
|
||||
// Clean routine runs every 90 secs.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000 * 100));
|
||||
|
||||
// Ensure connections are removed but indexes are still there
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 2);
|
||||
values = Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 0);
|
||||
values++;
|
||||
EXPECT_EQ(values->second.size(), 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
}}} // namespace Azure::Core::Test
|
||||
@ -28,7 +28,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
*/
|
||||
class MockCurlNetworkConnection : public Azure::Core::Http::CurlNetworkConnection {
|
||||
public:
|
||||
MOCK_METHOD(std::string const&, GetHost, (), (const, override));
|
||||
MOCK_METHOD(std::string const&, GetConnectionKey, (), (const, override));
|
||||
MOCK_METHOD(void, updateLastUsageTime, (), (override));
|
||||
MOCK_METHOD(bool, isExpired, (), (override));
|
||||
MOCK_METHOD(
|
||||
|
||||
@ -50,7 +50,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
{
|
||||
// chunked response with no content and no size
|
||||
std::string response("HTTP/1.1 200 Ok\r\ntransfer-encoding: chunked\r\n\r\n\n\r\n");
|
||||
std::string host("sample-host");
|
||||
std::string connectionKey("connection-key");
|
||||
|
||||
// Can't mock the curMock directly from a unique ptr, heap allocate it first and then make a
|
||||
// unique ptr for it
|
||||
@ -60,7 +60,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
.WillOnce(DoAll(
|
||||
SetArrayArgument<1>(response.data(), response.data() + response.size()),
|
||||
Return(response.size())));
|
||||
EXPECT_CALL(*curlMock, GetHost()).WillRepeatedly(ReturnRef(host));
|
||||
EXPECT_CALL(*curlMock, GetConnectionKey()).WillRepeatedly(ReturnRef(connectionKey));
|
||||
EXPECT_CALL(*curlMock, updateLastUsageTime());
|
||||
EXPECT_CALL(*curlMock, DestructObj());
|
||||
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "transport_adapter_base.hpp"
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/http/policy.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// The next includes are from Azure Core private headers.
|
||||
// That's why the path starts from `sdk/core/azure-core/src/`
|
||||
// They are included to test the connection pool from the curl transport adapter implementation.
|
||||
#include <http/curl/curl_connection_pool_private.hpp>
|
||||
#include <http/curl/curl_connection_private.hpp>
|
||||
|
||||
using testing::ValuesIn;
|
||||
|
||||
namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
/*********************** Unique Tests for Libcurl ********************************/
|
||||
// Disabling test with INCLUDE_DISABLED_TESTS. The test name cannot be changed because it depends
|
||||
// on a friend class definition. Hence, it can't use the gtest DISABLE_.
|
||||
#if defined(INCLUDE_DISABLED_TESTS)
|
||||
TEST_P(TransportAdapter, connectionPoolTest)
|
||||
{
|
||||
Azure::Core::Http::Url host("http://httpbin.org/get");
|
||||
Azure::Core::Http::CurlConnectionPool::ClearIndex();
|
||||
|
||||
auto threadRoutine = [&]() {
|
||||
using namespace std::chrono_literals;
|
||||
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
|
||||
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
|
||||
checkResponseCode(response->GetStatusCode());
|
||||
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
|
||||
CheckBodyFromBuffer(*response, expectedResponseBodySize);
|
||||
std::this_thread::sleep_for(1s);
|
||||
};
|
||||
|
||||
std::thread t1(threadRoutine);
|
||||
std::thread t2(threadRoutine);
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
// 2 connections must be available at this point
|
||||
EXPECT_EQ(Http::CurlConnectionPool::ConnectionsOnPool("httpbin.org"), 2);
|
||||
|
||||
std::thread t3(threadRoutine);
|
||||
std::thread t4(threadRoutine);
|
||||
std::thread t5(threadRoutine);
|
||||
t3.join();
|
||||
t4.join();
|
||||
t5.join();
|
||||
|
||||
// Two connections re-used plus one connection created
|
||||
EXPECT_EQ(Http::CurlConnectionPool::ConnectionsOnPool("httpbin.org"), 3);
|
||||
|
||||
#if defined(RUN_LONG_UNIT_TESTS)
|
||||
{
|
||||
// Test pool clean routine
|
||||
std::cout << "Running Connection Pool Cleaner Test. This test takes more than 3 minutes to "
|
||||
"complete."
|
||||
<< std::endl
|
||||
<< "Add compiler option -DRUN_LONG_UNIT_TESTS=OFF when building if you want to "
|
||||
"skip this test."
|
||||
<< std::endl;
|
||||
|
||||
// Wait for 180 secs to make sure any previous connection is removed by the cleaner
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000 * 180));
|
||||
|
||||
std::cout << "First wait time done. Validating state." << std::endl;
|
||||
|
||||
// index is not affected by cleaner. It does not remove index
|
||||
EXPECT_EQ(Http::CurlConnectionPool::ConnectionsIndexOnPool(), 1);
|
||||
// cleaner should have removed connections
|
||||
EXPECT_EQ(Http::CurlConnectionPool::ConnectionsOnPool("httpbin.org"), 0);
|
||||
|
||||
std::thread t1(threadRoutine);
|
||||
std::thread t2(threadRoutine);
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
// wait for connection to be moved back to pool
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
// 2 connections must be available at this point and one index
|
||||
EXPECT_EQ(Http::CurlConnectionPool::ConnectionsIndexOnPool(), 1);
|
||||
// Depending on how fast the previous requests are sent, there could be one or more
|
||||
// connections in the pool. If first request is too fast, the second request will reuse the
|
||||
// same connection.
|
||||
EXPECT_PRED1(
|
||||
[](int currentConnections) { return currentConnections > 1; },
|
||||
Http::CurlConnectionPool::ConnectionsOnPool("httpbin.org"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}}} // namespace Azure::Core::Test
|
||||
Loading…
Reference in New Issue
Block a user