Skip to content

Provide HTTP request-level data in PutObjectResponse #2548

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ public void upload_fileSentCorrectly() throws IOException {
.source(testFile.toPath())
.build());

upload.completionFuture().join();
CompletedUpload completedUpload = upload.completionFuture().join();
assertThat(completedUpload.response().responseMetadata().requestId()).isNotNull();
assertThat(completedUpload.response().sdkHttpResponse()).isNotNull();

ResponseInputStream<GetObjectResponse> obj = s3.getObject(r -> r.bucket(TEST_BUCKET).key(TEST_KEY),
ResponseTransformer.toInputStream());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@

package software.amazon.awssdk.transfer.s3.internal;


import com.amazonaws.s3.RequestDataSupplier;
import com.amazonaws.s3.S3NativeClient;
import com.amazonaws.s3.model.PutObjectOutput;
import java.util.concurrent.CompletableFuture;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
Expand Down Expand Up @@ -85,14 +84,19 @@ public CompletableFuture<PutObjectResponse> putObject(PutObjectRequest putObject
com.amazonaws.s3.model.PutObjectRequest adaptedRequest = S3CrtPojoConversion.toCrtPutObjectRequest(putObjectRequest);

if (adaptedRequest.contentLength() == null && requestBody.contentLength().isPresent()) {
adaptedRequest = adaptedRequest.toBuilder().contentLength(requestBody.contentLength().get())
.build();
adaptedRequest = adaptedRequest.toBuilder()
.contentLength(requestBody.contentLength().get())
.build();
}

RequestDataSupplierAdapter requestDataSupplier = new RequestDataSupplierAdapter(requestBody);
CompletableFuture<PutObjectOutput> putObjectOutputCompletableFuture = s3NativeClient.putObject(adaptedRequest,
adaptToDataSupplier(requestBody));
requestDataSupplier);

return putObjectOutputCompletableFuture.thenApply(S3CrtPojoConversion::fromCrtPutObjectOutput);
CompletableFuture<SdkHttpResponse> httpResponseFuture = requestDataSupplier.sdkHttpResponseFuture();
return httpResponseFuture.thenCombine(putObjectOutputCompletableFuture,
(header, putObjectOutput) ->
S3CrtPojoConversion.fromCrtPutObjectOutput(putObjectOutput, header));
}

@Override
Expand All @@ -106,10 +110,6 @@ public void close() {
configuration.close();
}

private static RequestDataSupplier adaptToDataSupplier(AsyncRequestBody requestBody) {
return new RequestDataSupplierAdapter(requestBody);
}

public static final class DefaultS3CrtClientBuilder implements S3CrtAsyncClientBuilder {
private AwsCredentialsProvider credentialsProvider;
private Region region;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Deque;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
Expand All @@ -30,6 +31,7 @@
import org.reactivestreams.Subscription;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.crt.http.HttpHeader;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.utils.Logger;

/**
Expand Down Expand Up @@ -62,6 +64,10 @@ public RequestDataSupplierAdapter(Publisher<ByteBuffer> bodyPublisher) {
this.headersHandler = new ResponseHeadersHandler();
}

public CompletableFuture<SdkHttpResponse> sdkHttpResponseFuture() {
return headersHandler.sdkHttpResponseFuture();
}

@Override
public void onResponseHeaders(final int statusCode, final HttpHeader[] headers) {
headersHandler.onResponseHeaders(statusCode, headers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ public static com.amazonaws.s3.model.PutObjectRequest toCrtPutObjectRequest(PutO
return putObjectBuilder.build();
}

public static PutObjectResponse fromCrtPutObjectOutput(PutObjectOutput crtPutObjectOutput) {
// TODO: Provide the HTTP request-level data (e.g. response metadata, HTTP response)
public static PutObjectResponse fromCrtPutObjectOutput(PutObjectOutput crtPutObjectOutput,
SdkHttpResponse sdkHttpResponse) {
S3ResponseMetadata s3ResponseMetadata = createS3ResponseMetadata(sdkHttpResponse);
PutObjectResponse.Builder builder = PutObjectResponse.builder()
.bucketKeyEnabled(crtPutObjectOutput.bucketKeyEnabled())
.eTag(crtPutObjectOutput.eTag())
Expand All @@ -232,7 +233,9 @@ public static PutObjectResponse fromCrtPutObjectOutput(PutObjectOutput crtPutObj
builder.serverSideEncryption(crtPutObjectOutput.serverSideEncryption().name());
}

return builder.build();
return (PutObjectResponse) builder.responseMetadata(s3ResponseMetadata)
.sdkHttpResponse(sdkHttpResponse)
.build();
}

private static S3ResponseMetadata createS3ResponseMetadata(SdkHttpResponse sdkHttpResponse) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,14 @@
public class S3CrtPojoConversionTest {
private static final Logger log = Logger.loggerFor(S3CrtPojoConversionTest.class);
private static final Random RNG = new Random();
private static final String ACCESS_KEY = "accessKey";
private static final String SECRET_ACCESS_KEY = "secretAccessKey";
private static final String SESSION_TOKEN = "sessionToken";

@Test
public void fromCrtPutObjectOutputAllFields_shouldConvert() throws IllegalAccessException {

PutObjectOutput crtResponse = randomCrtPutObjectOutput();
PutObjectResponse sdkResponse = S3CrtPojoConversion.fromCrtPutObjectOutput(crtResponse);
SdkHttpResponse sdkHttpResponse = SdkHttpResponse.builder()
.build();
PutObjectResponse sdkResponse = S3CrtPojoConversion.fromCrtPutObjectOutput(crtResponse, sdkHttpResponse);

// ignoring fields with different casings and enum fields.
assertThat(sdkResponse).isEqualToIgnoringGivenFields(crtResponse,
Expand All @@ -86,6 +85,30 @@ public void fromCrtPutObjectOutputAllFields_shouldConvert() throws IllegalAccess
//assertThat(sdkResponse.requestCharged().name()).isEqualTo(crtResponse.requestCharged().name());
}

@Test
public void fromCrtPutObjectOutputAllFields_shouldAddSdkHttpResponse() throws IllegalAccessException {
String expectedRequestId = "123456";
PutObjectOutput crtResponse = PutObjectOutput.builder().build();
SdkHttpResponse sdkHttpResponse = SdkHttpResponse.builder()
.statusCode(200)
.appendHeader("x-amz-request-id", expectedRequestId)
.build();
PutObjectResponse sdkResponse = S3CrtPojoConversion.fromCrtPutObjectOutput(crtResponse, sdkHttpResponse);

// ignoring fields with different casing and enum fields.
assertThat(sdkResponse).isEqualToIgnoringGivenFields(crtResponse,
"sseCustomerAlgorithm",
"sseCustomerKeyMD5",
"ssekmsKeyId",
"ssekmsEncryptionContext",
"serverSideEncryption",
"requestCharged",
"responseMetadata",
"sdkHttpResponse");
assertThat(sdkResponse.sdkHttpResponse()).isEqualTo(sdkHttpResponse);
assertThat(sdkResponse.responseMetadata().requestId()).isEqualTo(expectedRequestId);
}

@Test
public void fromCrtGetObjectOutput_shouldAddSdkHttpResponse() {
String expectedRequestId = "123456";
Expand Down