Telemetry policy (#264)

This commit is contained in:
Anton Kolesnyk 2020-07-17 17:03:47 -07:00 committed by GitHub
parent 6122529bac
commit b259db5a3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 330 additions and 29 deletions

View File

@ -25,6 +25,7 @@ add_library (
src/http/request.cpp
src/http/raw_response.cpp
src/http/retry_policy.cpp
src/http/telemetry_policy.cpp
src/http/url.cpp
src/http/winhttp/win_http_transport.cpp
src/strings.cpp

View File

@ -24,40 +24,33 @@ namespace Azure { namespace Core { namespace Credentials { namespace Policy {
void operator=(BearerTokenAuthenticationPolicy const&) = delete;
public:
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
std::string const& scope)
: m_credential(credential)
explicit BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> credential,
std::string scope)
: m_credential(std::move(credential))
{
m_scopes.push_back(scope);
m_scopes.emplace_back(std::move(scope));
}
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
std::vector<std::string> const& scopes)
: m_credential(credential), m_scopes(scopes)
{
}
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
std::vector<std::string> const&& scopes)
: m_credential(credential), m_scopes(std::move(scopes))
explicit BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> credential,
std::vector<std::string> scopes)
: m_credential(std::move(credential)), m_scopes(std::move(scopes))
{
}
template <typename ScopesIterator>
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
explicit BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> credential,
ScopesIterator const& scopesBegin,
ScopesIterator const& scopesEnd)
: m_credential(credential), m_scopes(scopesBegin, scopesEnd)
: m_credential(std::move(credential)), m_scopes(scopesBegin, scopesEnd)
{
}
HttpPolicy* Clone() const override
std::unique_ptr<HttpPolicy> Clone() const override
{
return new BearerTokenAuthenticationPolicy(m_credential, m_scopes);
return std::make_unique<BearerTokenAuthenticationPolicy>(m_credential, m_scopes);
}
std::unique_ptr<Http::RawResponse> Send(

View File

@ -25,7 +25,7 @@ namespace Azure { namespace Core { namespace Http {
Request& request,
NextHttpPolicy policy) const = 0;
virtual ~HttpPolicy() {}
virtual HttpPolicy* Clone() const = 0;
virtual std::unique_ptr<HttpPolicy> Clone() const = 0;
protected:
HttpPolicy() = default;
@ -59,7 +59,10 @@ namespace Azure { namespace Core { namespace Http {
{
}
HttpPolicy* Clone() const override { return new TransportPolicy(m_transport); }
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<TransportPolicy>(m_transport);
}
std::unique_ptr<RawResponse> Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy)
const override
@ -96,18 +99,23 @@ namespace Azure { namespace Core { namespace Http {
public:
explicit RetryPolicy(RetryOptions options) : m_retryOptions(std::move(options)) {}
HttpPolicy* Clone() const override { return new RetryPolicy(m_retryOptions); }
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<RetryPolicy>(*this);
}
std::unique_ptr<RawResponse> Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy)
const override;
};
class RequestIdPolicy : public HttpPolicy {
public:
explicit RequestIdPolicy() {}
HttpPolicy* Clone() const override { return new RequestIdPolicy(); }
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<RequestIdPolicy>(*this);
}
std::unique_ptr<RawResponse> Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy)
const override
@ -117,4 +125,37 @@ namespace Azure { namespace Core { namespace Http {
}
};
class TelemetryPolicy : public HttpPolicy {
std::string m_telemetryId;
static std::string const g_emptyApplicationId;
static std::string BuildTelemetryId(
std::string const& componentName,
std::string const& componentVersion,
std::string const& applicationId);
public:
explicit TelemetryPolicy(std::string const& componentName, std::string const& componentVersion)
: TelemetryPolicy(componentName, componentVersion, g_emptyApplicationId)
{
}
explicit TelemetryPolicy(
std::string const& componentName,
std::string const& componentVersion,
std::string const& applicationId)
: m_telemetryId(BuildTelemetryId(componentName, componentVersion, applicationId))
{
}
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<TelemetryPolicy>(*this);
}
std::unique_ptr<RawResponse> Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy)
const override;
};
}}} // namespace Azure::Core::Http

View File

