diff --git a/src/main/java/software/amazon/awssdk/crt/s3/CrtS3RuntimeException.java b/src/main/java/software/amazon/awssdk/crt/s3/CrtS3RuntimeException.java deleted file mode 100644 index 384e55dbd..000000000 --- a/src/main/java/software/amazon/awssdk/crt/s3/CrtS3RuntimeException.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -package software.amazon.awssdk.crt.s3; - -import software.amazon.awssdk.crt.CrtRuntimeException; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class CrtS3RuntimeException extends CrtRuntimeException { - - private final int statusCode; - private final String awsErrorCode; - private final String awsErrorMessage; - private final String errorPayload; - - private final static String codeBeginBlock = new String(""); - private final static String codeEndBlock = new String(""); - private final static String messageBeginBlock = new String(""); - private final static String messageEndBlock = new String(""); - - public CrtS3RuntimeException(int errorCode, int responseStatus, String errorPayload) { - super(errorCode); - this.statusCode = responseStatus; - this.errorPayload = errorPayload; - this.awsErrorCode = GetElementFromPayload(errorPayload, codeBeginBlock, codeEndBlock); - this.awsErrorMessage = GetElementFromPayload(errorPayload, messageBeginBlock, messageEndBlock); - } - - public CrtS3RuntimeException(S3FinishedResponseContext context) { - super(context.getErrorCode()); - String errorString = new String(context.getErrorPayload(), java.nio.charset.StandardCharsets.UTF_8); - this.statusCode = context.getResponseStatus(); - this.errorPayload = errorString; - this.awsErrorCode = GetElementFromPayload(this.errorPayload, codeBeginBlock, codeEndBlock); - this.awsErrorMessage = GetElementFromPayload(this.errorPayload, messageBeginBlock, messageEndBlock); - } - - public CrtS3RuntimeException(int errorCode, int responseStatus, byte[] errorPayload) { - super(errorCode); - String errorString = new String(errorPayload, java.nio.charset.StandardCharsets.UTF_8); - this.statusCode = responseStatus; - this.errorPayload = errorString; - this.awsErrorCode = GetElementFromPayload(this.errorPayload, codeBeginBlock, codeEndBlock); - this.awsErrorMessage = GetElementFromPayload(this.errorPayload, messageBeginBlock, messageEndBlock); - } - - /** - * Helper function to get the detail of an element from xml payload. If not - * found, empty string will be returned. - */ - private String GetElementFromPayload(String errorPayload, String beginBlock, String endBlock) { - Pattern regexFormat = Pattern.compile(beginBlock + ".*" + endBlock); - Matcher matcher = regexFormat.matcher(errorPayload); - String result = ""; - if (matcher.find()) { - result = errorPayload.substring(matcher.start() + beginBlock.length(), matcher.end() - endBlock.length()); - } - return result; - } - - /** - * Returns the aws error code from S3 response. The {@code Code} element in xml - * response. - * - * @return errorCode, if no {@code Code} element in the response, empty string will be - * returned - */ - public String getAwsErrorCode() { - return awsErrorCode; - } - - /** - * Returns the error message from S3 response. The detail among {@code Message} - * element in xml response. - * - * @return error message, if no {@code Message} element in the response, empty string - * will be returned - */ - public String getAwsErrorMessage() { - return awsErrorMessage; - } - - /** - * Returns the status code in S3 response. - * - * @return status code in int - */ - public int getStatusCode() { - return statusCode; - - } - @Override - public String toString() { - return String.format("%s: response status code(%d). aws error code(%s), aws error message(%s)", super.toString(), statusCode, awsErrorCode, awsErrorMessage); - } -} diff --git a/src/main/java/software/amazon/awssdk/crt/s3/S3FinishedResponseContext.java b/src/main/java/software/amazon/awssdk/crt/s3/S3FinishedResponseContext.java index db5d8e3b7..4972f11bf 100644 --- a/src/main/java/software/amazon/awssdk/crt/s3/S3FinishedResponseContext.java +++ b/src/main/java/software/amazon/awssdk/crt/s3/S3FinishedResponseContext.java @@ -11,22 +11,26 @@ public class S3FinishedResponseContext { private final ChecksumAlgorithm checksumAlgorithm; private final boolean didValidateChecksum; + private final Throwable cause; + /* * errorCode The CRT error code * responseStatus statusCode of the HTTP response * errorPayload body of the error response. Can be null if the request completed successfully * checksumAlgorithm, the algorithm used to validate the Body, None if not validated * didValidateChecksum which is true if the response was validated. + * cause of the error such as a Java exception in a callback. Maybe NULL if there was no exception in a callback. */ - S3FinishedResponseContext(final int errorCode, final int responseStatus, final byte[] errorPayload, final ChecksumAlgorithm checksumAlgorithm, final boolean didValidateChecksum) { + S3FinishedResponseContext(final int errorCode, final int responseStatus, final byte[] errorPayload, final ChecksumAlgorithm checksumAlgorithm, final boolean didValidateChecksum, Throwable cause) { this.errorCode = errorCode; this.responseStatus = responseStatus; this.errorPayload = errorPayload; this.checksumAlgorithm = checksumAlgorithm; this.didValidateChecksum = didValidateChecksum; + this.cause = cause; } - public int getErrorCode () { + public int getErrorCode() { return this.errorCode; } @@ -34,14 +38,14 @@ public int getErrorCode () { * If the request didn't receive a response due to a connection * failure or some other issue the response status will be 0. */ - public int getResponseStatus () { + public int getResponseStatus() { return this.responseStatus; } /* * In the case of a failed http response get the payload of the response. */ - public byte[] getErrorPayload () { + public byte[] getErrorPayload() { return this.errorPayload; } @@ -49,10 +53,20 @@ public byte[] getErrorPayload () { * if no checksum is found, or the request finished with an error the Algorithm will be None, * otherwise the algorithm will correspond to the one attached to the object when uploaded. */ - public ChecksumAlgorithm getChecksumAlgorithm () { + public ChecksumAlgorithm getChecksumAlgorithm() { return this.checksumAlgorithm; } - public boolean isChecksumValidated () { + + public boolean isChecksumValidated() { return this.didValidateChecksum; } + + /** + * Cause of the error, such as a Java exception from a callback. May be NULL if there was no exception in a callback. + * @return throwable + */ + public Throwable getCause() { + return cause; + } + } diff --git a/src/main/java/software/amazon/awssdk/crt/s3/S3MetaRequestResponseHandlerNativeAdapter.java b/src/main/java/software/amazon/awssdk/crt/s3/S3MetaRequestResponseHandlerNativeAdapter.java index 9c85883bf..2e5e453d7 100644 --- a/src/main/java/software/amazon/awssdk/crt/s3/S3MetaRequestResponseHandlerNativeAdapter.java +++ b/src/main/java/software/amazon/awssdk/crt/s3/S3MetaRequestResponseHandlerNativeAdapter.java @@ -19,8 +19,8 @@ int onResponseBody(byte[] bodyBytesIn, long objectRangeStart, long objectRangeEn return this.responseHandler.onResponseBody(ByteBuffer.wrap(bodyBytesIn), objectRangeStart, objectRangeEnd); } - void onFinished(int errorCode, int responseStatus, byte[] errorPayload, int checksumAlgorithm, boolean didValidateChecksum) { - S3FinishedResponseContext context = new S3FinishedResponseContext(errorCode, responseStatus, errorPayload, ChecksumAlgorithm.getEnumValueFromInteger(checksumAlgorithm), didValidateChecksum); + void onFinished(int errorCode, int responseStatus, byte[] errorPayload, int checksumAlgorithm, boolean didValidateChecksum, Throwable cause) { + S3FinishedResponseContext context = new S3FinishedResponseContext(errorCode, responseStatus, errorPayload, ChecksumAlgorithm.getEnumValueFromInteger(checksumAlgorithm), didValidateChecksum, cause); this.responseHandler.onFinished(context); } diff --git a/src/native/crt.c b/src/native/crt.c index 1c1863c28..a7bfe79f2 100644 --- a/src/native/crt.c +++ b/src/native/crt.c @@ -286,6 +286,16 @@ bool aws_jni_check_and_clear_exception(JNIEnv *env) { return exception_pending; } +bool aws_jni_get_and_clear_exception(JNIEnv *env, jthrowable *out) { + bool exception_pending = (*env)->ExceptionCheck(env); + if (exception_pending) { + (*env)->DeleteGlobalRef(env, *out); + *out = (jthrowable)(*env)->NewGlobalRef(env, (*env)->ExceptionOccurred(env)); + (*env)->ExceptionClear(env); + } + return exception_pending; +} + int aws_size_t_from_java(JNIEnv *env, size_t *out_size, jlong java_long, const char *errmsg_prefix) { if (java_long < 0) { aws_jni_throw_illegal_argument_exception(env, "%s cannot be negative", errmsg_prefix); diff --git a/src/native/crt.h b/src/native/crt.h index 30868039f..817aa6b5f 100644 --- a/src/native/crt.h +++ b/src/native/crt.h @@ -59,6 +59,21 @@ void aws_jni_throw_illegal_argument_exception(JNIEnv *env, const char *msg, ...) ******************************************************************************/ bool aws_jni_check_and_clear_exception(JNIEnv *env); +/******************************************************************************* + * Checks whether or not an exception is pending on the stack. + * If the exception is pending, deletes existing global reference of `out`, sets `out` to the new exception and clears + * it. + * + * @param env A pointer to the JNI environment, used to interact with the JVM. + * @param out A pointer to a jthrowable object. If an exception is pending, the function + * deletes any existing global reference pointed to by 'out', and sets 'out' + * to point to the new exception. Must not be NULL. + * + * @return true if an exception was pending and has been cleared; false otherwise. + * + ******************************************************************************/ +bool aws_jni_get_and_clear_exception(JNIEnv *env, jthrowable *out); + /******************************************************************************* * Set a size_t based on a jlong. * If conversion fails, a java IllegalArgumentException is thrown like diff --git a/src/native/java_class_ids.c b/src/native/java_class_ids.c index 8e0365689..53ed5adbc 100644 --- a/src/native/java_class_ids.c +++ b/src/native/java_class_ids.c @@ -693,7 +693,7 @@ static void s_cache_s3_meta_request_response_handler_native_adapter_properties(J AWS_FATAL_ASSERT(s3_meta_request_response_handler_native_adapter_properties.onResponseBody); s3_meta_request_response_handler_native_adapter_properties.onFinished = - (*env)->GetMethodID(env, cls, "onFinished", "(II[BIZ)V"); + (*env)->GetMethodID(env, cls, "onFinished", "(II[BIZLjava/lang/Throwable;)V"); AWS_FATAL_ASSERT(s3_meta_request_response_handler_native_adapter_properties.onFinished); s3_meta_request_response_handler_native_adapter_properties.onResponseHeaders = diff --git a/src/native/s3_client.c b/src/native/s3_client.c index 31a5bedb5..feb3225e1 100644 --- a/src/native/s3_client.c +++ b/src/native/s3_client.c @@ -46,6 +46,7 @@ struct s3_client_make_meta_request_callback_data { jobject java_s3_meta_request_response_handler_native_adapter; struct aws_input_stream *input_stream; struct aws_signing_config_data signing_config_data; + jthrowable java_exception; }; static void s_on_s3_client_shutdown_complete_callback(void *user_data); @@ -354,10 +355,10 @@ static int s_on_s3_meta_request_body_callback( range_start, range_end); - if (aws_jni_check_and_clear_exception(env)) { + if (aws_jni_get_and_clear_exception(env, &(callback_data->java_exception))) { AWS_LOGF_ERROR( AWS_LS_S3_META_REQUEST, - "id=%p: Ignored Exception from S3MetaRequest.onResponseBody callback", + "id=%p: Received exception from S3MetaRequest.onResponseBody callback", (void *)meta_request); aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE); goto cleanup; @@ -432,7 +433,7 @@ static int s_on_s3_meta_request_headers_callback( response_status, java_headers_buffer); - if (aws_jni_check_and_clear_exception(env)) { + if (aws_jni_get_and_clear_exception(env, &(callback_data->java_exception))) { AWS_LOGF_ERROR( AWS_LS_S3_META_REQUEST, "id=%p: Exception thrown from S3MetaRequest.onResponseHeaders callback", @@ -481,6 +482,10 @@ static void s_on_s3_meta_request_finish_callback( error_response_cursor = aws_byte_cursor_from_buf(error_response_body); } jbyteArray jni_payload = aws_jni_byte_array_from_cursor(env, &error_response_cursor); + /* Only propagate java_exception if crt error code is callback failure */ + jthrowable java_exception = + meta_request_result->error_code == AWS_ERROR_HTTP_CALLBACK_FAILURE ? callback_data->java_exception : NULL; + (*env)->CallVoidMethod( env, callback_data->java_s3_meta_request_response_handler_native_adapter, @@ -489,7 +494,8 @@ static void s_on_s3_meta_request_finish_callback( meta_request_result->response_status, jni_payload, meta_request_result->validation_algorithm, - meta_request_result->did_validate); + meta_request_result->did_validate, + java_exception); if (aws_jni_check_and_clear_exception(env)) { AWS_LOGF_ERROR( @@ -571,6 +577,7 @@ static void s_s3_meta_request_callback_cleanup( if (callback_data) { (*env)->DeleteGlobalRef(env, callback_data->java_s3_meta_request); (*env)->DeleteGlobalRef(env, callback_data->java_s3_meta_request_response_handler_native_adapter); + (*env)->DeleteGlobalRef(env, callback_data->java_exception); aws_signing_config_data_clean_up(&callback_data->signing_config_data, env); aws_mem_release(aws_jni_get_allocator(), callback_data); } diff --git a/src/test/java/software/amazon/awssdk/crt/test/S3ClientTest.java b/src/test/java/software/amazon/awssdk/crt/test/S3ClientTest.java index ddf38ad9c..6e3e1684d 100644 --- a/src/test/java/software/amazon/awssdk/crt/test/S3ClientTest.java +++ b/src/test/java/software/amazon/awssdk/crt/test/S3ClientTest.java @@ -3,6 +3,7 @@ import org.junit.Assert; import org.junit.Assume; import org.junit.Test; +import software.amazon.awssdk.crt.CrtRuntimeException; import software.amazon.awssdk.crt.Log; import software.amazon.awssdk.crt.auth.credentials.CredentialsProvider; import software.amazon.awssdk.crt.auth.credentials.DefaultChainCredentialsProvider; @@ -16,6 +17,7 @@ import software.amazon.awssdk.crt.utils.ByteBufferUtils; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import java.io.File; import java.io.FileNotFoundException; @@ -91,6 +93,14 @@ private S3Client createS3Client(S3ClientOptions options) { return createS3Client(options, 1); } + private RuntimeException makeExceptionFromFinishedResponseContext(S3FinishedResponseContext context) { + return new RuntimeException(String.format("error code:(%d) response status code(%d), error payload(%s)", + context.getErrorCode(), + context.getResponseStatus(), + new String(context.getErrorPayload(), java.nio.charset.StandardCharsets.UTF_8), + context.getCause())); + } + @Test public void testS3ClientCreateDestroy() { skipIfAndroid(); @@ -271,7 +281,6 @@ public void testS3Get() { skipIfAndroid(); skipIfNetworkUnavailable(); Assume.assumeTrue(hasAwsCredentials()); - S3ClientOptions clientOptions = new S3ClientOptions().withRegion(REGION); try (S3Client client = createS3Client(clientOptions)) { CompletableFuture onFinishedFuture = new CompletableFuture<>(); @@ -290,9 +299,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -314,6 +321,49 @@ public void onFinished(S3FinishedResponseContext context) { } } + @Test + public void testS3CallbackExceptionIsProperlyPropagated() { + skipIfAndroid(); + skipIfNetworkUnavailable(); + Assume.assumeTrue(hasAwsCredentials()); + S3ClientOptions clientOptions = new S3ClientOptions().withRegion(REGION); + RuntimeException expectedException = new RuntimeException("Exception From a Java Function"); + + try (S3Client client = createS3Client(clientOptions)) { + CompletableFuture onFinishedFuture = new CompletableFuture<>(); + S3MetaRequestResponseHandler responseHandler = new S3MetaRequestResponseHandler() { + + @Override + public int onResponseBody(ByteBuffer bodyBytesIn, long objectRangeStart, long objectRangeEnd) { + throw expectedException; + } + + @Override + public void onFinished(S3FinishedResponseContext context) { + Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, + "Meta request finished with error code " + context.getErrorCode()); + if (context.getErrorCode() != 0) { + onFinishedFuture.completeExceptionally(context.getCause()); + return; + } + onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); + } + }; + + HttpHeader[] headers = { new HttpHeader("Host", ENDPOINT) }; + HttpRequest httpRequest = new HttpRequest("GET", "/get_object_test_1MB.txt", headers, null); + + S3MetaRequestOptions metaRequestOptions = new S3MetaRequestOptions() + .withMetaRequestType(MetaRequestType.GET_OBJECT).withHttpRequest(httpRequest) + .withResponseHandler(responseHandler); + + try (S3MetaRequest metaRequest = client.makeMetaRequest(metaRequestOptions)) { + ExecutionException ex = assertThrows(ExecutionException.class, () -> onFinishedFuture.get()); + Assert.assertSame(expectedException, ex.getCause()); + } + } + } + @Test public void testS3GetWithEndpoint() { skipIfAndroid(); @@ -338,9 +388,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -532,9 +580,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(new CrtRuntimeException(context.getErrorCode())); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -555,7 +601,7 @@ public void onFinished(S3FinishedResponseContext context) { /* * Maybe better to have a cause of the max retries exceed to be more informative */ - if (!(ex.getCause() instanceof CrtS3RuntimeException)) { + if (!(ex.getCause() instanceof CrtRuntimeException)) { Assert.fail(ex.getMessage()); } } @@ -585,9 +631,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -647,9 +691,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -768,7 +810,7 @@ public void testS3PutNonexistentFilePath() throws IOException { .withResponseHandler(responseHandler); // makeMetaRequest() should fail - Throwable thrown = Assert.assertThrows(Throwable.class, + Throwable thrown = assertThrows(Throwable.class, () -> client.makeMetaRequest(metaRequestOptions)); // exception should indicate the file doesn't exist @@ -791,9 +833,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(new CrtRuntimeException(context.getErrorCode())); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -858,10 +898,10 @@ public long getLength() { resumeToken = metaRequest.pause(); Assert.assertNotNull(resumeToken); - Throwable thrown = Assert.assertThrows(Throwable.class, + Throwable thrown = assertThrows(Throwable.class, () -> onFinishedFuture.get()); - Assert.assertEquals("AWS_ERROR_S3_PAUSED", ((CrtS3RuntimeException) thrown.getCause()).errorName); + Assert.assertEquals("AWS_ERROR_S3_PAUSED", ((CrtRuntimeException) thrown.getCause()).errorName); } final ByteBuffer payloadResume = ByteBuffer.wrap(createTestPayload(128 * 1024 * 1024)); @@ -936,9 +976,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onPutFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onPutFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } onPutFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -1000,9 +1038,7 @@ public void onFinished(S3FinishedResponseContext context) { Log.log(Log.LogLevel.Info, Log.LogSubject.JavaCrtS3, "Meta request finished with error code " + context.getErrorCode()); if (context.getErrorCode() != 0) { - onGetFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onGetFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } if (!context.isChecksumValidated()) { @@ -1049,9 +1085,7 @@ public void testS3GetChecksums() { @Override public void onFinished(S3FinishedResponseContext context) { if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } if (!context.isChecksumValidated()) { @@ -1128,9 +1162,7 @@ public void onFinished(S3FinishedResponseContext context) { if (context.getErrorCode() != 0) { System.out.println("Test failed with error payload: " + new String(context.getErrorPayload(), StandardCharsets.UTF_8)); - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } onFinishedFuture.complete(Integer.valueOf(context.getErrorCode())); @@ -1333,9 +1365,7 @@ public void onFinished(S3FinishedResponseContext context) { concurrentSlots.release(); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; } @@ -1464,9 +1494,7 @@ public void onFinished(S3FinishedResponseContext context) { concurrentSlots.release(); if (context.getErrorCode() != 0) { - onFinishedFuture.completeExceptionally( - new CrtS3RuntimeException(context.getErrorCode(), context.getResponseStatus(), - context.getErrorPayload())); + onFinishedFuture.completeExceptionally(makeExceptionFromFinishedResponseContext(context)); return; }