Skip to content

Commit ea0f6c0

Browse files
authored
feat: add AttemptCount to HttpResponseException (#1505)
* feat: add tests for attemptCount * fix: more renaming * fix: linter fixes
1 parent 8b1b8f2 commit ea0f6c0

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,9 @@ public HttpResponse execute() throws IOException {
11131113
// throw an exception if unsuccessful response
11141114
if (throwExceptionOnExecuteError && !response.isSuccessStatusCode()) {
11151115
try {
1116-
throw new HttpResponseException(response);
1116+
throw new HttpResponseException.Builder(response)
1117+
.setAttemptCount(numRetries - retriesRemaining)
1118+
.build();
11171119
} finally {
11181120
response.disconnect();
11191121
}

google-http-client/src/main/java/com/google/api/client/http/HttpResponseException.java

+28
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public class HttpResponseException extends IOException {
4242
/** HTTP response content or {@code null} for none. */
4343
private final String content;
4444

45+
/** Number of attempts performed */
46+
private final int attemptCount;
47+
4548
/**
4649
* Constructor that constructs a detail message from the given HTTP response that includes the
4750
* status code, status message and HTTP response content.
@@ -73,6 +76,7 @@ protected HttpResponseException(Builder builder) {
7376
statusMessage = builder.statusMessage;
7477
headers = builder.headers;
7578
content = builder.content;
79+
attemptCount = builder.attemptCount;
7680
}
7781

7882
/**
@@ -121,6 +125,15 @@ public final String getContent() {
121125
return content;
122126
}
123127

128+
/**
129+
* Returns the attempt count
130+
*
131+
* @since 1.41
132+
*/
133+
public final int getAttemptCount() {
134+
return attemptCount;
135+
}
136+
124137
/**
125138
* Builder.
126139
*
@@ -145,6 +158,9 @@ public static class Builder {
145158
/** Detail message to use or {@code null} for none. */
146159
String message;
147160

161+
/** Number of attempts performed */
162+
int attemptCount;
163+
148164
/**
149165
* @param statusCode HTTP status code
150166
* @param statusMessage status message or {@code null}
@@ -260,6 +276,18 @@ public Builder setContent(String content) {
260276
return this;
261277
}
262278

279+
/** Returns the request attempt count */
280+
public final int getAttemptCount() {
281+
return attemptCount;
282+
}
283+
284+
/** Sets the attempt count for the related HTTP request execution. */
285+
public Builder setAttemptCount(int attemptCount) {
286+
Preconditions.checkArgument(attemptCount >= 0);
287+
this.attemptCount = attemptCount;
288+
return this;
289+
}
290+
263291
/** Returns a new instance of {@link HttpResponseException} based on this builder. */
264292
public HttpResponseException build() {
265293
return new HttpResponseException(this);

google-http-client/src/test/java/com/google/api/client/http/HttpResponseExceptionTest.java

+48
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
import com.google.api.client.testing.http.MockHttpTransport;
2424
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
2525
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
26+
import com.google.api.client.util.ExponentialBackOff;
2627
import java.io.ByteArrayInputStream;
2728
import java.io.ByteArrayOutputStream;
2829
import java.io.IOException;
2930
import java.io.ObjectInputStream;
3031
import java.io.ObjectOutput;
3132
import java.io.ObjectOutputStream;
3233
import junit.framework.TestCase;
34+
import org.junit.Assert;
3335
import org.junit.function.ThrowingRunnable;
3436

3537
/**
@@ -208,6 +210,8 @@ public void run() throws Throwable {
208210
+ SIMPLE_GENERIC_URL
209211
+ LINE_SEPARATOR
210212
+ "Unable to find resource");
213+
// no retries expected
214+
assertEquals(1, responseException.getAttemptCount());
211215
}
212216

213217
public void testInvalidCharset() throws Exception {
@@ -245,6 +249,50 @@ public void run() throws Throwable {
245249
.isEqualTo("404 Not Found\nGET " + SIMPLE_GENERIC_URL);
246250
}
247251

252+
public void testAttemptCountWithBackOff() throws Exception {
253+
HttpTransport fakeTransport =
254+
new MockHttpTransport() {
255+
@Override
256+
public LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
257+
return new MockLowLevelHttpRequest() {
258+
@Override
259+
public LowLevelHttpResponse execute() throws IOException {
260+
MockLowLevelHttpResponse result = new MockLowLevelHttpResponse();
261+
result.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR);
262+
result.setReasonPhrase("Error");
263+
result.setContent("Unknown Error");
264+
return result;
265+
}
266+
};
267+
}
268+
};
269+
ExponentialBackOff backoff = new ExponentialBackOff.Builder().build();
270+
final HttpRequest request =
271+
fakeTransport.createRequestFactory().buildGetRequest(new GenericUrl("http://not/used"));
272+
request.setUnsuccessfulResponseHandler(
273+
new HttpBackOffUnsuccessfulResponseHandler(backoff)
274+
.setBackOffRequired(
275+
new HttpBackOffUnsuccessfulResponseHandler.BackOffRequired() {
276+
public boolean isRequired(HttpResponse response) {
277+
return true;
278+
}
279+
}));
280+
request.setNumberOfRetries(1);
281+
HttpResponseException responseException =
282+
assertThrows(
283+
HttpResponseException.class,
284+
new ThrowingRunnable() {
285+
@Override
286+
public void run() throws Throwable {
287+
request.execute();
288+
}
289+
});
290+
291+
Assert.assertEquals(500, responseException.getStatusCode());
292+
// original request and 1 retry - total 2
293+
assertEquals(2, responseException.getAttemptCount());
294+
}
295+
248296
public void testUnsupportedCharset() throws Exception {
249297
HttpTransport transport =
250298
new MockHttpTransport() {

0 commit comments

Comments
 (0)