@ -0,0 +1,142 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <http/policy.hpp>
#include <sstream>
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace {
std::string GetOSVersion()
{
std::ostringstream osVersionInfo;
{
HKEY regKey{};
auto regKeyOpened = false;
try
{
if (RegOpenKeyExA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
0,
KEY_READ,
&regKey)
== ERROR_SUCCESS)
{
regKeyOpened = true;
auto first = true;
static constexpr char const* regValues[]{
"ProductName", "CurrentVersion", "CurrentBuildNumber", "BuildLabEx"};
for (auto regValue : regValues)
{
char valueBuf[200] = {};
DWORD valueBufSize = sizeof(valueBuf);
if (RegQueryValueExA(regKey, regValue, NULL, NULL, (LPBYTE)valueBuf, &valueBufSize)
== ERROR_SUCCESS)
{
if (valueBufSize > 0)
{
osVersionInfo << (first ? "" : " ")
<< std::string(valueBuf, valueBuf + (valueBufSize - 1));
first = false;
}
}
}
RegCloseKey(regKey);
}
}
catch (...)
{
if (regKeyOpened)
{
RegCloseKey(regKey);
}
throw;
}
}
return osVersionInfo.str();
}
} // namespace
#else
#include <sys/utsname.h>
namespace {
std::string GetOSVersion()
{
std::ostringstream osVersionInfo;
{
utsname sysInfo{};
if (uname(&sysInfo) == 0)
{
osVersionInfo << sysInfo.sysname << " " << sysInfo.release << " " << sysInfo.machine << " "
<< sysInfo.version;
}
}
return osVersionInfo.str();
}
} // namespace
#endif
namespace {
std::string TrimString(std::string s)
{
auto const isSpace = [](int c) { return !std::isspace(c); };
s.erase(s.begin(), std::find_if(s.begin(), s.end(), isSpace));
s.erase(std::find_if(s.rbegin(), s.rend(), isSpace).base(), s.end());
return s;
}
} // namespace
using namespace Azure::Core::Http;
std::string const TelemetryPolicy::g_emptyApplicationId;
std::string TelemetryPolicy::BuildTelemetryId(
std::string const& componentName,
std::string const& componentVersion,
std::string const& applicationId)
{
// Spec: https://azure.github.io/azure-sdk/general_azurecore.html#telemetry-policy
std::ostringstream telemetryId;
if (!applicationId.empty())
{
telemetryId << TrimString(applicationId).substr(0, 24) << " ";
}
static std::string const osVer = GetOSVersion();
telemetryId << "azsdk-cpp-" << componentName << "/" << componentVersion << " (" << osVer << ")";
return telemetryId.str();
}
std::unique_ptr<RawResponse> TelemetryPolicy::Send(
Context& ctx,
Request& request,
NextHttpPolicy nextHttpPolicy) const
{
request.AddHeader("User-Agent", m_telemetryId);
return nextHttpPolicy.Send(ctx, request);
}

View File

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <algorithm>
#include <azure.hpp>

View File

@ -15,6 +15,7 @@ add_executable (
main.cpp
nullable.cpp
string.cpp
telemetry_policy.cpp
transport_adapter.cpp
transport_adapter.hpp
)

View File

