Skip to content

Commit 744e991

Browse files
authored
chore: added crc to upload part
2 parents e030050 + 59017bd commit 744e991

File tree

3 files changed

+82
-17
lines changed

3 files changed

+82
-17
lines changed

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

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,25 @@
2222
import java.io.IOException;
2323
import java.util.Arrays;
2424
import java.util.Collections;
25+
import java.util.List;
2526
import java.util.Map;
26-
import java.util.Optional;
2727
import java.util.stream.Collectors;
2828

29-
/** A utility class to parse {@link HttpResponse} and create a {@link UploadPartResponse}. */
29+
/** A utility class to parse checksums from an {@link HttpResponse}. */
3030
final class ChecksumResponseParser {
3131

32+
private static final String X_GOOG_HASH = "x-goog-hash";
33+
3234
private ChecksumResponseParser() {}
3335

3436
static UploadPartResponse parseUploadResponse(HttpResponse response) {
3537
String eTag = response.getHeaders().getETag();
3638
Map<String, String> hashes = extractHashesFromHeader(response);
37-
return UploadPartResponse.builder().eTag(eTag).md5(hashes.get("md5")).build();
39+
return UploadPartResponse.builder()
40+
.eTag(eTag)
41+
.md5(hashes.get("md5"))
42+
.crc32c(hashes.get("crc32c"))
43+
.build();
3844
}
3945

4046
static CompleteMultipartUploadResponse parseCompleteResponse(HttpResponse response)
@@ -52,14 +58,18 @@ static CompleteMultipartUploadResponse parseCompleteResponse(HttpResponse respon
5258
}
5359

5460
static Map<String, String> extractHashesFromHeader(HttpResponse response) {
55-
return Optional.ofNullable(response.getHeaders().getFirstHeaderStringValue("x-goog-hash"))
56-
.map(
57-
h ->
58-
Arrays.stream(h.split(","))
59-
.map(s -> s.trim().split("=", 2))
60-
.filter(a -> a.length == 2)
61-
.filter(a -> "crc32c".equalsIgnoreCase(a[0]) || "md5".equalsIgnoreCase(a[0]))
62-
.collect(Collectors.toMap(a -> a[0].toLowerCase(), a -> a[1], (v1, v2) -> v1)))
63-
.orElse(Collections.emptyMap());
61+
List<String> hashHeaders = response.getHeaders().getHeaderStringValues(X_GOOG_HASH);
62+
if (hashHeaders == null || hashHeaders.isEmpty()) {
63+
return Collections.emptyMap();
64+
}
65+
66+
return hashHeaders.stream()
67+
.flatMap(h -> Arrays.stream(h.split(",")))
68+
.map(String::trim)
69+
.filter(s -> !s.isEmpty())
70+
.map(s -> s.split("=", 2))
71+
.filter(a -> a.length == 2)
72+
.filter(a -> "crc32c".equalsIgnoreCase(a[0]) || "md5".equalsIgnoreCase(a[0]))
73+
.collect(Collectors.toMap(a -> a[0].toLowerCase(), a -> a[1], (v1, v2) -> v1));
6474
}
6575
}

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

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ public final class UploadPartResponse {
3131

3232
private final String eTag;
3333
private final String md5;
34+
private final String crc32c;
3435

3536
private UploadPartResponse(Builder builder) {
3637
this.eTag = builder.etag;
3738
this.md5 = builder.md5;
39+
this.crc32c = builder.crc32c;
3840
}
3941

4042
/**
@@ -59,6 +61,17 @@ public String md5() {
5961
return md5;
6062
}
6163

64+
/**
65+
* Returns the CRC32C checksum of the uploaded part.
66+
*
67+
* @return The CRC32C checksum.
68+
* @since 2.61.0 This new api is in preview and is subject to breaking changes.
69+
*/
70+
@BetaApi
71+
public String crc32c() {
72+
return crc32c;
73+
}
74+
6275
@Override
6376
public boolean equals(Object o) {
6477
if (this == o) {
@@ -68,17 +81,23 @@ public boolean equals(Object o) {
6881
return false;
6982
}
7083
UploadPartResponse that = (UploadPartResponse) o;
71-
return Objects.equals(eTag, that.eTag) && Objects.equals(md5, that.md5);
84+
return Objects.equals(eTag, that.eTag)
85+
&& Objects.equals(md5, that.md5)
86+
&& Objects.equals(crc32c, that.crc32c);
7287
}
7388

7489
@Override
7590
public int hashCode() {
76-
return Objects.hash(eTag, md5);
91+
return Objects.hash(eTag, md5, crc32c);
7792
}
7893

7994
@Override
8095
public String toString() {
81-
return MoreObjects.toStringHelper(this).add("etag", eTag).add("md5", md5).toString();
96+
return MoreObjects.toStringHelper(this)
97+
.add("etag", eTag)
98+
.add("md5", md5)
99+
.add("crc32c", crc32c)
100+
.toString();
82101
}
83102

84103
/**
@@ -101,6 +120,7 @@ public static Builder builder() {
101120
public static class Builder {
102121
private String etag;
103122
private String md5;
123+
private String crc32c;
104124

105125
private Builder() {}
106126

@@ -130,6 +150,19 @@ public Builder md5(String md5) {
130150
return this;
131151
}
132152

153+
/**
154+
* Sets the CRC32C checksum for the uploaded part.
155+
*
156+
* @param crc32c The CRC32C checksum.
157+
* @return This builder.
158+
* @since 2.61.0 This new api is in preview and is subject to breaking changes.
159+
*/
160+
@BetaApi
161+
public Builder crc32c(String crc32c) {
162+
this.crc32c = crc32c;
163+
return this;
164+
}
165+
133166
/**
134167
* Builds the {@code UploadPartResponse} object.
135168
*

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public void testParse() throws IOException {
4343

4444
assertThat(uploadPartResponse.eTag()).isEqualTo("\"test-etag\"");
4545
assertThat(uploadPartResponse.md5()).isEqualTo("rL0Y20zC+Fzt72VPzMSk2A==");
46+
assertThat(uploadPartResponse.crc32c()).isEqualTo("AAAAAA==");
4647
}
4748

4849
@Test
@@ -85,13 +86,34 @@ public void testExtractHashesFromHeader_emptyHeader() throws IOException {
8586
assertThat(hashes).isEmpty();
8687
}
8788

88-
private HttpResponse createHttpResponse(String etag, String googHash) throws IOException {
89+
@Test
90+
public void testExtractHashesFromHeader_multipleHeaders() throws IOException {
91+
HttpResponse response =
92+
createHttpResponse(null, "crc32c=AAAAAA==", "md5=rL0Y20zC+Fzt72VPzMSk2A==");
93+
Map<String, String> hashes = ChecksumResponseParser.extractHashesFromHeader(response);
94+
assertThat(hashes).containsEntry("crc32c", "AAAAAA==");
95+
assertThat(hashes).containsEntry("md5", "rL0Y20zC+Fzt72VPzMSk2A==");
96+
}
97+
98+
@Test
99+
public void testExtractHashesFromHeader_multipleHeadersAndCsv() throws IOException {
100+
HttpResponse response =
101+
createHttpResponse(null, "crc32c=AAAAAA==", "md5=rL0Y20zC+Fzt72VPzMSk2A==,extra=value");
102+
Map<String, String> hashes = ChecksumResponseParser.extractHashesFromHeader(response);
103+
assertThat(hashes).containsEntry("crc32c", "AAAAAA==");
104+
assertThat(hashes).containsEntry("md5", "rL0Y20zC+Fzt72VPzMSk2A==");
105+
assertThat(hashes).hasSize(2);
106+
}
107+
108+
private HttpResponse createHttpResponse(String etag, String... googHash) throws IOException {
89109
MockLowLevelHttpResponse lowLevelResponse = new MockLowLevelHttpResponse();
90110
if (etag != null) {
91111
lowLevelResponse.addHeader("ETag", etag);
92112
}
93113
if (googHash != null) {
94-
lowLevelResponse.addHeader("x-goog-hash", googHash);
114+
for (String hash : googHash) {
115+
lowLevelResponse.addHeader("x-goog-hash", hash);
116+
}
95117
}
96118
HttpTransport transport =
97119
new MockHttpTransport.Builder().setLowLevelHttpResponse(lowLevelResponse).build();

0 commit comments

Comments
 (0)