Skip to content

Treat UncheckedIOException as IOExceptions #5319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
6 changes: 6 additions & 0 deletions .changes/next-release/bugfix-AWSSDKforJavav2-603b66f.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "AWS SDK for Java v2",
"contributor": "anirudh9391",
"description": "Modify exception handling to not have it throw UncheckedIoExceptions if caused by timeout"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package software.amazon.awssdk.core.internal.http.pipeline.stages;

import static software.amazon.awssdk.core.internal.http.timers.TimerUtils.resolveTimeoutInMillis;
import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
import static software.amazon.awssdk.utils.FunctionalUtils.runAndLogError;

import java.io.IOException;
import software.amazon.awssdk.annotations.SdkInternalApi;
Expand All @@ -32,6 +32,7 @@
import software.amazon.awssdk.core.internal.http.pipeline.RequestPipeline;
import software.amazon.awssdk.core.internal.http.pipeline.RequestToResponsePipeline;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.utils.Logger;

/**
* Check if an {@link Exception} is caused by either ApiCallTimeout or ApiAttemptTimeout and translate that
Expand All @@ -40,6 +41,7 @@
@SdkInternalApi
public final class TimeoutExceptionHandlingStage<OutputT> implements RequestToResponsePipeline<OutputT> {

private static final Logger log = Logger.loggerFor(TimeoutExceptionHandlingStage.class);
private final HttpClientDependencies dependencies;
private final RequestPipeline<SdkHttpFullRequest, Response<OutputT>> requestPipeline;

Expand Down Expand Up @@ -90,8 +92,8 @@ public Response<OutputT> execute(SdkHttpFullRequest request, RequestExecutionCon
* @return The translated exception.
*/
private Exception translatePipelineException(RequestExecutionContext context, Exception e) {
if (e instanceof InterruptedException || e instanceof IOException ||
e instanceof AbortedException || Thread.currentThread().isInterrupted()
if (e instanceof InterruptedException || e instanceof IOException
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reformat it as before so that it doesnot show in diff , else in GitBlame it will show up as new change

|| e instanceof AbortedException || Thread.currentThread().isInterrupted()
|| (e instanceof SdkClientException && isCausedByApiCallAttemptTimeout(context))) {
return handleTimeoutCausedException(context, e);
}
Expand All @@ -100,7 +102,8 @@ private Exception translatePipelineException(RequestExecutionContext context, Ex

private Exception handleTimeoutCausedException(RequestExecutionContext context, Exception e) {
if (e instanceof SdkInterruptedException) {
((SdkInterruptedException) e).getResponseStream().ifPresent(r -> invokeSafely(r::close));
((SdkInterruptedException) e).getResponseStream().ifPresent(r -> runAndLogError(log.logger(), "Unable to close the "
+ "stream", r::close));
}

if (isCausedByApiCallTimeout(context)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.SocketException;
import java.util.concurrent.ScheduledFuture;
import org.junit.Before;
Expand All @@ -35,6 +37,7 @@
import software.amazon.awssdk.core.exception.AbortedException;
import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.exception.SdkInterruptedException;
import software.amazon.awssdk.core.http.NoopTestRequest;
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
Expand All @@ -43,6 +46,7 @@
import software.amazon.awssdk.core.internal.http.timers.ClientExecutionAndRequestTimerTestUtils;
import software.amazon.awssdk.core.internal.http.timers.TimeoutTask;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpFullResponse;
import utils.ValidSdkObjects;

@RunWith(MockitoJUnitRunner.class)
Expand Down Expand Up @@ -179,6 +183,13 @@ public void interruptedException_causedByApiCallAttemptTimeoutTask() throws Exce
verifyExceptionThrown(InterruptedException.class);
}

@Test
public void uncheckedIOException_doesNotThrowException() throws Exception {
when(apiCallTimeoutTask.hasExecuted()).thenReturn(true);
when(requestPipeline.execute(any(), any())).thenThrow(SdkInterruptedException.class);
verifyExceptionThrown(InterruptedException.class);
}


@Test
public void abortedException_causedByApiCallAttemptTimeoutTask_shouldNotPropagate() throws Exception {
Expand Down Expand Up @@ -207,6 +218,13 @@ private void verifyExceptionThrown(Class exceptionToAssert) {
assertThatThrownBy(() -> stage.execute(ValidSdkObjects.sdkHttpFullRequest().build(), context))
.isExactlyInstanceOf(exceptionToAssert);
}
private void verifyExceptionNotThrown() {
RequestExecutionContext context = requestContext();
context.apiCallTimeoutTracker(new ApiCallTimeoutTracker(apiCallTimeoutTask, scheduledFuture));
context.apiCallAttemptTimeoutTracker(new ApiCallTimeoutTracker(apiCallAttemptTimeoutTask, scheduledFuture));

assertDoesNotThrow(() -> stage.execute(ValidSdkObjects.sdkHttpFullRequest().build(), context));
}

private RequestExecutionContext requestContext() {
SdkRequestOverrideConfiguration.Builder configBuilder = SdkRequestOverrideConfiguration.builder();
Expand Down
Loading