Skip to content

Commit 6f4d255

Browse files
Dhriti07Dhriti Chopra
andauthored
feat: add UploadPartRequest.crc32c property and requisite plumbing (googleapis#3395)
Co-authored-by: Dhriti Chopra <[email protected]>
1 parent 39849bf commit 6f4d255

File tree

3 files changed

+84
-4
lines changed

3 files changed

+84
-4
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/MultipartUploadHttpRequestManager.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,11 @@ UploadPartResponse sendUploadPartRequest(
166166
HttpRequest httpRequest =
167167
requestFactory.buildPutRequest(new GenericUrl(uploadUri), rewindableContent);
168168
httpRequest.getHeaders().putAll(headerProvider.getHeaders());
169-
addChecksumHeader(rewindableContent.getCrc32c(), httpRequest.getHeaders());
169+
if (request.getCrc32c() != null) {
170+
addChecksumHeader(request.getCrc32c(), httpRequest.getHeaders());
171+
} else {
172+
addChecksumHeader(rewindableContent.getCrc32c(), httpRequest.getHeaders());
173+
}
170174
httpRequest.setThrowExceptionOnExecuteError(true);
171175
return ChecksumResponseParser.parseUploadResponse(httpRequest.execute());
172176
}
@@ -195,7 +199,13 @@ static MultipartUploadHttpRequestManager createFrom(HttpStorageOptions options)
195199

196200
private void addChecksumHeader(@Nullable Crc32cLengthKnown crc32c, HttpHeaders headers) {
197201
if (crc32c != null) {
198-
headers.put("x-goog-hash", "crc32c=" + Utils.crc32cCodec.encode(crc32c.getValue()));
202+
addChecksumHeader(Utils.crc32cCodec.encode(crc32c.getValue()), headers);
203+
}
204+
}
205+
206+
private void addChecksumHeader(@Nullable String crc32c, HttpHeaders headers) {
207+
if (crc32c != null) {
208+
headers.put("x-goog-hash", "crc32c=" + crc32c);
199209
}
200210
}
201211

google-cloud-storage/src/main/java/com/google/cloud/storage/multipartupload/model/UploadPartRequest.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.api.core.BetaApi;
2020
import com.google.common.base.MoreObjects;
2121
import java.util.Objects;
22+
import org.checkerframework.checker.nullness.qual.Nullable;
2223

2324
/**
2425
* An object to represent an upload part request. An upload part request is used to upload a single
@@ -33,12 +34,14 @@ public final class UploadPartRequest {
3334
private final String key;
3435
private final int partNumber;
3536
private final String uploadId;
37+
@Nullable private final String crc32c;
3638

3739
private UploadPartRequest(Builder builder) {
3840
this.bucket = builder.bucket;
3941
this.key = builder.key;
4042
this.partNumber = builder.partNumber;
4143
this.uploadId = builder.uploadId;
44+
this.crc32c = builder.crc32c;
4245
}
4346

4447
/**
@@ -85,6 +88,18 @@ public String uploadId() {
8588
return uploadId;
8689
}
8790

91+
/**
92+
* Returns the CRC32C checksum of the part to upload.
93+
*
94+
* @return The CRC32C checksum of the part to upload.
95+
* @since 2.61.0 This new api is in preview and is subject to breaking changes.
96+
*/
97+
@BetaApi
98+
@Nullable
99+
public String getCrc32c() {
100+
return crc32c;
101+
}
102+
88103
@Override
89104
public boolean equals(Object o) {
90105
if (this == o) {
@@ -97,12 +112,13 @@ public boolean equals(Object o) {
97112
return partNumber == that.partNumber
98113
&& Objects.equals(bucket, that.bucket)
99114
&& Objects.equals(key, that.key)
100-
&& Objects.equals(uploadId, that.uploadId);
115+
&& Objects.equals(uploadId, that.uploadId)
116+
&& Objects.equals(crc32c, that.crc32c);
101117
}
102118

103119
@Override
104120
public int hashCode() {
105-
return Objects.hash(bucket, key, partNumber, uploadId);
121+
return Objects.hash(bucket, key, partNumber, uploadId, crc32c);
106122
}
107123

108124
@Override
@@ -112,6 +128,7 @@ public String toString() {
112128
.add("key", key)
113129
.add("partNumber", partNumber)
114130
.add("uploadId", uploadId)
131+
.add("crc32c", crc32c)
115132
.toString();
116133
}
117134

@@ -137,6 +154,7 @@ public static class Builder {
137154
private String key;
138155
private int partNumber;
139156
private String uploadId;
157+
@Nullable private String crc32c;
140158

141159
private Builder() {}
142160

@@ -192,6 +210,19 @@ public Builder uploadId(String uploadId) {
192210
return this;
193211
}
194212

213+
/**
214+
* Sets the CRC32C checksum of the part to upload.
215+
*
216+
* @param crc32c The CRC32C checksum of the part to upload.
217+
* @return This builder.
218+
* @since 2.61.0 This new api is in preview and is subject to breaking changes.
219+
*/
220+
@BetaApi
221+
public Builder crc32c(@Nullable String crc32c) {
222+
this.crc32c = crc32c;
223+
return this;
224+
}
225+
195226
/**
196227
* Builds the {@link UploadPartRequest}.
197228
*

google-cloud-storage/src/test/java/com/google/cloud/storage/ITMultipartUploadHttpRequestManagerTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.api.client.http.HttpResponseException;
2727
import com.google.cloud.NoCredentials;
2828
import com.google.cloud.storage.FakeHttpServer.HttpRequestHandler;
29+
import com.google.cloud.storage.it.ChecksummedTestContent;
2930
import com.google.cloud.storage.it.runner.StorageITRunner;
3031
import com.google.cloud.storage.it.runner.annotations.Backend;
3132
import com.google.cloud.storage.it.runner.annotations.ParallelFriendly;
@@ -813,6 +814,44 @@ public void sendUploadPartRequest_withChecksums() throws Exception {
813814
}
814815
}
815816

817+
@Test
818+
public void sendUploadPartRequest_withCustomChecksum() throws Exception {
819+
String etag = "\"af1ed31420542285653c803a34aa839a\"";
820+
ChecksummedTestContent content =
821+
ChecksummedTestContent.of("hello world".getBytes(StandardCharsets.UTF_8));
822+
823+
HttpRequestHandler handler =
824+
req -> {
825+
assertThat(req.headers().get("x-goog-hash"))
826+
.isEqualTo("crc32c=" + content.getCrc32cBase64());
827+
FullHttpRequest fullReq = (FullHttpRequest) req;
828+
ByteBuf requestContent = fullReq.content();
829+
byte[] receivedBytes = new byte[requestContent.readableBytes()];
830+
requestContent.readBytes(receivedBytes);
831+
assertThat(receivedBytes).isEqualTo(content.getBytes());
832+
DefaultFullHttpResponse resp = new DefaultFullHttpResponse(req.protocolVersion(), OK);
833+
resp.headers().set("ETag", etag);
834+
return resp;
835+
};
836+
837+
try (FakeHttpServer fakeHttpServer = FakeHttpServer.of(handler)) {
838+
URI endpoint = URI.create(fakeHttpServer.getEndpoint() + "/");
839+
UploadPartRequest request =
840+
UploadPartRequest.builder()
841+
.bucket("test-bucket")
842+
.key("test-key")
843+
.uploadId("test-upload-id")
844+
.partNumber(1)
845+
.crc32c(content.getCrc32cBase64())
846+
.build();
847+
UploadPartResponse response =
848+
multipartUploadHttpRequestManager.sendUploadPartRequest(
849+
endpoint, request, RewindableContent.of(content.asByteBuffer()));
850+
assertThat(response).isNotNull();
851+
assertThat(response.eTag()).isEqualTo(etag);
852+
}
853+
}
854+
816855
@Test
817856
public void sendUploadPartRequest_error() throws Exception {
818857
HttpRequestHandler handler =

0 commit comments

Comments
 (0)