Skip to content

Commit 82bb676

Browse files
committed
Invoke responseHandler#onError when the request future is cancelled
1 parent 9ba3d85 commit 82bb676

File tree

4 files changed

+81
-4
lines changed

4 files changed

+81
-4
lines changed

http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/internal/CrtRequestExecutor.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import software.amazon.awssdk.crt.http.HttpHeader;
3131
import software.amazon.awssdk.crt.http.HttpRequest;
3232
import software.amazon.awssdk.http.Header;
33+
import software.amazon.awssdk.http.SdkCancellationException;
3334
import software.amazon.awssdk.http.SdkHttpRequest;
3435
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
3536
import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler;
@@ -41,7 +42,7 @@ public final class CrtRequestExecutor {
4142
private static final Logger log = Logger.loggerFor(CrtRequestExecutor.class);
4243

4344
public CompletableFuture<Void> execute(CrtRequestContext executionContext) {
44-
CompletableFuture<Void> requestFuture = new CompletableFuture<>();
45+
CompletableFuture<Void> requestFuture = createExecutionFuture(executionContext.sdkRequest());
4546

4647
// When a Connection is ready from the Connection Pool, schedule the Request on the connection
4748
CompletableFuture<HttpClientConnection> httpClientConnectionCompletableFuture =
@@ -77,6 +78,27 @@ public CompletableFuture<Void> execute(CrtRequestContext executionContext) {
7778
return requestFuture;
7879
}
7980

81+
/**
82+
* Convenience method to create the execution future and set up the cancellation logic.
83+
*
84+
* @return The created execution future.
85+
*/
86+
private CompletableFuture<Void> createExecutionFuture(AsyncExecuteRequest request) {
87+
CompletableFuture<Void> future = new CompletableFuture<>();
88+
89+
future.whenComplete((r, t) -> {
90+
if (t == null) {
91+
return;
92+
}
93+
//TODO: Aborting request once it's supported in CRT
94+
if (future.isCancelled()) {
95+
request.responseHandler().onError(new SdkCancellationException("The request was cancelled"));
96+
}
97+
});
98+
99+
return future;
100+
}
101+
80102
private void handleFailure(Throwable cause,
81103
CompletableFuture<Void> executeFuture,
82104
SdkAsyncHttpResponseHandler responseHandler) {

http-clients/aws-crt-client/src/test/java/software/amazon/awssdk/http/crt/internal/CrtRequestExecutorTest.java

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.IOException;
2323
import java.net.URI;
2424
import java.util.concurrent.CompletableFuture;
25+
import org.junit.After;
2526
import org.junit.Before;
2627
import org.junit.Test;
2728
import org.junit.runner.RunWith;
@@ -33,6 +34,7 @@
3334
import software.amazon.awssdk.crt.http.HttpClientConnection;
3435
import software.amazon.awssdk.crt.http.HttpClientConnectionManager;
3536
import software.amazon.awssdk.crt.http.HttpRequest;
37+
import software.amazon.awssdk.http.SdkCancellationException;
3638
import software.amazon.awssdk.http.SdkHttpFullRequest;
3739
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
3840
import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler;
@@ -43,6 +45,7 @@ public class CrtRequestExecutorTest {
4345
private CrtRequestExecutor requestExecutor;
4446
@Mock
4547
private HttpClientConnectionManager connectionManager;
48+
4649
@Mock
4750
private SdkAsyncHttpResponseHandler responseHandler;
4851

@@ -54,8 +57,13 @@ public void setup() {
5457
requestExecutor = new CrtRequestExecutor();
5558
}
5659

60+
@After
61+
public void teardown() {
62+
Mockito.reset(connectionManager, responseHandler, httpClientConnection);
63+
}
64+
5765
@Test
58-
public void execute_acquireConnectionThrowException_shouldInvokeOnError() {
66+
public void acquireConnectionThrowException_shouldInvokeOnError() {
5967
RuntimeException exception = new RuntimeException("error");
6068
CrtRequestContext context = CrtRequestContext.builder()
6169
.crtConnPool(connectionManager)
@@ -80,7 +88,7 @@ public void execute_acquireConnectionThrowException_shouldInvokeOnError() {
8088
}
8189

8290
@Test
83-
public void execute_makeRequestThrowException_shouldInvokeOnError() {
91+
public void makeRequestThrowException_shouldInvokeOnError() {
8492
CrtRuntimeException exception = new CrtRuntimeException("");
8593
SdkHttpFullRequest request = createRequest(URI.create("http://localhost"));
8694
CrtRequestContext context = CrtRequestContext.builder()
@@ -110,4 +118,47 @@ public void execute_makeRequestThrowException_shouldInvokeOnError() {
110118
assertThat(actualException).hasCause(exception);
111119
assertThat(executeFuture).hasFailedWithThrowableThat().hasCause(exception).isInstanceOf(IOException.class);
112120
}
121+
122+
@Test
123+
public void makeRequest_success() {
124+
SdkHttpFullRequest request = createRequest(URI.create("http://localhost"));
125+
CrtRequestContext context = CrtRequestContext.builder()
126+
.readBufferSize(2000)
127+
.crtConnPool(connectionManager)
128+
.request(AsyncExecuteRequest.builder()
129+
.request(request)
130+
.requestContentPublisher(createProvider(""))
131+
.responseHandler(responseHandler)
132+
.build())
133+
.build();
134+
CompletableFuture<HttpClientConnection> completableFuture = new CompletableFuture<>();
135+
Mockito.when(connectionManager.acquireConnection()).thenReturn(completableFuture);
136+
completableFuture.complete(httpClientConnection);
137+
138+
CompletableFuture<Void> executeFuture = requestExecutor.execute(context);
139+
Mockito.verifyZeroInteractions(responseHandler);
140+
}
141+
142+
@Test
143+
public void cancelRequest_shouldInvokeOnError() {
144+
CrtRequestContext context = CrtRequestContext.builder()
145+
.crtConnPool(connectionManager)
146+
.request(AsyncExecuteRequest.builder()
147+
.responseHandler(responseHandler)
148+
.build())
149+
.build();
150+
CompletableFuture<HttpClientConnection> completableFuture = new CompletableFuture<>();
151+
152+
Mockito.when(connectionManager.acquireConnection()).thenReturn(completableFuture);
153+
154+
CompletableFuture<Void> executeFuture = requestExecutor.execute(context);
155+
executeFuture.cancel(true);
156+
157+
ArgumentCaptor<Exception> argumentCaptor = ArgumentCaptor.forClass(Exception.class);
158+
Mockito.verify(responseHandler).onError(argumentCaptor.capture());
159+
160+
Exception actualException = argumentCaptor.getValue();
161+
assertThat(actualException).hasMessageContaining("The request was cancelled");
162+
assertThat(actualException).isInstanceOf(SdkCancellationException.class);
163+
}
113164
}

test/http-client-tests/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
<artifactId>http-client-spi</artifactId>
4949
<version>${awsjavasdk.version}</version>
5050
</dependency>
51+
<dependency>
52+
<groupId>software.amazon.awssdk</groupId>
53+
<artifactId>metrics-spi</artifactId>
54+
<version>${awsjavasdk.version}</version>
55+
</dependency>
5156
<dependency>
5257
<groupId>software.amazon.awssdk</groupId>
5358
<artifactId>utils</artifactId>

test/http-client-tests/src/main/java/software/amazon/awssdk/http/RecordingResponseHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public final class RecordingResponseHandler implements SdkAsyncHttpResponseHandl
3434

3535
@Override
3636
public void onHeaders(SdkHttpResponse response) {
37-
System.out.println("received response");
3837
responses.add(response);
3938
}
4039

0 commit comments

Comments
 (0)