Skip to content

Commit 675410e

Browse files
authored
HDDS-13920. Conditional Reads (GetObject, HeadObject) (#10031)
1 parent 4501218 commit 675410e

10 files changed

Lines changed: 603 additions & 137 deletions

File tree

hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,118 @@ public void testPutObjectIfMatchMissingKeyFail() {
338338
b -> b.bucket(bucketName).key(keyName)));
339339
}
340340

341+
@Test
342+
public void testGetObjectIfMatch() {
343+
final String bucketName = getBucketName();
344+
final String keyName = getKeyName();
345+
final String content = "bar";
346+
s3Client.createBucket(b -> b.bucket(bucketName));
347+
348+
PutObjectResponse initialResponse = s3Client.putObject(
349+
b -> b.bucket(bucketName).key(keyName),
350+
RequestBody.fromString(content));
351+
352+
ResponseBytes<GetObjectResponse> response = s3Client.getObjectAsBytes(
353+
b -> b.bucket(bucketName).key(keyName).ifMatch(initialResponse.eTag()));
354+
355+
assertEquals(content, response.asUtf8String());
356+
}
357+
358+
@Test
359+
public void testGetObjectIfMatchFail() {
360+
final String bucketName = getBucketName();
361+
final String keyName = getKeyName();
362+
final String content = "bar";
363+
s3Client.createBucket(b -> b.bucket(bucketName));
364+
s3Client.putObject(b -> b.bucket(bucketName).key(keyName),
365+
RequestBody.fromString(content));
366+
367+
S3Exception exception = assertThrows(S3Exception.class,
368+
() -> s3Client.getObjectAsBytes(
369+
b -> b.bucket(bucketName).key(keyName).ifMatch("wrong-etag")));
370+
371+
assertEquals(412, exception.statusCode());
372+
assertEquals("PreconditionFailed", exception.awsErrorDetails().errorCode());
373+
}
374+
375+
@Test
376+
public void testGetObjectIfNoneMatchReturnsNotModified() {
377+
final String bucketName = getBucketName();
378+
final String keyName = getKeyName();
379+
final String content = "bar";
380+
s3Client.createBucket(b -> b.bucket(bucketName));
381+
382+
PutObjectResponse initialResponse = s3Client.putObject(
383+
b -> b.bucket(bucketName).key(keyName),
384+
RequestBody.fromString(content));
385+
386+
S3Exception exception = assertThrows(S3Exception.class,
387+
() -> s3Client.getObjectAsBytes(
388+
b -> b.bucket(bucketName).key(keyName)
389+
.ifNoneMatch(initialResponse.eTag())));
390+
391+
assertEquals(304, exception.statusCode());
392+
}
393+
394+
@Test
395+
public void testGetObjectIfModifiedSinceReturnsNotModified() {
396+
final String bucketName = getBucketName();
397+
final String keyName = getKeyName();
398+
final String content = "bar";
399+
s3Client.createBucket(b -> b.bucket(bucketName));
400+
s3Client.putObject(b -> b.bucket(bucketName).key(keyName),
401+
RequestBody.fromString(content));
402+
403+
HeadObjectResponse headObjectResponse = s3Client.headObject(
404+
b -> b.bucket(bucketName).key(keyName));
405+
406+
S3Exception exception = assertThrows(S3Exception.class,
407+
() -> s3Client.getObjectAsBytes(
408+
b -> b.bucket(bucketName).key(keyName)
409+
.ifModifiedSince(headObjectResponse.lastModified()
410+
.plusSeconds(60))));
411+
412+
assertEquals(304, exception.statusCode());
413+
}
414+
415+
@Test
416+
public void testHeadObjectIfMatch() {
417+
final String bucketName = getBucketName();
418+
final String keyName = getKeyName();
419+
final String content = "bar";
420+
s3Client.createBucket(b -> b.bucket(bucketName));
421+
422+
PutObjectResponse initialResponse = s3Client.putObject(
423+
b -> b.bucket(bucketName).key(keyName),
424+
RequestBody.fromString(content));
425+
426+
HeadObjectResponse response = s3Client.headObject(
427+
b -> b.bucket(bucketName).key(keyName).ifMatch(initialResponse.eTag()));
428+
429+
assertEquals(Long.valueOf(content.length()), response.contentLength());
430+
}
431+
432+
@Test
433+
public void testHeadObjectIfUnmodifiedSinceFail() {
434+
final String bucketName = getBucketName();
435+
final String keyName = getKeyName();
436+
final String content = "bar";
437+
s3Client.createBucket(b -> b.bucket(bucketName));
438+
s3Client.putObject(b -> b.bucket(bucketName).key(keyName),
439+
RequestBody.fromString(content));
440+
441+
HeadObjectResponse headObjectResponse = s3Client.headObject(
442+
b -> b.bucket(bucketName).key(keyName));
443+
444+
S3Exception exception = assertThrows(S3Exception.class,
445+
() -> s3Client.headObject(
446+
b -> b.bucket(bucketName).key(keyName)
447+
.ifUnmodifiedSince(headObjectResponse.lastModified()
448+
.minusSeconds(60))));
449+
450+
assertEquals(412, exception.statusCode());
451+
}
452+
341453
@Test
342454
public void testPutObjectEmpty() throws Exception {
343455
final String bucketName = getBucketName();

hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import java.io.UnsupportedEncodingException;
5353
import java.security.MessageDigest;
5454
import java.security.NoSuchAlgorithmException;
55-
import java.text.ParseException;
5655
import java.util.ArrayList;
5756
import java.util.Arrays;
5857
import java.util.Collections;
@@ -61,7 +60,6 @@
6160
import java.util.Iterator;
6261
import java.util.List;
6362
import java.util.Map;
64-
import java.util.OptionalLong;
6563
import java.util.Set;
6664
import java.util.function.Consumer;
6765
import java.util.function.Function;
@@ -107,7 +105,6 @@
107105
import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
108106
import org.apache.hadoop.ozone.s3.util.AuditUtils;
109107
import org.apache.hadoop.ozone.s3.util.S3Utils;
110-
import org.apache.hadoop.ozone.web.utils.OzoneUtils;
111108
import org.apache.http.NameValuePair;
112109
import org.apache.http.client.utils.URLEncodedUtils;
113110
import org.slf4j.Logger;
@@ -471,7 +468,7 @@ protected AuditMessage.Builder auditMessageFor(AuditAction op) {
471468
Map<String, String> auditMap = getAuditParameters();
472469
auditMap.put("x-amz-request-id", requestIdentifier.getRequestId());
473470
auditMap.put("x-amz-id-2", requestIdentifier.getAmzId());
474-
471+
475472
AuditMessage.Builder builder = new AuditMessage.Builder()
476473
.forOperation(op)
477474
.withParams(auditMap);
@@ -649,52 +646,6 @@ protected static int parsePartNumberMarker(String partNumberMarker) {
649646
return partMarker;
650647
}
651648

652-
// Parses date string and return long representation. Returns an
653-
// empty if DateStr is null or invalid. Dates in the future are
654-
// considered invalid.
655-
private static OptionalLong parseAndValidateDate(String ozoneDateStr) {
656-
long ozoneDateInMs;
657-
if (ozoneDateStr == null) {
658-
return OptionalLong.empty();
659-
}
660-
try {
661-
ozoneDateInMs = OzoneUtils.formatDate(ozoneDateStr);
662-
} catch (ParseException e) {
663-
// if time not parseable, then return empty()
664-
return OptionalLong.empty();
665-
}
666-
667-
long currentDate = System.currentTimeMillis();
668-
if (ozoneDateInMs <= currentDate) {
669-
return OptionalLong.of(ozoneDateInMs);
670-
} else {
671-
// dates in the future are invalid, so return empty()
672-
return OptionalLong.empty();
673-
}
674-
}
675-
676-
public static boolean checkCopySourceModificationTime(
677-
Long lastModificationTime,
678-
String copySourceIfModifiedSinceStr,
679-
String copySourceIfUnmodifiedSinceStr) {
680-
long copySourceIfModifiedSince = Long.MIN_VALUE;
681-
long copySourceIfUnmodifiedSince = Long.MAX_VALUE;
682-
683-
OptionalLong modifiedDate =
684-
parseAndValidateDate(copySourceIfModifiedSinceStr);
685-
if (modifiedDate.isPresent()) {
686-
copySourceIfModifiedSince = modifiedDate.getAsLong();
687-
}
688-
689-
OptionalLong unmodifiedDate =
690-
parseAndValidateDate(copySourceIfUnmodifiedSinceStr);
691-
if (unmodifiedDate.isPresent()) {
692-
copySourceIfUnmodifiedSince = unmodifiedDate.getAsLong();
693-
}
694-
return (copySourceIfModifiedSince <= lastModificationTime) &&
695-
(lastModificationTime <= copySourceIfUnmodifiedSince);
696-
}
697-
698649
/**
699650
* Create a {@link S3ChunkInputStreamInfo} that contains the necessary information to handle
700651
* the S3 chunk upload.

0 commit comments

Comments
 (0)