maka transport adapter base class for any transport to add its own implementation (#928)

This commit is contained in:
Victor Vazquez 2020-11-10 15:42:09 -08:00 committed by GitHub
parent 71457f89d8
commit eb509cebb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 734 additions and 702 deletions

View File

@ -21,6 +21,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
if(BUILD_TRANSPORT_CURL)
SET(CURL_OPTIONS_TESTS curl_options.cpp)
SET(CURL_TRANSPORT_TESTS transport_adapter_curl.cpp)
endif()
include(GoogleTest)
@ -39,8 +40,8 @@ add_executable (
simplified_header.cpp
string.cpp
telemetry_policy.cpp
transport_adapter.cpp
transport_adapter_file_upload.cpp
transport_adapter_base.cpp
${CURL_TRANSPORT_TESTS}
url.cpp
uuid.cpp
)

View File

@ -1,508 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "transport_adapter.hpp"
#include <azure/core/context.hpp>
#include <azure/core/response.hpp>
#include <iostream>
#include <string>
#include <thread>
namespace Azure { namespace Core { namespace Test {
static std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> CreatePolicies()
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> p;
Azure::Core::Http::RetryOptions opt;
opt.RetryDelay = std::chrono::milliseconds(10);
// Retry policy will help to prevent server-occasionally-errors
p.push_back(std::make_unique<Azure::Core::Http::RetryPolicy>(opt));
p.push_back(std::make_unique<Azure::Core::Http::TransportPolicy>());
return p;
}
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> TransportAdapter::policies
= CreatePolicies();
Azure::Core::Http::HttpPipeline TransportAdapter::pipeline(policies);
Azure::Core::Context TransportAdapter::context = Azure::Core::GetApplicationContext();
// Connection pool feature is curl-implementation only. No other transport adapter would have the
// connection pool
#ifdef BUILD_CURL_HTTP_TRANSPORT_ADAPTER
// connectionPoolTest requires `ConnectionsOnPool` hook which is only available when building
// BUILD_TESTING. This test is only built when that case is true.
TEST_F(TransportAdapter, connectionPoolTest)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
Azure::Core::Http::CurlConnectionPool::ClearIndex();
auto threadRoutine = [host]() {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
};
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);
#ifdef 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);
EXPECT_EQ(Http::CurlConnectionPool::ConnectionsOnPool("httpbin.org"), 2);
}
#endif
}
#endif
TEST_F(TransportAdapter, get)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
// Need to init request again, since retry would be on after it is sent
request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// Add a header and send again. RawResponse should return that header in the body
request.AddHeader("123", "456");
response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
// header length is 6 (data) + 13 (formating) -> ` "123": "456"\r\n,`
CheckBodyFromBuffer(*response, expectedResponseBodySize + 6 + 13);
}
TEST_F(TransportAdapter, get204)
{
Azure::Core::Http::Url host("http://mt3.google.com/generate_204");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, getLoop)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// loop sending request
for (auto i = 0; i < 50; i++)
{
auto response = pipeline.Send(context, request);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
checkResponseCode(response->GetStatusCode());
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
}
TEST_F(TransportAdapter, head)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto expectedResponseBodySize = 0;
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, host);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromBuffer(*response, expectedResponseBodySize);
// Check content-length header to be greater than 0
int64_t contentLengthHeader = std::stoull(response->GetHeaders().at("content-length"));
EXPECT_TRUE(contentLengthHeader > 0);
}
TEST_F(TransportAdapter, put)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
// PUT 1K
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, deleteRequest)
{
Azure::Core::Http::Url host("http://httpbin.org/delete");
// Delete with 1k payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Delete, host, &bodyRequest);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, patch)
{
Azure::Core::Http::Url host("http://httpbin.org/patch");
// Patch with 1kb payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Patch, host, &bodyRequest);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, getChunk)
{
Azure::Core::Http::Url host("http://anglesharp.azurewebsites.net/Chunked");
auto expectedResponseBodySize = -1; // chunked will return unknown body length
auto expectedChunkResponse = std::string(
"<!DOCTYPE html>\r\n<html lang=en>\r\n<head>\r\n<meta charset='utf-8'>\r\n<title>Chunked "
"transfer encoding test</title>\r\n</head>\r\n<body><h1>Chunked transfer encoding "
"test</h1><h5>This is a chunked response after 100 ms.</h5><h5>This is a chunked "
"response after 1 second. The server should not close the stream before all chunks are "
"sent to a client.</h5></body></html>");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromBuffer(*response, expectedResponseBodySize, expectedChunkResponse);
}
TEST_F(TransportAdapter, putErrorResponse)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
// Try to make a PUT to a GET url. This will return an error code from server.
// This test makes sure that the connection is not re-used (because it gets closed by server)
// and next request is not hang
for (auto i = 0; i < 10; i++)
{
auto requestBodyVector = std::vector<uint8_t>(10, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest);
auto response = pipeline.Send(context, request);
}
}
// **********************
// ***Same tests but getting stream to pull from socket, simulating the Download Op
// **********************
TEST_F(TransportAdapter, getWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
// Add a header and send again. Response should return that header in the body
request.AddHeader("123", "456");
response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
// header length is 6 (data) + 13 (formating) -> ` "123": "456"\r\n,`
CheckBodyFromStream(*response, expectedResponseBodySize + 6 + 13);
}
TEST_F(TransportAdapter, getLoopWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
// loop sending request
for (auto i = 0; i < 50; i++)
{
auto response = pipeline.Send(context, request);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
checkResponseCode(response->GetStatusCode());
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
TEST_F(TransportAdapter, headWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto expectedResponseBodySize = 0;
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, host, true);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromStream(*response, expectedResponseBodySize);
// Check content-length header to be greater than 0
int64_t contentLengthHeader = std::stoull(response->GetHeaders().at("content-length"));
EXPECT_TRUE(contentLengthHeader > 0);
}
TEST_F(TransportAdapter, putWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
// PUT 1k
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &bodyRequest, true);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, deleteRequestWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/delete");
// Delete with 1k payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Delete, host, &bodyRequest, true);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, patchWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/patch");
// Patch with 1kb payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Patch, host, &bodyRequest, true);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, getChunkWithStream)
{
Azure::Core::Http::Url host("http://anglesharp.azurewebsites.net/Chunked");
auto expectedResponseBodySize = -1; // chunked will return unknown body length
auto expectedChunkResponse = std::string(
"<!DOCTYPE html>\r\n<html lang=en>\r\n<head>\r\n<meta charset='utf-8'>\r\n<title>Chunked "
"transfer encoding test</title>\r\n</head>\r\n<body><h1>Chunked transfer encoding "
"test</h1><h5>This is a chunked response after 100 ms.</h5><h5>This is a chunked "
"response after 1 second. The server should not close the stream before all chunks are "
"sent to a client.</h5></body></html>");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromStream(*response, expectedResponseBodySize, expectedChunkResponse);
}
TEST_F(TransportAdapter, createResponseT)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
std::string expectedType("This is the Response Type");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, false);
auto response = pipeline.Send(context, request);
Azure::Core::Response<std::string> responseT(expectedType, std::move(response));
auto& r = responseT.GetRawResponse();
EXPECT_TRUE(r.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok);
auto expectedResponseBodySize = std::stoull(r.GetHeaders().at("content-length"));
CheckBodyFromBuffer(r, expectedResponseBodySize);
// Direct access
EXPECT_STREQ((*responseT).data(), expectedType.data());
EXPECT_STREQ(responseT->data(), expectedType.data());
// extracting T out of response
auto result = responseT.ExtractValue();
EXPECT_STREQ(result.data(), expectedType.data());
// Test that calling getValue again will return empty
result = responseT.ExtractValue();
EXPECT_STREQ(result.data(), std::string("").data());
}
TEST_F(TransportAdapter, customSizePut)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
// PUT 1MB
auto requestBodyVector = std::vector<uint8_t>(1024 * 1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest);
// Make transport adapter to read all stream content for uploading instead of chunks
request.SetUploadChunkSize(1024 * 1024);
{
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
}
TEST_F(TransportAdapter, putWithStreamOnFail)
{
// point to bad address pah to generate server MethodNotAllowed error
Azure::Core::Http::Url host("http://httpbin.org/get");
// PUT 1k
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &bodyRequest, true);
auto response = pipeline.Send(context, request);
checkResponseCode(
response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::MethodNotAllowed);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_F(TransportAdapter, cancelTransferUpload)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
Azure::Core::Context cancelThis;
auto threadRoutine = [host, cancelThis]() {
// Start a big upload and expect it to throw cancelation
std::vector<uint8_t> bigBuffer(1024 * 1024 * 200, 'x'); // upload 200 Mb
auto stream = Azure::Core::Http::MemoryBodyStream(bigBuffer);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &stream);
// Request will be canceled from main thread throwing the exception
EXPECT_THROW(pipeline.Send(cancelThis, request), Azure::Core::OperationCanceledException);
};
// Start request
std::thread t1(threadRoutine);
// Wait 100 ms so we know upload has started
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cancelThis.Cancel();
t1.join();
}
TEST_F(TransportAdapter, cancelTransferDownload)
{
// public big blob (321MB)
Azure::Core::Http::Url host("https://bigtestfiles.blob.core.windows.net/cpptestfiles/321MB");
Azure::Core::Context cancelThis;
auto threadRoutine = [host, cancelThis]() {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// Request will be canceled from main thread throwing the exception
EXPECT_THROW(pipeline.Send(cancelThis, request), Azure::Core::OperationCanceledException);
};
// Start request
std::thread t1(threadRoutine);
// Wait 100 ms so we know download has started
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cancelThis.Cancel();
t1.join();
}
TEST_F(TransportAdapter, requestFailedException)
{
Azure::Core::Http::Url host("http://unresolvedHost.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
EXPECT_THROW(pipeline.Send(context, request), Azure::Core::RequestFailedException);
}
TEST_F(TransportAdapter, dynamicCast)
{
Azure::Core::Http::Url host("http://unresolvedHost.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// test dynamic cast
try
{
auto result = pipeline.Send(context, request);
}
catch (Azure::Core::RequestFailedException& err)
{
// if ref can't be cast, it throws
EXPECT_NO_THROW(dynamic_cast<Azure::Core::Http::TransportException&>(err));
EXPECT_NO_THROW(dynamic_cast<std::runtime_error&>(err));
EXPECT_THROW(dynamic_cast<std::range_error&>(err), std::bad_cast);
}
}
}}} // namespace Azure::Core::Test

