Telemetry policy (#264)
This commit is contained in:
parent
6122529bac
commit
b259db5a3b
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
142
sdk/core/azure-core/src/http/telemetry_policy.cpp
Normal file
142
sdk/core/azure-core/src/http/telemetry_policy.cpp
Normal 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,
|
||||
®Key)
|
||||
== 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);
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <algorithm>
|
||||
#include <azure.hpp>
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ add_executable (
|
||||
main.cpp
|
||||
nullable.cpp
|
||||
string.cpp
|
||||
telemetry_policy.cpp
|
||||
transport_adapter.cpp
|
||||
transport_adapter.hpp
|
||||
)
|
||||
|
||||
111
sdk/core/azure-core/test/ut/telemetry_policy.cpp
Normal file
111
sdk/core/azure-core/test/ut/telemetry_policy.cpp
Normal 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);
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user