Skip to content

Commit d6cff60

Browse files
authored
fix: Add configurable connect and read timeouts for IdentityPoolCredentials requests (#1847)
* fix: Add configurable connect and read timeouts to STS requests * chore: Fix lint issues * chore: Add connect and read timeout to identity pool creds * chore: Propogate connect and read timeouts to all calls
1 parent a65c22d commit d6cff60

File tree

3 files changed

+95
-18
lines changed

3 files changed

+95
-18
lines changed

oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.google.api.client.http.HttpHeaders;
3737
import com.google.api.client.json.GenericJson;
3838
import com.google.api.client.util.Data;
39+
import com.google.api.core.InternalApi;
3940
import com.google.auth.RequestMetadataCallback;
4041
import com.google.auth.http.HttpTransportFactory;
4142
import com.google.common.base.MoreObjects;
@@ -97,6 +98,9 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials {
9798

9899
private EnvironmentProvider environmentProvider;
99100

101+
private int connectTimeout;
102+
private int readTimeout;
103+
100104
/**
101105
* Constructor with minimum identifying information and custom HTTP transport. Does not support
102106
* workforce credentials.
@@ -271,6 +275,8 @@ protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder)
271275
: builder.metricsHandler;
272276

273277
this.name = GoogleCredentialsInfo.EXTERNAL_ACCOUNT_CREDENTIALS.getCredentialName();
278+
this.connectTimeout = builder.connectTimeout;
279+
this.readTimeout = builder.readTimeout;
274280
}
275281

276282
ImpersonatedCredentials buildImpersonatedCredentials() {
@@ -305,6 +311,8 @@ ImpersonatedCredentials buildImpersonatedCredentials() {
305311
.setScopes(new ArrayList<>(scopes))
306312
.setLifetime(this.serviceAccountImpersonationOptions.lifetime)
307313
.setIamEndpointOverride(serviceAccountImpersonationUrl)
314+
.setConnectTimeout(connectTimeout)
315+
.setReadTimeout(readTimeout)
308316
.build();
309317
}
310318

@@ -533,7 +541,9 @@ protected AccessToken exchangeExternalCredentialForAccessToken(
533541

534542
StsRequestHandler.Builder requestHandler =
535543
StsRequestHandler.newBuilder(
536-
tokenUrl, stsTokenExchangeRequest, transportFactory.create().createRequestFactory());
544+
tokenUrl, stsTokenExchangeRequest, transportFactory.create().createRequestFactory())
545+
.setConnectTimeout(connectTimeout)
546+
.setReadTimeout(readTimeout);
537547

538548
// If this credential was initialized with a Workforce configuration then the
539549
// workforcePoolUserProject must be passed to the Security Token Service via the internal
@@ -771,6 +781,9 @@ public abstract static class Builder extends GoogleCredentials.Builder {
771781
@Nullable protected String workforcePoolUserProject;
772782
@Nullable protected ServiceAccountImpersonationOptions serviceAccountImpersonationOptions;
773783

784+
protected int connectTimeout = 20000; // Default to 20000ms = 20s
785+
protected int readTimeout = 20000; // Default to 20000ms = 20s
786+
774787
/* The field is not being used and value not set. Superseded by the same field in the
775788
{@link GoogleCredentials.Builder}.
776789
*/
@@ -796,6 +809,8 @@ protected Builder(ExternalAccountCredentials credentials) {
796809
this.workforcePoolUserProject = credentials.workforcePoolUserProject;
797810
this.serviceAccountImpersonationOptions = credentials.serviceAccountImpersonationOptions;
798811
this.metricsHandler = credentials.metricsHandler;
812+
this.connectTimeout = credentials.connectTimeout;
813+
this.readTimeout = credentials.readTimeout;
799814
}
800815

801816
/**
@@ -988,6 +1003,20 @@ public Builder setUniverseDomain(String universeDomain) {
9881003
return this;
9891004
}
9901005

1006+
/** Warning: Not for public use and can be removed at any time. */
1007+
@InternalApi
1008+
public Builder setConnectTimeout(int connectTimeout) {
1009+
this.connectTimeout = connectTimeout;
1010+
return this;
1011+
}
1012+
1013+
/** Warning: Not for public use and can be removed at any time. */
1014+
@InternalApi
1015+
public Builder setReadTimeout(int readTimeout) {
1016+
this.readTimeout = readTimeout;
1017+
return this;
1018+
}
1019+
9911020
/**
9921021
* Sets the optional Environment Provider.
9931022
*

oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.google.api.client.json.GenericJson;
4545
import com.google.api.client.json.JsonObjectParser;
4646
import com.google.api.client.util.GenericData;
47+
import com.google.api.core.InternalApi;
4748
import com.google.auth.CredentialTypeForMetrics;
4849
import com.google.auth.ServiceAccountSigner;
4950
import com.google.auth.http.HttpCredentialsAdapter;
@@ -117,6 +118,9 @@ public class ImpersonatedCredentials extends GoogleCredentials
117118

118119
private transient Calendar calendar;
119120

121+
private int connectTimeout;
122+
private int readTimeout;
123+
120124
/**
121125
* @param sourceCredentials the source credential used to acquire the impersonated credentials. It
122126
* should be either a user account credential or a service account credential.
@@ -559,6 +563,8 @@ private ImpersonatedCredentials(Builder builder) throws IOException {
559563
+ "does not match %s universe domain set for impersonated credentials.",
560564
this.sourceCredentials.getUniverseDomain(), builder.getUniverseDomain()));
561565
}
566+
this.connectTimeout = builder.connectTimeout;
567+
this.readTimeout = builder.readTimeout;
562568
}
563569

564570
/**
@@ -587,6 +593,12 @@ public AccessToken refreshAccessToken() throws IOException {
587593
|| (isDefaultUniverseDomain()
588594
&& ((ServiceAccountCredentials) this.sourceCredentials)
589595
.shouldUseAssertionFlowForGdu())) {
596+
if (this.sourceCredentials instanceof IdentityPoolCredentials) {
597+
this.sourceCredentials =
598+
((IdentityPoolCredentials) this.sourceCredentials)
599+
.toBuilder().setConnectTimeout(connectTimeout).setReadTimeout(readTimeout).build();
600+
}
601+
590602
try {
591603
this.sourceCredentials.refreshIfExpired();
592604
} catch (IOException e) {
@@ -616,6 +628,8 @@ public AccessToken refreshAccessToken() throws IOException {
616628

617629
HttpContent requestContent = new JsonHttpContent(parser.getJsonFactory(), body);
618630
HttpRequest request = requestFactory.buildPostRequest(url, requestContent);
631+
request.setConnectTimeout(connectTimeout);
632+
request.setReadTimeout(readTimeout);
619633
adapter.initialize(request);
620634
request.setParser(parser);
621635
MetricsUtils.setMetricsHeader(
@@ -746,6 +760,9 @@ public static class Builder extends GoogleCredentials.Builder {
746760
private String iamEndpointOverride;
747761
private Calendar calendar = Calendar.getInstance();
748762

763+
private int connectTimeout = 20000; // Default to 20000ms = 20s
764+
private int readTimeout = 20000; // Default to 20000ms = 20s
765+
749766
protected Builder() {}
750767

751768
/**
@@ -769,6 +786,8 @@ protected Builder(ImpersonatedCredentials credentials) {
769786
this.lifetime = credentials.lifetime;
770787
this.transportFactory = credentials.transportFactory;
771788
this.iamEndpointOverride = credentials.iamEndpointOverride;
789+
this.connectTimeout = credentials.connectTimeout;
790+
this.readTimeout = credentials.readTimeout;
772791
}
773792

774793
@CanIgnoreReturnValue
@@ -860,6 +879,20 @@ public Builder setCalendar(Calendar calendar) {
860879
return this;
861880
}
862881

882+
/** Warning: Not for public use and can be removed at any time. */
883+
@InternalApi
884+
public Builder setConnectTimeout(int connectTimeout) {
885+
this.connectTimeout = connectTimeout;
886+
return this;
887+
}
888+
889+
/** Warning: Not for public use and can be removed at any time. */
890+
@InternalApi
891+
public Builder setReadTimeout(int readTimeout) {
892+
this.readTimeout = readTimeout;
893+
return this;
894+
}
895+
863896
public Calendar getCalendar() {
864897
return this.calendar;
865898
}

oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.google.api.client.json.JsonObjectParser;
4343
import com.google.api.client.json.JsonParser;
4444
import com.google.api.client.util.GenericData;
45+
import com.google.api.core.InternalApi;
4546
import com.google.common.base.Joiner;
4647
import com.google.errorprone.annotations.CanIgnoreReturnValue;
4748
import java.io.IOException;
@@ -73,26 +74,22 @@ public final class StsRequestHandler {
7374
@Nullable private final HttpHeaders headers;
7475
@Nullable private final String internalOptions;
7576

77+
private final int connectTimeout;
78+
private final int readTimeout;
79+
7680
/**
7781
* Internal constructor.
7882
*
79-
* @param tokenExchangeEndpoint the token exchange endpoint
80-
* @param request the token exchange request
81-
* @param headers optional additional headers to pass along the request
82-
* @param internalOptions optional GCP specific STS options
8383
* @return an StsTokenExchangeResponse instance if the request was successful
8484
*/
85-
private StsRequestHandler(
86-
String tokenExchangeEndpoint,
87-
StsTokenExchangeRequest request,
88-
HttpRequestFactory httpRequestFactory,
89-
@Nullable HttpHeaders headers,
90-
@Nullable String internalOptions) {
91-
this.tokenExchangeEndpoint = tokenExchangeEndpoint;
92-
this.request = request;
93-
this.httpRequestFactory = httpRequestFactory;
94-
this.headers = headers;
95-
this.internalOptions = internalOptions;
85+
private StsRequestHandler(Builder builder) {
86+
this.tokenExchangeEndpoint = builder.tokenExchangeEndpoint;
87+
this.request = builder.request;
88+
this.httpRequestFactory = builder.httpRequestFactory;
89+
this.headers = builder.headers;
90+
this.internalOptions = builder.internalOptions;
91+
this.connectTimeout = builder.connectTimeout;
92+
this.readTimeout = builder.readTimeout;
9693
}
9794

9895
/**
@@ -120,6 +117,8 @@ public StsTokenExchangeResponse exchangeToken() throws IOException {
120117
if (headers != null) {
121118
httpRequest.setHeaders(headers);
122119
}
120+
httpRequest.setConnectTimeout(connectTimeout);
121+
httpRequest.setReadTimeout(readTimeout);
123122

124123
try {
125124
HttpResponse response = httpRequest.execute();
@@ -214,6 +213,9 @@ public static class Builder {
214213
@Nullable private HttpHeaders headers;
215214
@Nullable private String internalOptions;
216215

216+
private int connectTimeout = 20000; // Default to 20000ms = 20s
217+
private int readTimeout = 20000; // Default to 20000ms = 20s
218+
217219
private Builder(
218220
String tokenExchangeEndpoint,
219221
StsTokenExchangeRequest stsTokenExchangeRequest,
@@ -235,9 +237,22 @@ public StsRequestHandler.Builder setInternalOptions(String internalOptions) {
235237
return this;
236238
}
237239

240+
/** Warning: Not for public use and can be removed at any time. */
241+
@InternalApi
242+
public StsRequestHandler.Builder setConnectTimeout(int connectTimeout) {
243+
this.connectTimeout = connectTimeout;
244+
return this;
245+
}
246+
247+
/** Warning: Not for public use and can be removed at any time. */
248+
@InternalApi
249+
public StsRequestHandler.Builder setReadTimeout(int readTimeout) {
250+
this.readTimeout = readTimeout;
251+
return this;
252+
}
253+
238254
public StsRequestHandler build() {
239-
return new StsRequestHandler(
240-
tokenExchangeEndpoint, request, httpRequestFactory, headers, internalOptions);
255+
return new StsRequestHandler(this);
241256
}
242257
}
243258
}

0 commit comments

Comments
 (0)