View File

@ -0,0 +1,597 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#ifdef POSIX
#include <fcntl.h>
#endif // Posix
#ifdef WINDOWS
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif // Windows
#include "transport_adapter_base.hpp"
#include <azure/core/context.hpp>
#include <azure/core/response.hpp>
#include <iostream>
#include <string>
#include <thread>
namespace Azure { namespace Core { namespace Test {
namespace Datails {
constexpr int64_t c_fileSize = 1024 * 100;
}
TEST_P(TransportAdapter, get)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
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);
// Need to init request again, since retry would be on after it is sent
request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// Add a header and send again. RawResponse should return that header in the body
request.AddHeader("123", "456");
response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
// header length is 6 (data) + 13 (formating) -> ` "123": "456"\r\n,`
CheckBodyFromBuffer(*response, expectedResponseBodySize + 6 + 13);
}
TEST_P(TransportAdapter, get204)
{
Azure::Core::Http::Url host("http://mt3.google.com/generate_204");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, getLoop)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// loop sending request
for (auto i = 0; i < 50; i++)
{
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
checkResponseCode(response->GetStatusCode());
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
}
TEST_P(TransportAdapter, head)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto expectedResponseBodySize = 0;
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, host);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromBuffer(*response, expectedResponseBodySize);
// Check content-length header to be greater than 0
int64_t contentLengthHeader = std::stoull(response->GetHeaders().at("content-length"));
EXPECT_TRUE(contentLengthHeader > 0);
}
TEST_P(TransportAdapter, put)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
// PUT 1K
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, deleteRequest)
{
Azure::Core::Http::Url host("http://httpbin.org/delete");
// Delete with 1k payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Delete, host, &bodyRequest);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, patch)
{
Azure::Core::Http::Url host("http://httpbin.org/patch");
// Patch with 1kb payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Patch, host, &bodyRequest);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, getChunk)
{
Azure::Core::Http::Url host("http://anglesharp.azurewebsites.net/Chunked");
auto expectedResponseBodySize = -1; // chunked will return unknown body length
auto expectedChunkResponse = std::string(
"<!DOCTYPE html>\r\n<html lang=en>\r\n<head>\r\n<meta charset='utf-8'>\r\n<title>Chunked "
"transfer encoding test</title>\r\n</head>\r\n<body><h1>Chunked transfer encoding "
"test</h1><h5>This is a chunked response after 100 ms.</h5><h5>This is a chunked "
"response after 1 second. The server should not close the stream before all chunks are "
"sent to a client.</h5></body></html>");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromBuffer(*response, expectedResponseBodySize, expectedChunkResponse);
}
TEST_P(TransportAdapter, putErrorResponse)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
// Try to make a PUT to a GET url. This will return an error code from server.
// This test makes sure that the connection is not re-used (because it gets closed by server)
// and next request is not hang
for (auto i = 0; i < 10; i++)
{
auto requestBodyVector = std::vector<uint8_t>(10, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
}
}
// **********************
// ***Same tests but getting stream to pull from socket, simulating the Download Op
// **********************
TEST_P(TransportAdapter, getWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
// Add a header and send again. Response should return that header in the body
request.AddHeader("123", "456");
response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
// header length is 6 (data) + 13 (formating) -> ` "123": "456"\r\n,`
CheckBodyFromStream(*response, expectedResponseBodySize + 6 + 13);
}
TEST_P(TransportAdapter, getLoopWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
// loop sending request
for (auto i = 0; i < 50; i++)
{
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
checkResponseCode(response->GetStatusCode());
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
TEST_P(TransportAdapter, headWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
auto expectedResponseBodySize = 0;
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, host, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromStream(*response, expectedResponseBodySize);
// Check content-length header to be greater than 0
int64_t contentLengthHeader = std::stoull(response->GetHeaders().at("content-length"));
EXPECT_TRUE(contentLengthHeader > 0);
}
TEST_P(TransportAdapter, putWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
// PUT 1k
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, deleteRequestWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/delete");
// Delete with 1k payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Delete, host, &bodyRequest, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, patchWithStream)
{
Azure::Core::Http::Url host("http://httpbin.org/patch");
// Patch with 1kb payload
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Patch, host, &bodyRequest, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, getChunkWithStream)
{
Azure::Core::Http::Url host("http://anglesharp.azurewebsites.net/Chunked");
auto expectedResponseBodySize = -1; // chunked will return unknown body length
auto expectedChunkResponse = std::string(
"<!DOCTYPE html>\r\n<html lang=en>\r\n<head>\r\n<meta charset='utf-8'>\r\n<title>Chunked "
"transfer encoding test</title>\r\n</head>\r\n<body><h1>Chunked transfer encoding "
"test</h1><h5>This is a chunked response after 100 ms.</h5><h5>This is a chunked "
"response after 1 second. The server should not close the stream before all chunks are "
"sent to a client.</h5></body></html>");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
CheckBodyFromStream(*response, expectedResponseBodySize, expectedChunkResponse);
}
TEST_P(TransportAdapter, createResponseT)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
std::string expectedType("This is the Response Type");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host, false);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
Azure::Core::Response<std::string> responseT(expectedType, std::move(response));
auto& r = responseT.GetRawResponse();
EXPECT_TRUE(r.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok);
auto expectedResponseBodySize = std::stoull(r.GetHeaders().at("content-length"));
CheckBodyFromBuffer(r, expectedResponseBodySize);
// Direct access
EXPECT_STREQ((*responseT).data(), expectedType.data());
EXPECT_STREQ(responseT->data(), expectedType.data());
// extracting T out of response
auto result = responseT.ExtractValue();
EXPECT_STREQ(result.data(), expectedType.data());
// Test that calling getValue again will return empty
result = responseT.ExtractValue();
EXPECT_STREQ(result.data(), std::string("").data());
}
TEST_P(TransportAdapter, customSizePut)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
// PUT 1MB
auto requestBodyVector = std::vector<uint8_t>(1024 * 1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest);
// Make transport adapter to read all stream content for uploading instead of chunks
request.SetUploadChunkSize(1024 * 1024);
{
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
}
TEST_P(TransportAdapter, putWithStreamOnFail)
{
// point to bad address pah to generate server MethodNotAllowed error
Azure::Core::Http::Url host("http://httpbin.org/get");
// PUT 1k
auto requestBodyVector = std::vector<uint8_t>(1024, 'x');
auto bodyRequest = Azure::Core::Http::MemoryBodyStream(requestBodyVector);
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &bodyRequest, true);
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(
response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::MethodNotAllowed);
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromBuffer(*response, expectedResponseBodySize);
}
TEST_P(TransportAdapter, cancelTransferUpload)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
Azure::Core::Context cancelThis;
auto threadRoutine = [&]() {
// Start a big upload and expect it to throw cancelation
std::vector<uint8_t> bigBuffer(1024 * 1024 * 200, 'x'); // upload 200 Mb
auto stream = Azure::Core::Http::MemoryBodyStream(bigBuffer);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, host, &stream);
// Request will be canceled from main thread throwing the exception
EXPECT_THROW(m_pipeline->Send(cancelThis, request), Azure::Core::OperationCanceledException);
};
// Start request
std::thread t1(threadRoutine);
// Wait 100 ms so we know upload has started
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cancelThis.Cancel();
t1.join();
}
TEST_P(TransportAdapter, cancelTransferDownload)
{
// public big blob (321MB)
Azure::Core::Http::Url host("https://bigtestfiles.blob.core.windows.net/cpptestfiles/321MB");
Azure::Core::Context cancelThis;
auto threadRoutine = [&]() {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// Request will be canceled from main thread throwing the exception
EXPECT_THROW(m_pipeline->Send(cancelThis, request), Azure::Core::OperationCanceledException);
};
// Start request
std::thread t1(threadRoutine);
// Wait 100 ms so we know download has started
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cancelThis.Cancel();
t1.join();
}
TEST_P(TransportAdapter, requestFailedException)
{
Azure::Core::Http::Url host("http://unresolvedHost.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
EXPECT_THROW(
m_pipeline->Send(Azure::Core::GetApplicationContext(), request),
Azure::Core::RequestFailedException);
}
TEST_P(TransportAdapter, dynamicCast)
{
Azure::Core::Http::Url host("http://unresolvedHost.org/get");
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
// test dynamic cast
try
{
auto result = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
}
catch (Azure::Core::RequestFailedException& err)
{
// if ref can't be cast, it throws
EXPECT_NO_THROW(dynamic_cast<Azure::Core::Http::TransportException&>(err));
EXPECT_NO_THROW(dynamic_cast<std::runtime_error&>(err));
EXPECT_THROW(dynamic_cast<std::range_error&>(err), std::bad_cast);
}
}
TEST_P(TransportAdapter, SizePutFromFile)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
std::string testDataPath(AZURE_TEST_DATA_PATH);
#ifdef POSIX
testDataPath.append("/fileData");
int f = open(testDataPath.data(), O_RDONLY);
#endif
#ifdef WINDOWS
testDataPath.append("\\fileData");
HANDLE f = CreateFile(
testDataPath.data(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
#endif
auto requestBodyStream
= Azure::Core::Http::FileBodyStream(f, 0, Azure::Core::Test::Datails::c_fileSize);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &requestBodyStream, true);
// Make transport adapter to read all stream content for uploading instead of chunks
request.SetUploadChunkSize(Azure::Core::Test::Datails::c_fileSize);
{
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
TEST_P(TransportAdapter, SizePutFromFileDefault)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
std::string testDataPath(AZURE_TEST_DATA_PATH);
#ifdef POSIX
testDataPath.append("/fileData");
int f = open(testDataPath.data(), O_RDONLY);
#endif
#ifdef WINDOWS
testDataPath.append("\\fileData");
HANDLE f = CreateFile(
testDataPath.data(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
#endif
auto requestBodyStream
= Azure::Core::Http::FileBodyStream(f, 0, Azure::Core::Test::Datails::c_fileSize);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &requestBodyStream, true);
// Make transport adapter to read default chunk size
{
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
TEST_P(TransportAdapter, SizePutFromFileBiggerPage)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
std::string testDataPath(AZURE_TEST_DATA_PATH);
#ifdef POSIX
testDataPath.append("/fileData");
int f = open(testDataPath.data(), O_RDONLY);
#endif
#ifdef WINDOWS
testDataPath.append("\\fileData");
HANDLE f = CreateFile(
testDataPath.data(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
#endif
auto requestBodyStream
= Azure::Core::Http::FileBodyStream(f, 0, Azure::Core::Test::Datails::c_fileSize);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &requestBodyStream, true);
// Make transport adapter to read more than file size (5Mb)
request.SetUploadChunkSize(Azure::Core::Test::Datails::c_fileSize * 5);
{
auto response = m_pipeline->Send(Azure::Core::GetApplicationContext(), request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
/***************** Test Utils *************************/
void TransportAdapter::checkResponseCode(
Azure::Core::Http::HttpStatusCode code,
Azure::Core::Http::HttpStatusCode expectedCode)
{
EXPECT_PRED2(
[](Azure::Core::Http::HttpStatusCode a, Azure::Core::Http::HttpStatusCode b) {
return a == b;
},
code,
expectedCode);
}
void TransportAdapter::CheckBodyFromBuffer(
Azure::Core::Http::RawResponse& response,
int64_t size,
std::string expectedBody)
{
auto body = response.GetBodyStream();
EXPECT_EQ(body, nullptr);
std::vector<uint8_t> bodyVector = response.GetBody();
int64_t bodySize = bodyVector.size();
if (size > 0)
{ // only for known body size
EXPECT_EQ(bodySize, size);
}
if (expectedBody.size() > 0)
{
auto bodyString = std::string(bodyVector.begin(), bodyVector.end());
EXPECT_STREQ(expectedBody.data(), bodyString.data());
}
}
void TransportAdapter::CheckBodyFromStream(
Azure::Core::Http::RawResponse& response,
int64_t size,
std::string expectedBody)
{
auto body = response.GetBodyStream();
EXPECT_NE(body, nullptr);
std::vector<uint8_t> bodyVector
= Azure::Core::Http::BodyStream::ReadToEnd(Azure::Core::GetApplicationContext(), *body);
int64_t bodySize = body->Length();
EXPECT_EQ(bodySize, size);
bodySize = bodyVector.size();
if (size > 0)
{ // only for known body size
EXPECT_EQ(bodyVector.size(), size);
}
if (expectedBody.size() > 0)
{
auto bodyString = std::string(bodyVector.begin(), bodyVector.end());
EXPECT_STREQ(expectedBody.data(), bodyString.data());
}
}
}}} // namespace Azure::Core::Test

View File

@ -20,11 +20,26 @@
namespace Azure { namespace Core { namespace Test {
class TransportAdapter : public ::testing::Test {
class TransportAdapter
: public testing::TestWithParam<Azure::Core::Http::TransportPolicyOptions> {
protected:
static Azure::Core::Http::HttpPipeline pipeline;
static std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
static Azure::Core::Context context;
std::unique_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
// Befor each test, create pipeline
virtual void SetUp() override
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
Azure::Core::Http::RetryOptions opt;
opt.RetryDelay = std::chrono::milliseconds(10);
// Retry policy will help to prevent server-occasionally-errors
policies.push_back(std::make_unique<Azure::Core::Http::RetryPolicy>(opt));
// Will get transport policy options from test param
// auto param = GetParam();
policies.push_back(std::make_unique<Azure::Core::Http::TransportPolicy>(GetParam()));
m_pipeline = std::make_unique<Azure::Core::Http::HttpPipeline>(policies);
}
static void CheckBodyFromBuffer(
Azure::Core::Http::RawResponse& response,

View File

@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "transport_adapter_base.hpp"
#include <azure/core/context.hpp>
#include <azure/core/response.hpp>
#include <iostream>
#include <string>
#include <thread>
using testing::ValuesIn;
namespace Azure { namespace Core { namespace Test {
/********************** Define the parameters for the base test and a suffix ***************/
namespace {
static Azure::Core::Http::TransportPolicyOptions GetTransportOptions()
{
Azure::Core::Http::TransportPolicyOptions options;
options.Transport = std::make_shared<Azure::Core::Http::CurlTransport>();
return options;
}
// When adding more than one parameter, this function should return a unique string.
// But since we are only using one parameter (the libcurl transport adapter) this is fine.
static std::string GetSuffix(const testing::TestParamInfo<TransportAdapter::ParamType>& info)
{
// Can't use empty spaces or underscores (_) as per google test documentation
// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#specifying-names-for-value-parameterized-test-parameters
(void)(info);
std::string suffix("curlImplementation");
return suffix;
}
} // namespace
/*********************** Unique Tests for Libcurl ********************************/
TEST_P(TransportAdapter, connectionPoolTest)
{
Azure::Core::Http::Url host("http://httpbin.org/get");
Azure::Core::Http::CurlConnectionPool::ClearIndex();
auto threadRoutine = [&]() {
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::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);
#ifdef 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
}
/*********************** Base Transpoer Adapter Tests ******************************/
INSTANTIATE_TEST_SUITE_P(
TransportAdapterCurlImpl,
TransportAdapter,
testing::Values(GetTransportOptions()),
GetSuffix);
}}} // namespace Azure::Core::Test

View File

@ -1,188 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#ifdef POSIX
#include <fcntl.h>
#endif // Posix
#ifdef WINDOWS
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif // Windows
#include <azure/core/http/http.hpp>
#include "transport_adapter.hpp"
#include <string>
namespace Azure { namespace Core { namespace Test {
namespace Datails {
constexpr int64_t c_fileSize = 1024 * 100;
}
void TransportAdapter::checkResponseCode(
Azure::Core::Http::HttpStatusCode code,
Azure::Core::Http::HttpStatusCode expectedCode)
{
EXPECT_PRED2(
[](Azure::Core::Http::HttpStatusCode a, Azure::Core::Http::HttpStatusCode b) {
return a == b;
},
code,
expectedCode);
}
void TransportAdapter::CheckBodyFromBuffer(
Azure::Core::Http::RawResponse& response,
int64_t size,
std::string expectedBody)
{
auto body = response.GetBodyStream();
EXPECT_EQ(body, nullptr);
std::vector<uint8_t> bodyVector = response.GetBody();
int64_t bodySize = bodyVector.size();
if (size > 0)
{ // only for known body size
EXPECT_EQ(bodySize, size);
}
if (expectedBody.size() > 0)
{
auto bodyString = std::string(bodyVector.begin(), bodyVector.end());
EXPECT_STREQ(expectedBody.data(), bodyString.data());
}
}
void TransportAdapter::CheckBodyFromStream(
Azure::Core::Http::RawResponse& response,
int64_t size,
std::string expectedBody)
{
auto body = response.GetBodyStream();
EXPECT_NE(body, nullptr);
std::vector<uint8_t> bodyVector = Azure::Core::Http::BodyStream::ReadToEnd(context, *body);
int64_t bodySize = body->Length();
EXPECT_EQ(bodySize, size);
bodySize = bodyVector.size();
if (size > 0)
{ // only for known body size
EXPECT_EQ(bodyVector.size(), size);
}
if (expectedBody.size() > 0)
{
auto bodyString = std::string(bodyVector.begin(), bodyVector.end());
EXPECT_STREQ(expectedBody.data(), bodyString.data());
}
}
TEST_F(TransportAdapter, SizePutFromFile)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
std::string testDataPath(AZURE_TEST_DATA_PATH);
#ifdef POSIX
testDataPath.append("/fileData");
int f = open(testDataPath.data(), O_RDONLY);
#endif
#ifdef WINDOWS
testDataPath.append("\\fileData");
HANDLE f = CreateFile(
testDataPath.data(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
#endif
auto requestBodyStream
= Azure::Core::Http::FileBodyStream(f, 0, Azure::Core::Test::Datails::c_fileSize);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &requestBodyStream, true);
// Make transport adapter to read all stream content for uploading instead of chunks
request.SetUploadChunkSize(Azure::Core::Test::Datails::c_fileSize);
{
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
TEST_F(TransportAdapter, SizePutFromFileDefault)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
std::string testDataPath(AZURE_TEST_DATA_PATH);
#ifdef POSIX
testDataPath.append("/fileData");
int f = open(testDataPath.data(), O_RDONLY);
#endif
#ifdef WINDOWS
testDataPath.append("\\fileData");
HANDLE f = CreateFile(
testDataPath.data(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
#endif
auto requestBodyStream
= Azure::Core::Http::FileBodyStream(f, 0, Azure::Core::Test::Datails::c_fileSize);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &requestBodyStream, true);
// Make transport adapter to read default chunk size
{
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
TEST_F(TransportAdapter, SizePutFromFileBiggerPage)
{
Azure::Core::Http::Url host("http://httpbin.org/put");
std::string testDataPath(AZURE_TEST_DATA_PATH);
#ifdef POSIX
testDataPath.append("/fileData");
int f = open(testDataPath.data(), O_RDONLY);
#endif
#ifdef WINDOWS
testDataPath.append("\\fileData");
HANDLE f = CreateFile(
testDataPath.data(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
#endif
auto requestBodyStream
= Azure::Core::Http::FileBodyStream(f, 0, Azure::Core::Test::Datails::c_fileSize);
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, host, &requestBodyStream, true);
// Make transport adapter to read more than file size (5Mb)
request.SetUploadChunkSize(Azure::Core::Test::Datails::c_fileSize * 5);
{
auto response = pipeline.Send(context, request);
checkResponseCode(response->GetStatusCode());
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
CheckBodyFromStream(*response, expectedResponseBodySize);
}
}
}}} // namespace Azure::Core::Test