Skip to content

Commit 896f1c5

Browse files
committed
Fix test failures from auth scheme resolution refactoring: preserve interceptor signer properties and reorder SIGNING_METHOD resolution
1 parent 1fd96a1 commit 896f1c5

10 files changed

Lines changed: 689 additions & 7 deletions

File tree

core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ private AwsExecutionContextBuilder() {
161161
ExecutionInterceptorChain executionInterceptorChain =
162162
new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS));
163163

164+
// Snapshot the auth scheme before interceptors run so we can detect interceptor-modified signer properties later.
165+
executionAttributes.putAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_BEFORE_INTERCEPTORS,
166+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME));
167+
164168
InterceptorContext interceptorContext = InterceptorContext.builder()
165169
.request(originalRequest)
166170
.asyncRequestBody(executionParams.getAsyncRequestBody())
@@ -184,6 +188,15 @@ private AwsExecutionContextBuilder() {
184188
signer, executionAttributes, executionAttributes.getOptionalAttribute(
185189
AwsSignerExecutionAttribute.AWS_CREDENTIALS).orElse(null)));
186190

191+
// Set a callback to recompute SIGNING_METHOD after auth scheme resolution,
192+
// when derived attributes like ENABLE_CHUNKED_ENCODING have correct values.
193+
Signer resolvedSigner = signer;
194+
executionAttributes.putAttribute(SdkInternalExecutionAttribute.SIGNING_METHOD_UPDATER, attrs ->
195+
attrs.putAttribute(HttpChecksumConstant.SIGNING_METHOD,
196+
resolveSigningMethodUsed(
197+
resolvedSigner, attrs, attrs.getOptionalAttribute(
198+
AwsSignerExecutionAttribute.AWS_CREDENTIALS).orElse(null))));
199+
187200
putStreamingInputOutputTypesMetadata(executionAttributes, executionParams);
188201

189202
return ExecutionContext.builder()

