2828import java .util .Optional ;
2929import software .amazon .awssdk .annotations .SdkInternalApi ;
3030import software .amazon .awssdk .core .ApiName ;
31+ import software .amazon .awssdk .core .RequestOverrideConfiguration ;
32+ import software .amazon .awssdk .core .SelectedAuthScheme ;
3133import software .amazon .awssdk .core .client .config .SdkAdvancedClientOption ;
3234import software .amazon .awssdk .core .client .config .SdkClientConfiguration ;
3335import software .amazon .awssdk .core .client .config .SdkClientOption ;
3436import software .amazon .awssdk .core .interceptor .ExecutionAttributes ;
37+ import software .amazon .awssdk .core .interceptor .SdkExecutionAttribute ;
3538import software .amazon .awssdk .core .interceptor .SdkInternalExecutionAttribute ;
3639import software .amazon .awssdk .core .internal .http .HttpClientDependencies ;
3740import software .amazon .awssdk .core .internal .http .RequestExecutionContext ;
3841import software .amazon .awssdk .core .internal .http .pipeline .MutableRequestToRequestPipeline ;
42+ import software .amazon .awssdk .core .signer .Signer ;
3943import software .amazon .awssdk .core .useragent .AdditionalMetadata ;
4044import software .amazon .awssdk .core .useragent .BusinessMetricCollection ;
45+ import software .amazon .awssdk .core .useragent .BusinessMetricFeatureId ;
4146import software .amazon .awssdk .http .SdkHttpFullRequest ;
47+ import software .amazon .awssdk .http .auth .aws .scheme .AwsV4aAuthScheme ;
4248import software .amazon .awssdk .identity .spi .Identity ;
4349import software .amazon .awssdk .utils .CompletableFutureUtils ;
4450import software .amazon .awssdk .utils .Logger ;
@@ -112,7 +118,8 @@ private String finalizeUserAgent(RequestExecutionContext context) {
112118 userAgentMetadata .forEach (s -> javaUserAgent .append (SPACE ).append (s ));
113119 }
114120
115- Optional <String > businessMetrics = getBusinessMetricsString (context .executionAttributes (), groupedApiNames .right ());
121+ Optional <String > businessMetrics = getBusinessMetricsString (context .executionAttributes (),
122+ groupedApiNames .right (), context );
116123 businessMetrics .ifPresent (
117124 metrics -> appendSpaceAndField (javaUserAgent , BUSINESS_METADATA , metrics )
118125 );
@@ -143,15 +150,20 @@ private static Pair<List<ApiName>, Collection<String>> groupApiNames(List<ApiNam
143150 }
144151
145152 private static Optional <String > getBusinessMetricsString (ExecutionAttributes executionAttributes ,
146- Collection <String > metricsFromApiNames ) {
153+ Collection <String > metricsFromApiNames ,
154+ RequestExecutionContext context ) {
147155 BusinessMetricCollection businessMetrics =
148156 executionAttributes .getAttribute (SdkInternalExecutionAttribute .BUSINESS_METRICS );
149157 if (businessMetrics == null ) {
150158 businessMetrics = new BusinessMetricCollection ();
151159 }
152160 businessMetrics .merge (metricsFromApiNames );
153161
154- credentialProviderBusinessMetrics (executionAttributes ).ifPresent (businessMetrics ::merge );
162+ SelectedAuthScheme <?> selectedAuthScheme =
163+ executionAttributes .getAttribute (SdkInternalExecutionAttribute .SELECTED_AUTH_SCHEME );
164+
165+ credentialProviderBusinessMetrics (selectedAuthScheme ).ifPresent (businessMetrics ::merge );
166+ addSigV4aBusinessMetrics (selectedAuthScheme , businessMetrics , context , executionAttributes );
155167
156168 if (businessMetrics .recordedMetrics ().isEmpty ()) {
157169 return Optional .empty ();
@@ -161,11 +173,9 @@ private static Optional<String> getBusinessMetricsString(ExecutionAttributes exe
161173 }
162174
163175 private static Optional <Collection <String >> credentialProviderBusinessMetrics (
164- ExecutionAttributes executionAttributes ) {
165- return Optional .ofNullable (
166- executionAttributes .getAttribute (SdkInternalExecutionAttribute .SELECTED_AUTH_SCHEME ))
167- .map (selectedAuthScheme ->
168- CompletableFutureUtils .joinLikeSync (selectedAuthScheme .identity ()))
176+ SelectedAuthScheme <?> selectedAuthScheme ) {
177+ return Optional .ofNullable (selectedAuthScheme )
178+ .map (scheme -> CompletableFutureUtils .joinLikeSync (scheme .identity ()))
169179 .flatMap (Identity ::providerName )
170180 .map (providerName -> {
171181 if (StringUtils .isBlank (providerName )) {
@@ -175,6 +185,26 @@ private static Optional<Collection<String>> credentialProviderBusinessMetrics(
175185 });
176186 }
177187
188+ private static boolean isSignerOverridden (RequestExecutionContext context , ExecutionAttributes executionAttributes ) {
189+ boolean isClientSignerOverridden =
190+ Boolean .TRUE .equals (executionAttributes .getAttribute (SdkExecutionAttribute .SIGNER_OVERRIDDEN ));
191+ Optional <Signer > requestSigner = context .originalRequest ().overrideConfiguration ()
192+ .flatMap (RequestOverrideConfiguration ::signer );
193+ return isClientSignerOverridden || requestSigner .isPresent ();
194+ }
195+
196+ private static void addSigV4aBusinessMetrics (SelectedAuthScheme <?> selectedAuthScheme ,
197+ BusinessMetricCollection businessMetrics ,
198+ RequestExecutionContext context ,
199+ ExecutionAttributes executionAttributes ) {
200+ if (selectedAuthScheme != null &&
201+ selectedAuthScheme .authSchemeOption ().schemeId ().equals (AwsV4aAuthScheme .SCHEME_ID ) &&
202+ !isSignerOverridden (context , executionAttributes )) {
203+
204+ businessMetrics .addMetric (BusinessMetricFeatureId .SIGV4A_SIGNING .value ());
205+ }
206+ }
207+
178208 /**
179209 * This structure is used for external users as well as for internal tracking of features.
180210 * It's not governed by a specification.
0 commit comments