Add If-Match and If-None-Match support for PutObject operations

master
Nico Duldhardt 2025-10-07 20:13:50 +02:00 zatwierdzone przez Andrew Gaul
rodzic 5eec0f23fc
commit 07d420ef63
2 zmienionych plików z 124 dodań i 0 usunięć

Wyświetl plik

@ -1994,6 +1994,51 @@ public class S3ProxyHandler {
throw new S3Exception(S3ErrorCode.ENTITY_TOO_LARGE);
}
// Handle If-Match and If-None-Match headers for PUT operations.
// Unlike GET operations which use GetOptions to pass these conditions
// to jclouds, PUT operations lack a PutOptions equivalent in jclouds.
// Therefore, we manually fetch the blob metadata and validate ETags.
String ifMatch = request.getHeader(HttpHeaders.IF_MATCH);
String ifNoneMatch = request.getHeader(HttpHeaders.IF_NONE_MATCH);
if (ifMatch != null || ifNoneMatch != null) {
BlobMetadata metadata = blobStore.blobMetadata(containerName,
blobName);
if (ifMatch != null) {
if (metadata == null) {
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
}
String eTag = metadata.getETag();
if (eTag != null) {
eTag = maybeQuoteETag(eTag);
if (!equalsIgnoringSurroundingQuotes(ifMatch, eTag)) {
throw new S3Exception(S3ErrorCode.PRECONDITION_FAILED);
}
}
else {
throw new S3Exception(S3ErrorCode.PRECONDITION_FAILED);
}
}
if (ifNoneMatch != null) {
if (ifNoneMatch.equals("*")) {
if (metadata != null) {
throw new S3Exception(S3ErrorCode.PRECONDITION_FAILED);
}
}
else if (metadata != null) {
String eTag = metadata.getETag();
if (eTag != null) {
eTag = maybeQuoteETag(eTag);
if (equalsIgnoringSurroundingQuotes(ifNoneMatch, eTag)) {
throw new S3Exception(S3ErrorCode.PRECONDITION_FAILED);
}
}
}
}
}
BlobAccess access;
String cannedAcl = request.getHeader(AwsHttpHeaders.ACL);
if (cannedAcl == null || cannedAcl.equalsIgnoreCase("private")) {

Wyświetl plik

@ -1791,6 +1791,85 @@ public final class AwsSdkTest {
}
}
@Test
public void testPutIfMatch() throws Exception {
String blobName = "test-blob";
var metadata = new ObjectMetadata();
metadata.setContentLength(BYTE_SOURCE.size());
PutObjectResult result = client.putObject(containerName, blobName,
BYTE_SOURCE.openStream(), metadata);
String eTag = result.getETag();
// PUT with matching If-Match should succeed
metadata.setContentLength(BYTE_SOURCE.size());
var request = new PutObjectRequest(containerName, blobName,
BYTE_SOURCE.openStream(), metadata);
request.setIfMatch(eTag);
client.putObject(request);
// PUT with non-matching If-Match should fail
metadata.setContentLength(BYTE_SOURCE.size());
var badRequest = new PutObjectRequest(containerName, blobName,
BYTE_SOURCE.openStream(), metadata);
badRequest.setIfMatch("wrong-etag");
try {
client.putObject(badRequest);
Fail.failBecauseExceptionWasNotThrown(AmazonS3Exception.class);
} catch (AmazonS3Exception e) {
assertThat(e.getStatusCode()).isEqualTo(412);
}
// PUT with If-Match on non-existent object should fail with 404
metadata.setContentLength(BYTE_SOURCE.size());
var nonExistentRequest = new PutObjectRequest(containerName,
"non-existent", BYTE_SOURCE.openStream(), metadata);
nonExistentRequest.setIfMatch(eTag);
try {
client.putObject(nonExistentRequest);
Fail.failBecauseExceptionWasNotThrown(AmazonS3Exception.class);
} catch (AmazonS3Exception e) {
assertThat(e.getStatusCode()).isEqualTo(404);
}
}
@Test
public void testPutIfNoneMatch() throws Exception {
String blobName = "test-blob";
var metadata = new ObjectMetadata();
metadata.setContentLength(BYTE_SOURCE.size());
PutObjectResult result = client.putObject(containerName, blobName,
BYTE_SOURCE.openStream(), metadata);
String eTag = result.getETag();
// PUT with If-None-Match matching current ETag should fail
metadata.setContentLength(BYTE_SOURCE.size());
var request = new PutObjectRequest(containerName, blobName,
BYTE_SOURCE.openStream(), metadata);
request.setIfNoneMatch(eTag);
try {
client.putObject(request);
Fail.failBecauseExceptionWasNotThrown(AmazonS3Exception.class);
} catch (AmazonS3Exception e) {
assertThat(e.getStatusCode()).isEqualTo(412);
}
// PUT with If-None-Match with different ETag should succeed
metadata.setContentLength(BYTE_SOURCE.size());
var goodRequest = new PutObjectRequest(containerName, blobName,
BYTE_SOURCE.openStream(), metadata);
goodRequest.setIfNoneMatch("different-etag");
client.putObject(goodRequest);
// PUT with If-None-Match on non-existent object should succeed
metadata.setContentLength(BYTE_SOURCE.size());
var nonExistentRequest = new PutObjectRequest(containerName,
"non-existent", BYTE_SOURCE.openStream(), metadata);
nonExistentRequest.setIfNoneMatch("*");
client.putObject(nonExistentRequest);
}
@Test
public void testListRelativePath() throws Exception {
assumeTrue(!blobStoreType.equals("filesystem"));