Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposing new constructor to blob_client. #54

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions include/blob/blob_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,31 @@ namespace azure { namespace storage_lite {
/// </summary>
/// <param name="account">An existing <see cref="azure::storage_lite::storage_account" /> object.</param>
/// <param name="max_concurrency">An int value indicates the maximum concurrency expected during execute requests against the service.</param>
blob_client(std::shared_ptr<storage_account> account, int max_concurrency)
: m_account(account)
/// <param name="ca_path">A string value with absolute path to CA bundle location.</param>
blob_client(std::shared_ptr<storage_account> account, int max_concurrency, const std::string& ca_path = "")
: blob_client(account,
std::make_shared<executor_context>(std::make_shared<tinyxml2_parser>(), std::make_shared<retry_policy>()),
(ca_path.empty() ? std::make_shared<CurlEasyClient>(max_concurrency)
: std::make_shared<CurlEasyClient>(max_concurrency, ca_path)))
{
m_context = std::make_shared<executor_context>(std::make_shared<tinyxml2_parser>(), std::make_shared<retry_policy>());
m_client = std::make_shared<CurlEasyClient>(max_concurrency);
}

/// <summary>
/// Initializes a new instance of the <see cref="azure::storage_lite::blob_client" /> class.
/// </summary>
/// <param name="account">An existing <see cref="azure::storage_lite::storage_account" /> object.</param>
/// <param name="context">A context of execution defining how to parser and the retry policy <see cref="azure::storage_lite::executor_context" /> class.
/// <param name="max_concurrency">An int value indicates the maximum concurrency expected during execute requests against the service.</param>
/// <param name="ca_path">A string value with absolute path to CA bundle location.</param>
blob_client(std::shared_ptr<storage_account> account, int max_concurrency, const std::string& ca_path)
: m_account(account)
blob_client(std::shared_ptr<storage_account> account,
std::shared_ptr<executor_context> context,
int max_concurrency,
const std::string ca_path = "")
: blob_client(account,
context,
(ca_path.empty() ? std::make_shared<CurlEasyClient>(max_concurrency)
: std::make_shared<CurlEasyClient>(max_concurrency, ca_path)))
{
m_context = std::make_shared<executor_context>(std::make_shared<tinyxml2_parser>(), std::make_shared<retry_policy>());
m_client = std::make_shared<CurlEasyClient>(max_concurrency, ca_path);
}

/// <summary>
Expand Down Expand Up @@ -321,6 +328,15 @@ namespace azure { namespace storage_lite {
AZURE_STORAGE_API std::future<storage_outcome<void>> start_copy(const std::string &sourceContainer, const std::string &sourceBlob, const std::string &destContainer, const std::string &destBlob);

private:
blob_client(std::shared_ptr<storage_account> account,
std::shared_ptr<executor_context> context,
std::shared_ptr<CurlEasyClient> client)
: m_client(client),
m_account(account),
m_context(context)
{
}

std::shared_ptr<CurlEasyClient> m_client;
std::shared_ptr<storage_account> m_account;
std::shared_ptr<executor_context> m_context;
Expand Down
3 changes: 3 additions & 0 deletions include/constants.dat
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ DAT(header_value_storage_version, "2018-11-09")

DAT(header_value_user_agent, "azure-storage-cpplite/0.1.0")

DAT_TYPED(int, maximum_retries, 3)
DAT_TYPED(int, base_timeout, 10)

DAT(date_format_rfc_1123, "%a, %d %b %Y %H:%M:%S GMT")
DAT(date_format_iso_8601, "%Y-%m-%dT%H:%M:%SZ")

Expand Down
4 changes: 3 additions & 1 deletion include/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace azure { namespace storage_lite { namespace constants {

#define DAT(x, y) extern AZURE_STORAGE_API const char *x; const int x ## _size{ sizeof(y) / sizeof(char) - 1 };
#define DAT_TYPED(type, name, value) extern AZURE_STORAGE_API const type name;
#include "constants.dat"
#undef DAT
#undef DAT_TYPED

}}} // azure::storage_lite::constants
}}} // azure::storage_lite::constants
2 changes: 1 addition & 1 deletion include/get_container_property_request_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace azure { namespace storage_lite {
m_valid = valid;
}

bool valid()
bool valid() const
{
return m_valid;
}
Expand Down
34 changes: 24 additions & 10 deletions include/retry.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "storage_EXPORTS.h"

#include "constants.h"
#include "http_base.h"
#include "utility.h"

Expand Down Expand Up @@ -78,27 +79,40 @@ namespace azure { namespace storage_lite {
class retry_policy final : public retry_policy_base
{
public:

retry_policy(const int maximum_retries = azure::storage_lite::constants::maximum_retries,
const std::chrono::seconds base_timeout = std::chrono::seconds{azure::storage_lite::constants::base_timeout})
: m_maximum_retries(maximum_retries),
m_base_timeout(base_timeout)
{
}

retry_info evaluate(const retry_context &context) const override
{
if (context.numbers() == 0)
{
return retry_info(true, std::chrono::seconds(0));
}
else if (context.numbers() < 26 && can_retry(context.result()))
if (context.numbers() >= m_maximum_retries ||
!can_retry(context.result()))
{
double delay = (pow(1.2, context.numbers()-1)-1);
delay = std::min(delay, 60.0); // Maximum backoff delay of 1 minute
delay *= (((double)rand())/RAND_MAX)/2 + 0.75;
return retry_info(true, std::chrono::seconds((int)delay));
return retry_info(false, std::chrono::seconds(0));
}
return retry_info(false, std::chrono::seconds(0));

return retry_info(true, calculate_new_delay(context));
}

private:
bool can_retry(http_base::http_code code) const
{
return retryable(code);
}

std::chrono::seconds calculate_new_delay(const retry_context &context) const
{
// (1 << N) == 2^N
return ((context.numbers() == 0) ? std::chrono::seconds(0)
: (m_base_timeout * (1 << context.numbers())));
}

const int m_maximum_retries;
const std::chrono::seconds m_base_timeout;
};

}} // azure::storage_lite
Expand Down
4 changes: 4 additions & 0 deletions include/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <string>
#include <limits>
#include <map>
#include <vector>

#include "storage_EXPORTS.h"

Expand All @@ -28,6 +30,8 @@ namespace azure { namespace storage_lite {

AZURE_STORAGE_API std::string get_http_verb(http_base::http_method method);

AZURE_STORAGE_API std::vector<std::pair<std::string, std::string>> filter_headers(const std::map<std::string, std::string, case_insensitive_compare>& headers, const std::string& value);

inline void add_optional_query(storage_url &url, const std::string &name, unsigned int value)
{
if (value > 0)
Expand Down
23 changes: 4 additions & 19 deletions src/blob/blob_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ storage_outcome<chunk_property> blob_client::get_chunk_to_stream_sync(const std:
property.etag = http->get_response_header(constants::header_etag);
property.totalSize = get_length_from_content_range(http->get_response_header(constants::header_content_range));
std::istringstream(http->get_response_header(constants::header_content_length)) >> property.size;
property.last_modified = curl_getdate(http->get_response_header(constants::header_last_modified).c_str(), NULL);
property.last_modified = curl_getdate(http->get_response_header(constants::header_last_modified).c_str(), nullptr);
return storage_outcome<chunk_property>(property);
}
return storage_outcome<chunk_property>(storage_error(response.error()));
Expand Down Expand Up @@ -196,14 +196,7 @@ std::future<storage_outcome<container_property>> blob_client::get_container_prop
container_property properties(true);
properties.etag = http->get_response_header(constants::header_etag);

auto& headers = http->get_response_headers();
for (auto iter = headers.begin(); iter != headers.end(); ++iter)
{
if (iter->first.find(constants::header_ms_meta_prefix) == 0)
{
properties.metadata.push_back(std::make_pair(iter->first.substr(constants::header_ms_meta_prefix_size), iter->second));
}
}
properties.metadata = filter_headers(http->get_response_headers(), constants::header_ms_meta_prefix);
return storage_outcome<container_property>(properties);
}
else
Expand Down Expand Up @@ -275,23 +268,15 @@ std::future<storage_outcome<blob_property>> blob_client::get_blob_properties(con
properties.content_type = http->get_response_header(constants::header_content_type);
properties.etag = http->get_response_header(constants::header_etag);
properties.copy_status = http->get_response_header(constants::header_ms_copy_status);
properties.last_modified = curl_getdate(http->get_response_header(constants::header_last_modified).c_str(), NULL);
properties.last_modified = curl_getdate(http->get_response_header(constants::header_last_modified).c_str(), nullptr);
std::string::size_type sz = 0;
std::string contentLength = http->get_response_header(constants::header_content_length);
if (contentLength.length() > 0)
{
properties.size = std::stoull(contentLength, &sz, 0);
}

auto& headers = http->get_response_headers();
for (auto iter = headers.begin(); iter != headers.end(); ++iter)
{
if (iter->first.find(constants::header_ms_meta_prefix) == 0)
{
// We need to strip ten characters from the front of the key to account for "x-ms-meta-".
properties.metadata.push_back(std::make_pair(iter->first.substr(constants::header_ms_meta_prefix_size), iter->second));
}
}
properties.metadata = filter_headers(http->get_response_headers(), constants::header_ms_meta_prefix);
return storage_outcome<blob_property>(properties);
}
else
Expand Down
2 changes: 2 additions & 0 deletions src/constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace azure { namespace storage_lite { namespace constants {

#define DAT(x, y) const char *x{ y };
#define DAT_TYPED(type, name, value) const type name{value};
#include "constants.dat"
#undef DAT
#undef DAT_TYPED

}}} // azure::storage_lite
15 changes: 15 additions & 0 deletions src/utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ namespace azure { namespace storage_lite {
return std::string();
}

std::vector<std::pair<std::string, std::string>> filter_headers(const std::map<std::string, std::string, case_insensitive_compare>& headers,
const std::string& value)
{
std::vector<std::pair<std::string, std::string>> filteredHeaders;
filteredHeaders.reserve(headers.size());
for (const auto& header : headers)
{
if (header.first.find(value) == 0)
{
filteredHeaders.emplace_back(header.first.substr(value.size()), header.second);
}
}
return filteredHeaders;
}

void add_access_condition_headers(http_base &h, storage_headers &headers, const blob_request_base &r)
{
if (!r.if_modified_since().empty())
Expand Down