Fix for Azure Core with libcurl. clean up and global clean up (#1500)
make SDK to call global_init and global_cleanup
This commit is contained in:
parent
ff0d5a18cd
commit
5de76e7a21
@ -29,7 +29,7 @@ The Azure SDK for C++ provides two HTTP transport adapters as part of the azure-
|
||||
|
||||
Users can create and use their own transport adapter [by following the guidance below](#building-a-custom-http-transport-adapter).
|
||||
|
||||
For the simplest case, when no specific configuration is set, the default transport adapter will be selected based on the OS. The WinHTTP transport adapter will be used for Windows-based systems (making libcurl an optional dependency). The libcurl transport adapter will be selected for any non-Windows-based system.
|
||||
For the simplest case, when no specific configuration is set, the default transport adapter will be selected based on the OS. The WinHTTP transport adapter will be used for Windows-based systems (making libcurl an optional dependency). The libcurl transport adapter will be selected for any non-Windows-based system. Read more about using the libcurl transport adapter here.
|
||||
|
||||
## Building the HTTP Transport Adapter
|
||||
|
||||
|
||||
37
doc/LibCurlTransportAdatper.md
Normal file
37
doc/LibCurlTransportAdatper.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Libcurl Transport Adapter
|
||||
|
||||
The Azure SDK for C++ provides an http transport adapter implementation using the well known libcurl library. There are some general guidelines that you should know if you decide to use this implementation.
|
||||
|
||||
## Global init and clean up
|
||||
|
||||
Libcurl provides the functions [curl_global_init](https://curl.se/libcurl/c/curl_global_init.html) and [curl_global_cleanup](https://curl.se/libcurl/c/curl_global_cleanup.html) which are expected to be called at the start and before exiting the application. However, for a modular part of the application, like a library, it is expected that each library using libcurl will call the `curl_global_init` and `curl_global_cleanup`. From [libcurl global constants documentation](https://curl.se/libcurl/c/libcurl.html):
|
||||
|
||||
> Note that if multiple modules in the program use libcurl, they all will separately call the libcurl functions, and that's OK because only the first curl_global_init and the last curl_global_cleanup in a program change anything. (libcurl uses a reference count in static memory).
|
||||
|
||||
Consider the next example:
|
||||
|
||||
```cpp
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file Demonstrate how to use libcurl and the Azure SDK for C++.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <azure/storage/blob/blobs.hpp>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
// Use the Azure SDK clients.
|
||||
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If the application is not using libcurl at all, only the Azure SDK library will call `curl_global_init` and `curl_global_cleanup`. If the application is calling the global functions and using libcurl, then the calls to these functions from the Azure SDK library won't change anything. So, as an application or library owner, you only need to worry about calling the global libcurl functions if you will be using libcurl directly.
|
||||
@ -184,7 +184,6 @@ static inline std::string GetHTTPMessagePreBody(Azure::Core::Http::Request const
|
||||
|
||||
using Azure::Core::Context;
|
||||
using Azure::Core::Http::CurlConnection;
|
||||
using Azure::Core::Http::CurlConnectionPool;
|
||||
using Azure::Core::Http::CurlNetworkConnection;
|
||||
using Azure::Core::Http::CurlSession;
|
||||
using Azure::Core::Http::CurlTransport;
|
||||
@ -193,6 +192,7 @@ using Azure::Core::Http::HttpStatusCode;
|
||||
using Azure::Core::Http::RawResponse;
|
||||
using Azure::Core::Http::Request;
|
||||
using Azure::Core::Http::TransportException;
|
||||
using Azure::Core::Http::_detail::CurlConnectionPool;
|
||||
|
||||
std::unique_ptr<RawResponse> CurlTransport::Send(Request& request, Context const& context)
|
||||
{
|
||||
@ -1109,8 +1109,8 @@ int64_t CurlSession::ResponseBufferParser::BuildHeader(
|
||||
std::mutex CurlConnectionPool::ConnectionPoolMutex;
|
||||
std::map<std::string, std::list<std::unique_ptr<CurlNetworkConnection>>>
|
||||
CurlConnectionPool::ConnectionPoolIndex;
|
||||
uint64_t CurlConnectionPool::s_connectionCounter = 0;
|
||||
bool CurlConnectionPool::s_isCleanConnectionsRunning = false;
|
||||
uint64_t CurlConnectionPool::g_connectionCounter = 0;
|
||||
std::atomic<bool> CurlConnectionPool::g_isCleanConnectionsRunning(false);
|
||||
|
||||
namespace {
|
||||
inline std::string GetConnectionKey(std::string const& host, CurlTransportOptions const& options)
|
||||
@ -1174,7 +1174,7 @@ std::unique_ptr<CurlNetworkConnection> CurlConnectionPool::ExtractOrCreateCurlCo
|
||||
if (resetPool)
|
||||
{
|
||||
// Remove all connections for the connection Key and move to spawn new connection below
|
||||
CurlConnectionPool::s_connectionCounter -= hostPoolIndex->second.size();
|
||||
CurlConnectionPool::g_connectionCounter -= hostPoolIndex->second.size();
|
||||
hostPoolIndex->second.clear();
|
||||
}
|
||||
else
|
||||
@ -1186,7 +1186,7 @@ std::unique_ptr<CurlNetworkConnection> CurlConnectionPool::ExtractOrCreateCurlCo
|
||||
// Remove the connection ref from list
|
||||
hostPoolIndex->second.erase(fistConnectionIterator);
|
||||
// reduce number of connections on the pool
|
||||
CurlConnectionPool::s_connectionCounter -= 1;
|
||||
CurlConnectionPool::g_connectionCounter -= 1;
|
||||
|
||||
// Remove index if there are no more connections
|
||||
if (hostPoolIndex->second.size() == 0)
|
||||
@ -1330,11 +1330,11 @@ void CurlConnectionPool::MoveConnectionBackToPool(
|
||||
// update the time when connection was moved back to pool
|
||||
connection->updateLastUsageTime();
|
||||
hostPool.push_front(std::move(connection));
|
||||
CurlConnectionPool::s_connectionCounter += 1;
|
||||
CurlConnectionPool::g_connectionCounter += 1;
|
||||
// Check if there's no cleaner running and started
|
||||
if (!CurlConnectionPool::s_isCleanConnectionsRunning)
|
||||
if (!CurlConnectionPool::g_isCleanConnectionsRunning)
|
||||
{
|
||||
CurlConnectionPool::s_isCleanConnectionsRunning = true;
|
||||
CurlConnectionPool::g_isCleanConnectionsRunning = true;
|
||||
CurlConnectionPool::CleanUp();
|
||||
}
|
||||
}
|
||||
@ -1350,14 +1350,22 @@ void CurlConnectionPool::CleanUp()
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(_detail::DefaultCleanerIntervalMilliseconds));
|
||||
|
||||
// while sleeping, it is allowed to explicitly prevent the cleaner to run and stop it, for
|
||||
// example, when the application exits, and the cleaner is sleeping, we don't want it to wake
|
||||
// up and try to access de-allocated memory.
|
||||
if (!CurlConnectionPool::g_isCleanConnectionsRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// take mutex for reading the pool
|
||||
std::lock_guard<std::mutex> lock(CurlConnectionPool::ConnectionPoolMutex);
|
||||
|
||||
if (CurlConnectionPool::s_connectionCounter == 0)
|
||||
if (CurlConnectionPool::g_connectionCounter == 0)
|
||||
{
|
||||
// stop the cleaner since there are no connections
|
||||
CurlConnectionPool::s_isCleanConnectionsRunning = false;
|
||||
CurlConnectionPool::g_isCleanConnectionsRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1385,7 +1393,7 @@ void CurlConnectionPool::CleanUp()
|
||||
// remove connection from the pool and update the connection to the next one
|
||||
// which is going to be list.end()
|
||||
connection = index->second.erase(connection);
|
||||
CurlConnectionPool::s_connectionCounter -= 1;
|
||||
CurlConnectionPool::g_connectionCounter -= 1;
|
||||
|
||||
// Connection removed, break if there are no more connections to check
|
||||
if (index->second.size() == 0)
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#include "curl_connection_private.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <curl/curl.h>
|
||||
#include <list>
|
||||
#include <map>
|
||||
@ -27,7 +28,10 @@ namespace Azure { namespace Core { namespace Test {
|
||||
}}} // namespace Azure::Core::Test
|
||||
#endif
|
||||
|
||||
namespace Azure { namespace Core { namespace Http {
|
||||
namespace Azure { namespace Core { namespace Http { namespace _detail {
|
||||
|
||||
// In charge of calling the libcurl global functions for the Azure SDK
|
||||
struct CurlGlobalStateForAzureSdk;
|
||||
|
||||
/**
|
||||
* @brief CURL HTTP connection pool makes it possible to re-use one curl connection to perform
|
||||
@ -41,6 +45,10 @@ namespace Azure { namespace Core { namespace Http {
|
||||
// Give access to private to this tests class
|
||||
friend class Azure::Core::Test::CurlConnectionPool_connectionPoolTest_Test;
|
||||
#endif
|
||||
private:
|
||||
// The cttor and dttor of this member makes sure of calling the libcurl global init and cleanup
|
||||
AZ_CORE_DLLEXPORT static CurlGlobalStateForAzureSdk CurlGlobalState;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Mutex for accessing connection pool for thread-safe reading and writing.
|
||||
@ -66,8 +74,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
* @param request HTTP request to get #Azure::Core::Http::CurlNetworkConnection for.
|
||||
* @param options The connection settings which includes host name and libcurl handle specific
|
||||
* configuration.
|
||||
* @param resetPool Request the pool to remove all current connections for the provided options
|
||||
* to force the creation of a new connection.
|
||||
* @param resetPool Request the pool to remove all current connections for the provided
|
||||
* options to force the creation of a new connection.
|
||||
*
|
||||
* @return #Azure::Core::Http::CurlNetworkConnection to use.
|
||||
*/
|
||||
@ -89,6 +97,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
// Class can't have instances.
|
||||
CurlConnectionPool() = delete;
|
||||
|
||||
static void StopCleaner() { g_isCleanConnectionsRunning = false; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Review all connections in the pool and removes old connections that might be already
|
||||
@ -96,8 +106,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
*/
|
||||
static void CleanUp();
|
||||
|
||||
AZ_CORE_DLLEXPORT static uint64_t s_connectionCounter;
|
||||
AZ_CORE_DLLEXPORT static bool s_isCleanConnectionsRunning;
|
||||
AZ_CORE_DLLEXPORT static uint64_t g_connectionCounter;
|
||||
AZ_CORE_DLLEXPORT static std::atomic<bool> g_isCleanConnectionsRunning;
|
||||
// Removes all connections and indexes
|
||||
static void ClearIndex() { CurlConnectionPool::ConnectionPoolIndex.clear(); }
|
||||
|
||||
@ -115,4 +125,16 @@ namespace Azure { namespace Core { namespace Http {
|
||||
return CurlConnectionPool::ConnectionPoolIndex.size();
|
||||
};
|
||||
};
|
||||
}}} // namespace Azure::Core::Http
|
||||
|
||||
struct CurlGlobalStateForAzureSdk
|
||||
{
|
||||
CurlGlobalStateForAzureSdk() { curl_global_init(CURL_GLOBAL_ALL); }
|
||||
|
||||
~CurlGlobalStateForAzureSdk()
|
||||
{
|
||||
CurlConnectionPool::StopCleaner();
|
||||
curl_global_cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
}}}} // namespace Azure::Core::Http::_detail
|
||||
|
||||
@ -385,7 +385,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
// IsEOF will also handle a connection that fail to complete an upload request.
|
||||
if (IsEOF() && m_keepAlive)
|
||||
{
|
||||
CurlConnectionPool::MoveConnectionBackToPool(std::move(m_connection), m_lastStatusCode);
|
||||
_detail::CurlConnectionPool::MoveConnectionBackToPool(
|
||||
std::move(m_connection), m_lastStatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -105,7 +105,32 @@ gtest_discover_tests(azure-core-test
|
||||
NO_PRETTY_TYPES
|
||||
NO_PRETTY_VALUES)
|
||||
|
||||
if(BUILD_TRANSPORT_CURL)
|
||||
################## Azure Libcurl Core Test #################################
|
||||
# Creating one exe file alone for this test since it requires the full control over the connection pool.
|
||||
# This test will check that end-user can call `curl_global_cleanup()` with active handlers in the connection pool
|
||||
# without getting errors.
|
||||
add_executable (
|
||||
azure-core-libcurl-test
|
||||
azure_libcurl_core_main.cpp
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
# warning C4389: '==': signed/unsigned mismatch
|
||||
target_compile_options(azure-core-libcurl-test PUBLIC /wd4389)
|
||||
endif()
|
||||
|
||||
# Adding private headers from CORE to the tests so we can test the private APIs with no relative paths include.
|
||||
target_include_directories (azure-core-libcurl-test PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src>)
|
||||
|
||||
target_link_libraries(azure-core-libcurl-test PRIVATE azure-core gtest gmock)
|
||||
|
||||
# Use the same prefix to run this test
|
||||
gtest_discover_tests(azure-core-libcurl-test
|
||||
TEST_PREFIX azure-core.)
|
||||
|
||||
endif()
|
||||
gtest_discover_tests(azure-core-global-context-test
|
||||
TEST_PREFIX azure-core-global-context.
|
||||
TEST_PREFIX azure-core.
|
||||
NO_PRETTY_TYPES
|
||||
NO_PRETTY_VALUES)
|
||||
|
||||
63
sdk/core/azure-core/test/ut/azure_libcurl_core_main.cpp
Normal file
63
sdk/core/azure-core/test/ut/azure_libcurl_core_main.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file This test assumes the application is already using libcurl.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/http/curl_transport.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/core/http/policies/policy.hpp>
|
||||
#include <azure/core/io/body_stream.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
|
||||
#include <http/curl/curl_connection_pool_private.hpp>
|
||||
#include <http/curl/curl_connection_private.hpp>
|
||||
#include <http/curl/curl_session_private.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace Azure { namespace Core { namespace Test {
|
||||
TEST(SdkWithLibcurl, globalCleanUp)
|
||||
{
|
||||
Azure::Core::Http::Request req(
|
||||
Azure::Core::Http::HttpMethod::Get, Azure::Core::Url("https://httpbin.org/get"));
|
||||
|
||||
{
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection
|
||||
= Azure::Core::Http::_detail::CurlConnectionPool::ExtractOrCreateCurlConnection(
|
||||
req, options);
|
||||
|
||||
auto session = std::make_unique<Azure::Core::Http::CurlSession>(
|
||||
req, std::move(connection), options.HttpKeepAlive);
|
||||
session->Perform(Azure::Core::Context::GetApplicationContext());
|
||||
// Reading all the response
|
||||
session->ReadToEnd(Azure::Core::Context::GetApplicationContext());
|
||||
}
|
||||
// Check that after the connection is gone, it is moved back to the pool
|
||||
EXPECT_EQ(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
}
|
||||
}}} // namespace Azure::Core::Test
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
auto r = RUN_ALL_TESTS();
|
||||
return r;
|
||||
}
|
||||
@ -30,9 +30,9 @@ namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
TEST(CurlConnectionPool, connectionPoolTest)
|
||||
{
|
||||
Azure::Core::Http::CurlConnectionPool::ClearIndex();
|
||||
Azure::Core::Http::_detail::CurlConnectionPool::ClearIndex();
|
||||
// Make sure there are nothing in the pool
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
EXPECT_EQ(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
|
||||
// Use the same request for all connections.
|
||||
Azure::Core::Http::Request req(
|
||||
@ -43,7 +43,8 @@ namespace Azure { namespace Core { namespace Test {
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection
|
||||
= Azure::Core::Http::CurlConnectionPool::ExtractOrCreateCurlConnection(req, options);
|
||||
= Azure::Core::Http::_detail::CurlConnectionPool::ExtractOrCreateCurlConnection(
|
||||
req, options);
|
||||
|
||||
EXPECT_EQ(connection->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
@ -54,10 +55,11 @@ namespace Azure { namespace Core { namespace Test {
|
||||
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(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
auto connectionFromPool
|
||||
= Azure::Core::Http::_detail::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
|
||||
@ -65,10 +67,11 @@ namespace Azure { namespace Core { namespace Test {
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection
|
||||
= Azure::Core::Http::CurlConnectionPool::ExtractOrCreateCurlConnection(req, options);
|
||||
= Azure::Core::Http::_detail::CurlConnectionPool::ExtractOrCreateCurlConnection(
|
||||
req, options);
|
||||
|
||||
// There was just one connection in the pool, it should be empty now
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
EXPECT_EQ(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
// And the connection key for the connection we got is the expected
|
||||
EXPECT_EQ(connection->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
@ -79,8 +82,8 @@ namespace Azure { namespace Core { namespace Test {
|
||||
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(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
auto values = Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), expectedConnectionKey);
|
||||
|
||||
@ -91,13 +94,14 @@ namespace Azure { namespace Core { namespace Test {
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
options.SslVerifyPeer = false;
|
||||
auto connection
|
||||
= Azure::Core::Http::CurlConnectionPool::ExtractOrCreateCurlConnection(req, options);
|
||||
= Azure::Core::Http::_detail::CurlConnectionPool::ExtractOrCreateCurlConnection(
|
||||
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::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
EXPECT_EQ(
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
->second.begin()
|
||||
->get()
|
||||
->GetConnectionKey(),
|
||||
@ -111,8 +115,8 @@ namespace Azure { namespace Core { namespace Test {
|
||||
}
|
||||
|
||||
// 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(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 2);
|
||||
values = Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), secondExpectedKey);
|
||||
values++;
|
||||
@ -124,13 +128,14 @@ namespace Azure { namespace Core { namespace Test {
|
||||
// Creating a new connection with default options
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection
|
||||
= Azure::Core::Http::CurlConnectionPool::ExtractOrCreateCurlConnection(req, options);
|
||||
= Azure::Core::Http::_detail::CurlConnectionPool::ExtractOrCreateCurlConnection(
|
||||
req, options);
|
||||
EXPECT_EQ(connection->GetConnectionKey(), expectedConnectionKey);
|
||||
// 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::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 1);
|
||||
EXPECT_EQ(
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.begin()
|
||||
->second.begin()
|
||||
->get()
|
||||
->GetConnectionKey(),
|
||||
@ -143,8 +148,8 @@ namespace Azure { namespace Core { namespace Test {
|
||||
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(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 2);
|
||||
values = Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 1);
|
||||
EXPECT_EQ(values->second.begin()->get()->GetConnectionKey(), secondExpectedKey);
|
||||
values++;
|
||||
@ -167,8 +172,8 @@ namespace Azure { namespace Core { namespace Test {
|
||||
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(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 2);
|
||||
values = Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.begin();
|
||||
EXPECT_EQ(values->second.size(), 0);
|
||||
values++;
|
||||
EXPECT_EQ(values->second.size(), 0);
|
||||
@ -183,7 +188,8 @@ namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
Azure::Core::Http::CurlTransportOptions options;
|
||||
auto connection
|
||||
= Azure::Core::Http::CurlConnectionPool::ExtractOrCreateCurlConnection(req, options);
|
||||
= Azure::Core::Http::_detail::CurlConnectionPool::ExtractOrCreateCurlConnection(
|
||||
req, options);
|
||||
// Simulate connection lost (like server disconnection).
|
||||
connection->Shutdown();
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
// Clean the connection from the pool *Windows fails to clean if we leave to be clean uppon
|
||||
// app-destruction
|
||||
EXPECT_NO_THROW(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear());
|
||||
EXPECT_NO_THROW(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.clear());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -208,7 +208,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
// Clean the connection from the pool *Windows fails to clean if we leave to be clean uppon
|
||||
// app-destruction
|
||||
EXPECT_NO_THROW(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear());
|
||||
EXPECT_NO_THROW(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.clear());
|
||||
}
|
||||
|
||||
TEST(CurlTransportOptions, httpsDefault)
|
||||
@ -240,7 +240,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
|
||||
// Clean the connection from the pool *Windows fails to clean if we leave to be clean uppon
|
||||
// app-destruction
|
||||
EXPECT_NO_THROW(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear());
|
||||
EXPECT_NO_THROW(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.clear());
|
||||
}
|
||||
|
||||
TEST(CurlTransportOptions, disableKeepAlive)
|
||||
@ -276,7 +276,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
responseCode));
|
||||
}
|
||||
// Make sure there are no connections in the pool
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
EXPECT_EQ(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Core::Test
|
||||
|
||||
@ -79,7 +79,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
EXPECT_NO_THROW(session->Perform(Azure::Core::Context::GetApplicationContext()));
|
||||
}
|
||||
// Clear the connections from the pool to invoke clean routine
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear();
|
||||
Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.clear();
|
||||
}
|
||||
|
||||
TEST_F(CurlSession, chunkBadFormatResponse)
|
||||
@ -127,7 +127,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
Azure::Core::Http::TransportException);
|
||||
}
|
||||
// Clear the connections from the pool to invoke clean routine
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear();
|
||||
Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.clear();
|
||||
}
|
||||
|
||||
TEST_F(CurlSession, chunkSegmentedResponse)
|
||||
@ -202,7 +202,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
EXPECT_NO_THROW(bodyS->ReadToEnd(Azure::Core::Context::GetApplicationContext()));
|
||||
}
|
||||
// Clear the connections from the pool to invoke clean routine
|
||||
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear();
|
||||
Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.clear();
|
||||
}
|
||||
|
||||
TEST_F(CurlSession, DoNotReuseConnectionIfDownloadFail)
|
||||
@ -231,6 +231,6 @@ namespace Azure { namespace Core { namespace Test {
|
||||
EXPECT_EQ(CURLE_SEND_ERROR, returnCode);
|
||||
}
|
||||
// Check connection pool is empty (connection was not moved to the pool)
|
||||
EXPECT_EQ(Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
EXPECT_EQ(Azure::Core::Http::_detail::CurlConnectionPool::ConnectionPoolIndex.size(), 0);
|
||||
}
|
||||
}}} // namespace Azure::Core::Test
|
||||
|
||||
@ -11,24 +11,14 @@
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
// OpenSSL signals SIGPIPE when trying to clean an HTTPS closed connection.
|
||||
// End users need to decide if SIGPIPE should be ignored or not.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
auto r = RUN_ALL_TESTS();
|
||||
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
// Cleaning ssl connections on Windows is broken until
|
||||
// https://github.com/Azure/azure-sdk-for-cpp/pull/1500 is merged.
|
||||
curl_global_cleanup();
|
||||
#endif
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -38,12 +38,9 @@ namespace Azure { namespace Perf { namespace Test {
|
||||
*/
|
||||
void GlobalSetup() override
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
_detail::HttpClient = std::make_unique<Azure::Core::Http::CurlTransport>();
|
||||
}
|
||||
|
||||
void GlobalCleanup() override { curl_global_cleanup(); }
|
||||
|
||||
/**
|
||||
* @brief Get the static Test Metadata for the test.
|
||||
*
|
||||
|
||||
@ -11,24 +11,7 @@
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
// OpenSSL signals SIGPIPE when trying to clean an HTTPS closed connection.
|
||||
// End users need to decide if SIGPIPE should be ignored or not.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
auto r = RUN_ALL_TESTS();
|
||||
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
// Cleaning ssl connections on Windows is broken until
|
||||
// https://github.com/Azure/azure-sdk-for-cpp/pull/1500 is merged.
|
||||
curl_global_cleanup();
|
||||
#endif
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -9,16 +9,7 @@
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
auto r = RUN_ALL_TESTS();
|
||||
|
||||
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
|
||||
// Can't call global cleanup due to: https://github.com/Azure/azure-sdk-for-cpp/issues/1499
|
||||
// curl_global_cleanup();
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user