Skip to content

Commit eb10360

Browse files
authored
Merge pull request #963 from AzureAD/avdunn/retry-behavior-disable
Add API to disable internal retry behavior
2 parents b0afc5d + 57406d2 commit eb10360

File tree

5 files changed

+76
-8
lines changed

5 files changed

+76
-8
lines changed

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AbstractApplicationBase.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public abstract class AbstractApplicationBase implements IApplicationBase {
3232
private IHttpClient httpClient;
3333
private Integer connectTimeoutForDefaultHttpClient;
3434
private Integer readTimeoutForDefaultHttpClient;
35+
private boolean retryDisabled;
3536
String tenant;
3637

3738
//The following fields are set in only some applications and/or set internally by the library. To avoid excessive
@@ -150,6 +151,10 @@ public Integer readTimeoutForDefaultHttpClient() {
150151
return this.readTimeoutForDefaultHttpClient;
151152
}
152153

154+
boolean isRetryDisabled() {
155+
return this.retryDisabled;
156+
}
157+
153158
String tenant() {
154159
return this.tenant;
155160
}
@@ -190,6 +195,7 @@ public abstract static class Builder<T extends Builder<T>> {
190195
Boolean onlySendFailureTelemetry = false;
191196
Integer connectTimeoutForDefaultHttpClient;
192197
Integer readTimeoutForDefaultHttpClient;
198+
boolean disableInternalRetries;
193199
private String clientId;
194200
private Authority authenticationAuthority = createDefaultAADAuthority();
195201

@@ -319,6 +325,18 @@ public T readTimeoutForDefaultHttpClient(Integer val) {
319325
return self();
320326
}
321327

328+
/**
329+
* The library has a number of policies for retrying HTTP calls in different scenarios.
330+
* <p>
331+
* This will disable all internal retry behavior, allowing customized retry behavior via your own implementation of {@link IHttpClient}
332+
*
333+
* @return instance of the Builder on which method was called
334+
*/
335+
public T disableInternalRetries() {
336+
disableInternalRetries = true;
337+
return self();
338+
}
339+
322340
T telemetryConsumer(Consumer<List<HashMap<String, String>>> val) {
323341
validateNotNull("telemetryConsumer", val);
324342

@@ -356,5 +374,16 @@ private static Authority createDefaultAADAuthority() {
356374
readTimeoutForDefaultHttpClient = builder.readTimeoutForDefaultHttpClient;
357375
authenticationAuthority = builder.authenticationAuthority;
358376
clientId = builder.clientId;
377+
retryDisabled = builder.disableInternalRetries;
378+
379+
if (builder.httpClient == null) {
380+
httpClient = new DefaultHttpClient(
381+
builder.proxy,
382+
builder.sslSocketFactory,
383+
builder.connectTimeoutForDefaultHttpClient,
384+
builder.readTimeoutForDefaultHttpClient);
385+
} else {
386+
httpClient = builder.httpClient;
387+
}
359388
}
360389
}

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AbstractClientApplicationBase.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,10 +566,7 @@ public T correlationId(String val) {
566566
super.serviceBundle = new ServiceBundle(
567567
builder.executorService,
568568
new TelemetryManager(telemetryConsumer, builder.onlySendFailureTelemetry),
569-
new HttpHelper(builder.httpClient == null ?
570-
new DefaultHttpClient(builder.proxy, builder.sslSocketFactory, builder.connectTimeoutForDefaultHttpClient, builder.readTimeoutForDefaultHttpClient) :
571-
builder.httpClient,
572-
new DefaultRetryPolicy())
569+
new HttpHelper(this, new DefaultRetryPolicy())
573570
);
574571

575572
if (aadAadInstanceDiscoveryResponse != null) {

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/HttpHelper.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@ class HttpHelper implements IHttpHelper {
2121

2222
private IHttpClient httpClient;
2323
private IRetryPolicy retryPolicy;
24+
private boolean retryDisabled;
2425

2526
HttpHelper(IHttpClient httpClient, IRetryPolicy retryPolicy) {
2627
this.httpClient = httpClient;
2728
this.retryPolicy = retryPolicy != null ? retryPolicy : new DefaultRetryPolicy();
2829
}
2930

31+
HttpHelper(AbstractApplicationBase application, IRetryPolicy retryPolicy) {
32+
this.httpClient = application.httpClient();
33+
this.retryDisabled = application.isRetryDisabled();
34+
this.retryPolicy = retryPolicy != null ? retryPolicy : new DefaultRetryPolicy();
35+
}
36+
3037
public IHttpResponse executeHttpRequest(HttpRequest httpRequest,
3138
RequestContext requestContext,
3239
ServiceBundle serviceBundle) {
@@ -140,6 +147,10 @@ IHttpResponse executeHttpRequestWithRetries(HttpRequest httpRequest, IHttpClient
140147
throws Exception {
141148
IHttpResponse httpResponse = httpClient.send(httpRequest);
142149

150+
if (retryDisabled) {
151+
return httpResponse;
152+
}
153+
143154
int retryCount = 0;
144155
int maxRetries = retryPolicy.getMaxRetryCount(httpResponse);
145156

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ManagedIdentityApplication.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ private ManagedIdentityApplication(Builder builder) {
3737
super.serviceBundle = new ServiceBundle(
3838
builder.executorService,
3939
new TelemetryManager(telemetryConsumer, builder.onlySendFailureTelemetry),
40-
new HttpHelper(builder.httpClient == null ?
41-
new DefaultHttpClient(builder.proxy, builder.sslSocketFactory, builder.connectTimeoutForDefaultHttpClient, builder.readTimeoutForDefaultHttpClient) :
42-
builder.httpClient,
43-
new ManagedIdentityRetryPolicy())
40+
new HttpHelper(this, new ManagedIdentityRetryPolicy())
4441
);
4542
log = LoggerFactory.getLogger(ManagedIdentityApplication.class);
4643

msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/ManagedIdentityTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,40 @@ void managedIdentityTest_Retry(ManagedIdentitySourceType source, String endpoint
586586
fail("MsalServiceException is expected but not thrown.");
587587
}
588588

589+
@ParameterizedTest
590+
@MethodSource("com.microsoft.aad.msal4j.ManagedIdentityTestDataProvider#createDataWrongScope")
591+
void managedIdentityTest_RetriesDisabled(ManagedIdentitySourceType source, String endpoint, String resource) throws Exception {
592+
IEnvironmentVariables environmentVariables = new EnvironmentVariablesHelper(source, endpoint);
593+
ManagedIdentityApplication.setEnvironmentVariables(environmentVariables);
594+
595+
DefaultHttpClientManagedIdentity httpClientMock = mock(DefaultHttpClientManagedIdentity.class);
596+
if (source == SERVICE_FABRIC) {
597+
ServiceFabricManagedIdentitySource.setHttpClient(httpClientMock);
598+
}
599+
600+
miApp = ManagedIdentityApplication
601+
.builder(ManagedIdentityId.systemAssigned())
602+
.httpClient(httpClientMock)
603+
.disableInternalRetries()
604+
.build();
605+
606+
//Several specific 4xx and 5xx errors, such as 500, should trigger MSAL's retry logic
607+
when(httpClientMock.send(expectedRequest(source, resource))).thenReturn(expectedResponse(500, ManagedIdentityTestConstants.MSI_ERROR_RESPONSE_500));
608+
609+
try {
610+
acquireTokenCommon(resource).get();
611+
} catch (Exception exception) {
612+
assert(exception.getCause() instanceof MsalServiceException);
613+
614+
//But because retries are disabled at the app level, there should be no retries for any source except Service Fabric, which uses its own HttpClient
615+
if (source == SERVICE_FABRIC) {
616+
verify(httpClientMock, times(4)).send(any());
617+
} else {
618+
verify(httpClientMock, times(1)).send(any());
619+
}
620+
}
621+
}
622+
589623
@Test
590624
void managedIdentityTest_IMDSRetry() throws Exception {
591625
IEnvironmentVariables environmentVariables = new EnvironmentVariablesHelper(IMDS, ManagedIdentityTestConstants.IMDS_ENDPOINT);

0 commit comments

Comments
 (0)