core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute {
177177
public static final ExecutionAttribute<IdentityProviderUpdater> IDENTITY_PROVIDER_UPDATER =
178178
new ExecutionAttribute<>("IdentityProviderUpdater");
179179

180+
/**
181+
* Callback to recompute {@code SIGNING_METHOD} after auth scheme resolution.
182+
* Set by {@code AwsExecutionContextBuilder} so that the signing method can be updated
183+
* once the resolved auth scheme provides correct values for derived attributes like
184+
* {@code ENABLE_CHUNKED_ENCODING}.
185+
*/
186+
public static final ExecutionAttribute<java.util.function.Consumer<ExecutionAttributes>> SIGNING_METHOD_UPDATER =
187+
new ExecutionAttribute<>("SigningMethodUpdater");
188+
180189
/**
181190
* Callback to resolve auth scheme options from the (possibly modified) request.
182191
* Called by AuthSchemeResolutionStage after interceptors have run.
@@ -204,6 +213,22 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute {
204213
public static final ExecutionAttribute<SelectedAuthScheme<?>> SELECTED_AUTH_SCHEME =
205214
new ExecutionAttribute<>("SelectedAuthScheme");
206215

216+
/**
217+
* Snapshot of {@link #SELECTED_AUTH_SCHEME} taken before execution interceptors run.
218+
* Used by {@code AuthSchemeResolver#mergePreExistingAuthSchemeProperties} to detect which signer properties
219+
* were explicitly modified by interceptors (and should therefore override the freshly-resolved values).
220+
*/
221+
public static final ExecutionAttribute<SelectedAuthScheme<?>> AUTH_SCHEME_BEFORE_INTERCEPTORS =
222+
new ExecutionAttribute<>("AuthSchemeBeforeInterceptors");
223+
224+
/**
225+
* Snapshot of {@link #SELECTED_AUTH_SCHEME} taken after interceptors run but before auth scheme resolution.
226+
* Together with {@link #AUTH_SCHEME_BEFORE_INTERCEPTORS}, this allows detecting which signer properties
227+
* were explicitly modified by interceptors so they can be re-applied after endpoint resolution.
228+
*/
229+
public static final ExecutionAttribute<SelectedAuthScheme<?>> AUTH_SCHEME_AFTER_INTERCEPTORS =
230+
new ExecutionAttribute<>("AuthSchemeAfterInterceptors");
231+
207232
/**
208233
* The supported compression algorithms for an operation, and whether the operation is streaming or not.
209234
*/

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonAsyncHttpClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ public <OutputT> CompletableFuture<OutputT> execute(
199199
.then(MergeCustomQueryParamsStage::new)
200200
.then(QueryParametersToBodyStage::new)
201201
.then(() -> new CompressRequestStage(httpClientDependencies))
202-
.then(() -> new HttpChecksumStage(ClientType.ASYNC))
203202
.then(AuthSchemeResolutionStage::new)
204203
.then(EndpointResolutionStage::new)
204+
.then(() -> new HttpChecksumStage(ClientType.ASYNC))
205205
.then(ApplyUserAgentStage::new)
206206
.then(MakeRequestImmutableStage::new)
207207
.then(RequestPipelineBuilder

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonSyncHttpClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,9 @@ public <OutputT> OutputT execute(HttpResponseHandler<Response<OutputT>> response
187187
.then(MergeCustomQueryParamsStage::new)
188188
.then(QueryParametersToBodyStage::new)
189189
.then(() -> new CompressRequestStage(httpClientDependencies))
190-
.then(() -> new HttpChecksumStage(ClientType.SYNC))
191190
.then(AuthSchemeResolutionStage::new)
192191
.then(EndpointResolutionStage::new)
192+
.then(() -> new HttpChecksumStage(ClientType.SYNC))
193193
.then(ApplyUserAgentStage::new)
194194
.then(MakeRequestImmutableStage::new)
195195
// End of mutating request

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/auth/AuthSchemeResolver.java

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121
import java.util.Map;
22+
import java.util.Objects;
2223
import java.util.concurrent.CompletableFuture;
2324
import java.util.function.Supplier;
2425
import java.util.stream.Collectors;
@@ -32,6 +33,7 @@
3233
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
3334
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
3435
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
36+
import software.amazon.awssdk.http.auth.spi.signer.SignerProperty;
3537
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
3638
import software.amazon.awssdk.identity.spi.Identity;
3739
import software.amazon.awssdk.identity.spi.IdentityProvider;
@@ -94,6 +96,9 @@ public static SelectedAuthScheme<? extends Identity> selectAuthScheme(
9496

9597
/**
9698
* Merge properties from any pre-existing auth scheme into the selected one.
99+
* Properties explicitly modified by execution interceptors (detected by diffing against the pre-interceptor snapshot)
100+
* are force-overwritten onto the newly resolved auth scheme. All other pre-existing properties are merged with
101+
* {@code putIfAbsent} so that freshly resolved values take precedence.
97102
*/
98103
public static <T extends Identity> SelectedAuthScheme<T> mergePreExistingAuthSchemeProperties(
99104
SelectedAuthScheme<T> selectedAuthScheme,
@@ -106,9 +111,23 @@ public static <T extends Identity> SelectedAuthScheme<T> mergePreExistingAuthSch
106111
return selectedAuthScheme;
107112
}
108113

114+
SelectedAuthScheme<?> beforeInterceptors =
115+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_BEFORE_INTERCEPTORS);
116+
109117
AuthSchemeOption.Builder mergedOption = selectedAuthScheme.authSchemeOption().toBuilder();
118+
119+
existingAuthScheme.authSchemeOption().forEachSignerProperty(new AuthSchemeOption.SignerPropertyConsumer() {
120+
@Override
121+
public <S> void accept(SignerProperty<S> key, S value) {
122+
if (wasModifiedByInterceptor(beforeInterceptors, key, value)) {
123+
mergedOption.putSignerProperty(key, value);
124+
} else {
125+
mergedOption.putSignerPropertyIfAbsent(key, value);
126+
}
127+
}
128+
});
129+
110130
existingAuthScheme.authSchemeOption().forEachIdentityProperty(mergedOption::putIdentityPropertyIfAbsent);
111-
existingAuthScheme.authSchemeOption().forEachSignerProperty(mergedOption::putSignerPropertyIfAbsent);
112131

113132
return new SelectedAuthScheme<>(
114133
selectedAuthScheme.identity(),
@@ -117,6 +136,57 @@ public static <T extends Identity> SelectedAuthScheme<T> mergePreExistingAuthSch
117136
);
118137
}
119138

139+
private static <T> boolean wasModifiedByInterceptor(SelectedAuthScheme<?> beforeInterceptors,
140+
SignerProperty<T> key, T currentValue) {
141+
if (beforeInterceptors == null) {
142+
return true;
143+
}
144+
T originalValue = beforeInterceptors.authSchemeOption().signerProperty(key);
145+
return !Objects.equals(originalValue, currentValue);
146+
}
147+
148+
/**
149+
* Re-applies interceptor-modified signer properties onto the current auth scheme.
150+
* Called after endpoint resolution, which may have overwritten properties that interceptors set.
151+
*/
152+
public static void applyInterceptorModifiedProperties(SelectedAuthScheme<?> currentScheme,
153+
SelectedAuthScheme<?> beforeInterceptors,
154+
SelectedAuthScheme<?> afterInterceptors,
155+
ExecutionAttributes attrs) {
156+
if (afterInterceptors == null) {
157+
return;
158+
}
159+
doApplyInterceptorModifiedProperties(currentScheme, beforeInterceptors, afterInterceptors, attrs);
160+
}
161+
162+
@SuppressWarnings("unchecked")
163+
private static <T extends Identity> void doApplyInterceptorModifiedProperties(
164+
SelectedAuthScheme<T> currentScheme,
165+
SelectedAuthScheme<?> beforeInterceptors,
166+
SelectedAuthScheme<?> afterInterceptors,
167+
ExecutionAttributes attrs) {
168+
169+
AuthSchemeOption.Builder mergedOption = currentScheme.authSchemeOption().toBuilder();
170+
boolean[] changed = {false};
171+
172+
afterInterceptors.authSchemeOption().forEachSignerProperty(new AuthSchemeOption.SignerPropertyConsumer() {
173+
@Override
174+
public <S> void accept(SignerProperty<S> key, S value) {
175+
if (wasModifiedByInterceptor(beforeInterceptors, key, value)) {
176+
mergedOption.putSignerProperty(key, value);
177+
changed[0] = true;
178+
}
179+
}
180+
});
181+
182+
if (changed[0]) {
183+
attrs.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME,
184+
new SelectedAuthScheme<>(currentScheme.identity(),
185+
currentScheme.signer(),
186+
mergedOption.build()));
187+
}
188+
}
189+
120190
private static <T extends Identity> SelectedAuthScheme<T> trySelectAuthScheme(
121191
AuthSchemeOption authOption,
122192
AuthScheme<T> authScheme,

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AuthSchemeResolutionStage.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,22 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re
7676
SelectedAuthScheme<? extends Identity> selectedAuthScheme =
7777
AuthSchemeResolver.selectAuthScheme(authOptions, authSchemes, identityProviders, metricCollector);
7878

79+
// Snapshot the interceptor-modified auth scheme before overwriting, so EndpointResolutionStage
80+
// can re-apply interceptor-modified properties after the endpoint resolver callback runs.
81+
executionAttributes.putAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_AFTER_INTERCEPTORS,
82+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME));
83+
7984
selectedAuthScheme = AuthSchemeResolver.mergePreExistingAuthSchemeProperties(selectedAuthScheme, executionAttributes);
8085

8186
executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme);
8287

