88#include < aws/core/auth/signer/AWSAuthSignerHelper.h>
99
1010#include < aws/core/auth/AWSCredentialsProvider.h>
11+ #include < aws/core/client/UserAgent.h>
1112#include < aws/core/http/HttpRequest.h>
1213#include < aws/core/http/URI.h>
1314#include < aws/core/utils/DateTime.h>
2526
2627#include < iomanip>
2728#include < cstring>
29+ #include < numeric>
2830
2931using namespace Aws ;
3032using namespace Aws ::Client;
@@ -81,6 +83,8 @@ bool AWSAuthV4Signer::SignRequestWithSigV4a(Aws::Http::HttpRequest& request, con
8183 bool signBody, long long expirationTimeInSeconds, Aws::Crt::Auth::SignatureType signatureType) const
8284{
8385 AWSCredentials credentials = GetCredentials (request.GetServiceSpecificParameters ());
86+
87+ UpdateUserAgentWithCredentialFeatures (request, credentials.GetContext ());
8488 auto crtCredentials = Aws::MakeShared<Aws::Crt::Auth::Credentials>(v4AsymmetricLogTag,
8589 Aws::Crt::ByteCursorFromCString (credentials.GetAWSAccessKeyId ().c_str ()),
8690 Aws::Crt::ByteCursorFromCString (credentials.GetAWSSecretKey ().c_str ()),
@@ -336,6 +340,9 @@ bool AWSAuthV4Signer::SignRequestWithCreds(Aws::Http::HttpRequest& request, cons
336340bool AWSAuthV4Signer::SignRequest (Aws::Http::HttpRequest& request, const char * region, const char * serviceName, bool signBody) const
337341{
338342 AWSCredentials credentials = GetCredentials (request.GetServiceSpecificParameters ());
343+
344+ UpdateUserAgentWithCredentialFeatures (request, credentials.GetContext ());
345+
339346 return SignRequestWithCreds (request, credentials, region, serviceName, signBody);
340347}
341348
@@ -464,6 +471,9 @@ bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, const Aws:
464471bool AWSAuthV4Signer::PresignRequest (Aws::Http::HttpRequest& request, const char * region, const char * serviceName, long long expirationTimeInSeconds) const
465472{
466473 AWSCredentials credentials = GetCredentials (request.GetServiceSpecificParameters ());
474+
475+ UpdateUserAgentWithCredentialFeatures (request, credentials.GetContext ());
476+
467477 return PresignRequest (request, credentials, region,serviceName, expirationTimeInSeconds );
468478}
469479
@@ -595,3 +605,56 @@ Aws::Auth::AWSCredentials AWSAuthV4Signer::GetCredentials(const std::shared_ptr<
595605 AWS_UNREFERENCED_PARAM (serviceSpecificParameters);
596606 return m_credentialsProvider->GetAWSCredentials ();
597607}
608+
609+ void AWSAuthV4Signer::UpdateUserAgentWithCredentialFeatures (Aws::Http::HttpRequest& request, const Aws::Auth::CredentialsResolutionContext& context) const {
610+ if (!request.HasHeader (USER_AGENT)) {
611+ AWS_LOGSTREAM_DEBUG (v4LogTag, " Request does not have User-Agent header, skipping credential feature update" );
612+ return ;
613+ }
614+
615+ if (context.HasCustomUserAgent ()) {
616+ AWS_LOGSTREAM_DEBUG (v4LogTag, " Custom User-Agent detected, skipping credential feature update" );
617+ return ;
618+ }
619+
620+ const auto features = context.GetUserAgentFeatures ();
621+ if (features.empty ()) {
622+ AWS_LOGSTREAM_DEBUG (v4LogTag, " No credential features to add to User-Agent" );
623+ return ;
624+ }
625+
626+ std::vector<Aws::String> businessMetrics (features.size ());
627+ std::transform (features.begin (),
628+ features.end (),
629+ businessMetrics.begin (),
630+ [](UserAgentFeature feature) -> Aws::String { return UserAgent::BusinessMetricForFeature (feature); });
631+
632+ const auto credentialFeatures = std::accumulate (std::next (businessMetrics.begin ()),
633+ businessMetrics.end (),
634+ businessMetrics.front (),
635+ [](const Aws::String& a, const Aws::String& b) {
636+ return a + " ," + b;
637+ });
638+
639+ const auto userAgent = request.GetHeaderValue (USER_AGENT);
640+ auto userAgentParsed = Aws::Utils::StringUtils::Split (userAgent, ' ' );
641+ auto metricsSegment = std::find_if (userAgentParsed.begin (), userAgentParsed.end (),
642+ [](const Aws::String& value) { return value.find (" m/" ) != Aws::String::npos; });
643+
644+ if (metricsSegment != userAgentParsed.end ()) {
645+ // Add new metrics to existing metrics section
646+ *metricsSegment = Aws::String{*metricsSegment + " ," + credentialFeatures};
647+ } else {
648+ // No metrics section exists, add new one
649+ userAgentParsed.push_back (" m/" + credentialFeatures);
650+ }
651+
652+ // Reassemble all parts with spaces
653+ const auto newUserAgent = std::accumulate (std::next (userAgentParsed.begin ()),
654+ userAgentParsed.end (),
655+ userAgentParsed.front (),
656+ [](const Aws::String& a, const Aws::String& b) {
657+ return a + " " + b;
658+ });
659+ request.SetUserAgent (newUserAgent);
660+ }
0 commit comments