Skip to content

Commit 4359491

Browse files
author
Dhriti Chopra
committed
feat: Adding custom checksum for upload part request
1 parent feb1912 commit 4359491

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

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

Lines changed: 10 additions & 0 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+
if (request.getCrc32c() != null) {
170+
addChecksumHeader(request.getCrc32c(), httpRequest.getHeaders());
171+
} else {
169172
addChecksumHeader(rewindableContent.getCrc32c(), httpRequest.getHeaders());
173+
}
170174
httpRequest.setThrowExceptionOnExecuteError(true);
171175
return ChecksumResponseParser.parseUploadResponse(httpRequest.execute());
172176
}
@@ -199,6 +203,12 @@ private void addChecksumHeader(@Nullable Crc32cLengthKnown crc32c, HttpHeaders h
199203
}
200204
}
201205

206+
private void addChecksumHeader(@Nullable String crc32c, HttpHeaders headers) {
207+
if (crc32c != null) {
208+
headers.put("x-goog-hash", "crc32c=" + crc32c);
209+
}
210+
}
211+
202212
private void addHeadersForCreateMultipartUpload(
203213
CreateMultipartUploadRequest request, HttpHeaders headers) {
204214
if (request.getCannedAcl() != null) {

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.60.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.60.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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
2626
import com.google.api.client.http.HttpResponseException;
2727
import com.google.cloud.NoCredentials;
28+
import com.google.cloud.storage.Crc32cValue.Crc32cLengthKnown;
2829
import com.google.cloud.storage.FakeHttpServer.HttpRequestHandler;
2930
import com.google.cloud.storage.it.runner.StorageITRunner;
3031
import com.google.cloud.storage.it.runner.annotations.Backend;
@@ -813,6 +814,48 @@ public void sendUploadPartRequest_withChecksums() throws Exception {
813814
}
814815
}
815816

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

0 commit comments

Comments
 (0)