Skip to content

Commit 732c87c

Browse files
committed
Fix handling of empty S3 objects in S3ChecksumValidatingInputStream
1 parent 2417a30 commit 732c87c

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/checksums/S3ChecksumValidatingInputStream.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ public S3ChecksumValidatingInputStream(InputStream in, SdkChecksum cksum, long s
5858
*/
5959
@Override
6060
public int read() throws IOException {
61+
if (strippedLength == 0 && lengthRead == 0) {
62+
for (int i = 0; i < CHECKSUM_SIZE; i++) {
63+
int b = inputStream.read();
64+
if (b != -1) {
65+
streamChecksum[i] = (byte) b;
66+
}
67+
}
68+
lengthRead = CHECKSUM_SIZE;
69+
validateAndThrow();
70+
return -1;
71+
}
72+
6173
int read = inputStream.read();
6274

6375
if (read != -1 && lengthRead < strippedLength) {

services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/S3ChecksumValidatingInputStreamTest.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package software.amazon.awssdk.services.s3.checksums;
1717

1818
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
1920
import static org.junit.jupiter.api.Assertions.fail;
2021

2122
import java.io.ByteArrayInputStream;
@@ -56,20 +57,51 @@ public static void populateData() {
5657

5758
@Test
5859
public void validChecksumSucceeds() throws IOException {
59-
InputStream validatingInputStream = newValidatingStream(testData);
60+
InputStream validatingInputStream = newValidatingStream(testData, TEST_DATA_SIZE, CHECKSUM_SIZE);
6061
byte[] dataFromValidatingStream = IoUtils.toByteArray(validatingInputStream);
6162

6263
assertArrayEquals(testDataWithoutChecksum, dataFromValidatingStream);
6364
}
6465

66+
@Test
67+
public void emptyObjectSingleByteReadReturnsEOF() throws IOException {
68+
Md5Checksum checksum = new Md5Checksum();
69+
byte[] checksumBytes = checksum.getChecksumBytes();
70+
byte[] emptyWithChecksum = new byte[CHECKSUM_SIZE];
71+
72+
for (int i = 0; i < CHECKSUM_SIZE; i++) {
73+
emptyWithChecksum[i] = checksumBytes[i];
74+
}
75+
76+
InputStream validatingInputStream = newValidatingStream(emptyWithChecksum, 0, CHECKSUM_SIZE);
77+
78+
assertEquals(-1, validatingInputStream.read());
79+
}
80+
81+
@Test
82+
public void emptyObjectByteArrayReadReturnsEOF() throws IOException {
83+
Md5Checksum checksum = new Md5Checksum();
84+
byte[] checksumBytes = checksum.getChecksumBytes();
85+
byte[] emptyWithChecksum = new byte[CHECKSUM_SIZE];
86+
87+
for (int i = 0; i < CHECKSUM_SIZE; i++) {
88+
emptyWithChecksum[i] = checksumBytes[i];
89+
}
90+
91+
InputStream validatingInputStream = newValidatingStream(emptyWithChecksum, 0, CHECKSUM_SIZE);
92+
byte[] buffer = new byte[1];
93+
94+
assertEquals(-1, validatingInputStream.read(buffer));
95+
}
96+
6597
@Test
6698
public void invalidChecksumFails() throws IOException {
6799
for (int i = 0; i < testData.length; i++) {
68100
// Make sure that corruption of any byte in the test data causes a checksum validation failure.
69101
byte[] corruptedChecksumData = Arrays.copyOf(testData, testData.length);
70102
corruptedChecksumData[i] = (byte) ~corruptedChecksumData[i];
71103

72-
InputStream validatingInputStream = newValidatingStream(corruptedChecksumData);
104+
InputStream validatingInputStream = newValidatingStream(corruptedChecksumData, TEST_DATA_SIZE, CHECKSUM_SIZE);
73105

74106
try {
75107
IoUtils.toByteArray(validatingInputStream);
@@ -80,9 +112,9 @@ public void invalidChecksumFails() throws IOException {
80112
}
81113
}
82114

83-
private InputStream newValidatingStream(byte[] dataFromS3) {
115+
private InputStream newValidatingStream(byte[] dataFromS3, int testDataSize, int checkSumSize) {
84116
return new S3ChecksumValidatingInputStream(new ByteArrayInputStream(dataFromS3),
85117
new Md5Checksum(),
86-
TEST_DATA_SIZE + CHECKSUM_SIZE);
118+
testDataSize + checkSumSize);
87119
}
88120
}

services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/S3ChecksumValidatingPublisherTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,28 @@ public void checksumValidationFailure_throwsSdkClientException_NotNPE() {
169169
assertFalse(s.hasCompleted());
170170
}
171171

172+
@Test
173+
public void emptyObjectReturnsNoData() {
174+
Md5Checksum checksum = new Md5Checksum();
175+
byte[] checksumBytes = checksum.getChecksumBytes();
176+
byte[] emptyWithChecksum = new byte[CHECKSUM_SIZE];
177+
for (int i = 0; i < CHECKSUM_SIZE; i++) {
178+
emptyWithChecksum[i] = checksumBytes[i];
179+
}
180+
181+
final TestPublisher driver = new TestPublisher();
182+
final TestSubscriber s = new TestSubscriber();
183+
final S3ChecksumValidatingPublisher p = new S3ChecksumValidatingPublisher(driver, new Md5Checksum(), CHECKSUM_SIZE);
184+
p.subscribe(s);
185+
186+
driver.doOnNext(ByteBuffer.wrap(emptyWithChecksum));
187+
driver.doOnComplete();
188+
189+
assertArrayEquals(new byte[0], s.receivedData());
190+
assertTrue(s.hasCompleted());
191+
assertFalse(s.isOnErrorCalled());
192+
}
193+
172194
private class TestSubscriber implements Subscriber<ByteBuffer> {
173195
final List<ByteBuffer> received;
174196
boolean completed;

0 commit comments

Comments
 (0)