@ -0,0 +1,111 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "gtest/gtest.h"
#include <http/pipeline.hpp>
#include <http/policy.hpp>
using namespace Azure::Core;
using namespace Azure::Core::Http;
namespace {
class NoOpPolicy : public HttpPolicy {
std::unique_ptr<RawResponse> Send(Context& context, Request& request, NextHttpPolicy policy)
const override
{
(void)context;
(void)request;
(void)policy;
return std::unique_ptr<RawResponse>();
};
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<NoOpPolicy>(*this);
};
};
} // namespace
TEST(TelemetryPolicy, telemetryString)
{
std::vector<std::unique_ptr<HttpPolicy>> policy1;
std::vector<std::unique_ptr<HttpPolicy>> policy2;
std::vector<std::unique_ptr<HttpPolicy>> policy3;
std::vector<std::unique_ptr<HttpPolicy>> policy4;
std::string const expected1 = "azsdk-cpp-storage-blob/11.0.0 (";
policy1.emplace_back(std::make_unique<TelemetryPolicy>("storage-blob", "11.0.0"));
policy1.emplace_back(std::make_unique<NoOpPolicy>());
HttpPipeline pipeline1(policy1);
std::string const expected2 = "AzCopy/10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 (";
policy2.emplace_back(
std::make_unique<TelemetryPolicy>("storage-blob", "11.0.0", "AzCopy/10.0.4-Preview"));
policy2.emplace_back(std::make_unique<NoOpPolicy>());
HttpPipeline pipeline2(policy2);
std::string const expected3 = "AzCopy / 10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 (";
policy3.emplace_back(
std::make_unique<TelemetryPolicy>("storage-blob", "11.0.0", " AzCopy / 10.0.4-Preview "));
policy3.emplace_back(std::make_unique<NoOpPolicy>());
HttpPipeline pipeline3(policy3);
std::string const expected4 = "01234567890123456789abcd azsdk-cpp-storage-blob/11.0.0 (";
policy4.emplace_back(
std::make_unique<TelemetryPolicy>("storage-blob", "11.0.0", " 01234567890123456789abcde "));
policy4.emplace_back(std::make_unique<NoOpPolicy>());
HttpPipeline pipeline4(policy4);
constexpr auto TelemetryHeader = "user-agent";
constexpr auto ClosingBrace = ')';
constexpr auto OSInfoMin = 10;
auto request1 = Request(HttpMethod::Get, "https://www.microsoft.com");
auto request2 = Request(HttpMethod::Get, "https://www.microsoft.com");
auto request3 = Request(HttpMethod::Get, "https://www.microsoft.com");
auto request4 = Request(HttpMethod::Get, "https://www.microsoft.com");
Context context;
pipeline1.Send(context, request1);
pipeline2.Send(context, request2);
pipeline3.Send(context, request3);
pipeline4.Send(context, request4);
auto const headers1 = request1.GetHeaders();
auto const headers2 = request2.GetHeaders();
auto const headers3 = request3.GetHeaders();
auto const headers4 = request4.GetHeaders();
auto telemetryHeader1 = headers1.find(TelemetryHeader);
auto telemetryHeader2 = headers2.find(TelemetryHeader);
auto telemetryHeader3 = headers3.find(TelemetryHeader);
auto telemetryHeader4 = headers4.find(TelemetryHeader);
EXPECT_NE(telemetryHeader1, headers1.end());
EXPECT_NE(telemetryHeader2, headers2.end());
EXPECT_NE(telemetryHeader3, headers3.end());
EXPECT_NE(telemetryHeader4, headers4.end());
auto const actualValue1 = telemetryHeader1->second;
auto const actualValue2 = telemetryHeader2->second;
auto const actualValue3 = telemetryHeader3->second;
auto const actualValue4 = telemetryHeader4->second;
EXPECT_GE(actualValue1.size(), expected1.size() + OSInfoMin + sizeof(ClosingBrace));
EXPECT_GE(actualValue2.size(), expected2.size() + OSInfoMin + sizeof(ClosingBrace));
EXPECT_GE(actualValue3.size(), expected3.size() + OSInfoMin + sizeof(ClosingBrace));
EXPECT_GE(actualValue4.size(), expected4.size() + OSInfoMin + sizeof(ClosingBrace));
EXPECT_EQ(actualValue1[actualValue1.size() - 1], ClosingBrace);
EXPECT_EQ(actualValue2[actualValue2.size() - 1], ClosingBrace);
EXPECT_EQ(actualValue3[actualValue3.size() - 1], ClosingBrace);
EXPECT_EQ(actualValue4[actualValue4.size() - 1], ClosingBrace);
EXPECT_EQ(actualValue1.substr(0, expected1.size()), expected1);
EXPECT_EQ(actualValue2.substr(0, expected2.size()), expected2);
EXPECT_EQ(actualValue3.substr(0, expected3.size()), expected3);
EXPECT_EQ(actualValue4.substr(0, expected4.size()), expected4);
}

View File

@ -12,7 +12,10 @@ namespace Azure { namespace Storage {
explicit CommonHeadersRequestPolicy() {}
~CommonHeadersRequestPolicy() override {}
HttpPolicy* Clone() const override { return new CommonHeadersRequestPolicy(*this); }
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<CommonHeadersRequestPolicy>(*this);
}
std::unique_ptr<Core::Http::RawResponse> Send(
Core::Context& ctx,

View File

@ -17,7 +17,10 @@ namespace Azure { namespace Storage {
~SharedKeyPolicy() override {}
HttpPolicy* Clone() const override { return new SharedKeyPolicy(m_credential); }
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<SharedKeyPolicy>(m_credential);
}
std::unique_ptr<Core::Http::RawResponse> Send(
Core::Context& ctx,

View File

@ -17,7 +17,10 @@ namespace Azure { namespace Storage {
~TokenCredentialPolicy() override {}
HttpPolicy* Clone() const override { return new TokenCredentialPolicy(m_credential); }
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<TokenCredentialPolicy>(m_credential);
}
std::unique_ptr<Core::Http::RawResponse> Send(
Core::Context& ctx,