88+
// Recompute SIGNING_METHOD now that SELECTED_AUTH_SCHEME has correct values for derived attributes
89+
java.util.function.Consumer<ExecutionAttributes> signingMethodUpdater =
90+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.SIGNING_METHOD_UPDATER);
91+
if (signingMethodUpdater != null) {
92+
signingMethodUpdater.accept(executionAttributes);
93+
}
94+
8395
recordBusinessMetrics(selectedAuthScheme, sdkRequest, executionAttributes);
8496

8597
return request;

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/EndpointResolutionStage.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
import software.amazon.awssdk.annotations.SdkInternalApi;
2323
import software.amazon.awssdk.core.ClientEndpointProvider;
2424
import software.amazon.awssdk.core.SdkRequest;
25+
import software.amazon.awssdk.core.SelectedAuthScheme;
2526
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2627
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
2728
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
2829
import software.amazon.awssdk.core.internal.endpoint.EndpointResolver;
2930
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
3031
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
32+
import software.amazon.awssdk.core.internal.http.auth.AuthSchemeResolver;
3133
import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline;
3234
import software.amazon.awssdk.core.metrics.CoreMetric;
3335
import software.amazon.awssdk.endpoints.Endpoint;
@@ -70,6 +72,10 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re
7072
Endpoint endpoint = resolver.resolve(sdkRequest, attrs);
7173
Duration resolveEndpointDuration = Duration.ofNanos(System.nanoTime() - resolveEndpointStart);
7274

75+
// The endpoint resolver callback (generated per-service) may overwrite SELECTED_AUTH_SCHEME with
76+
// endpoint-resolved signer properties. Re-apply any interceptor-modified properties so they take precedence.
77+
reapplyInterceptorModifiedAuthProperties(attrs);
78+
7379
MetricCollector metricCollector = attrs.getAttribute(SdkExecutionAttribute.API_CALL_METRIC_COLLECTOR);
7480
if (metricCollector != null) {
7581
metricCollector.reportMetric(CoreMetric.ENDPOINT_RESOLVE_DURATION, resolveEndpointDuration);
@@ -140,4 +146,26 @@ private static String combinePath(String clientEndpointPath, String requestPath,
140146
String requestPathWithClientPathRemoved = StringUtils.replaceOnce(requestPath, clientEndpointPath, "");
141147
return SdkHttpUtils.appendUri(resolvedUriPath, requestPathWithClientPathRemoved);
142148
}
149+
150+
/**
151+
* Re-applies interceptor-modified signer properties after the endpoint resolver callback may have overwritten them.
152+
* Compares the pre-interceptor snapshot with the post-interceptor (pre-resolution) state to find what interceptors
153+
* changed, then force-overwrites those properties onto the current auth scheme.
154+
*/
155+
private static void reapplyInterceptorModifiedAuthProperties(ExecutionAttributes attrs) {
156+
SelectedAuthScheme<?> currentScheme = attrs.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME);
157+
if (currentScheme == null) {
158+
return;
159+
}
160+
SelectedAuthScheme<?> beforeInterceptors =
161+
attrs.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_BEFORE_INTERCEPTORS);
162+
163+
SelectedAuthScheme<?> afterInterceptors =
164+
attrs.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_AFTER_INTERCEPTORS);
165+
if (afterInterceptors == null) {
166+
return;
167+
}
168+
169+
AuthSchemeResolver.applyInterceptorModifiedProperties(currentScheme, beforeInterceptors, afterInterceptors, attrs);
170+
}
143171
}

0 commit comments

Comments
 (0)