Add partial support for list objects v2

Not supporting fetch-owner until jclouds adds support for this.
Several applications like AWS CLI now require this RPC.  Fixes #168.
pull/293/head
Andrew Gaul 2019-02-09 15:06:34 -08:00
rodzic 4698a2a913
commit 3fed3941f1
3 zmienionych plików z 97 dodań i 9 usunięć

Wyświetl plik

@ -105,7 +105,6 @@ S3Proxy has broad compatibility with the S3 API, however, it does not support:
* bucket logging * bucket logging
* [CORS bucket operations](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors) like getting or setting the CORS configuration for a bucket. S3Proxy only supports a static configuration (see below). * [CORS bucket operations](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors) like getting or setting the CORS configuration for a bucket. S3Proxy only supports a static configuration (see below).
* hosting static websites * hosting static websites
* list objects v2, see [#168](https://github.com/gaul/s3proxy/issues/168)
* object server-side encryption * object server-side encryption
* object tagging * object tagging
* object versioning, see [#74](https://github.com/gaul/s3proxy/issues/74) * object versioning, see [#74](https://github.com/gaul/s3proxy/issues/74)

Wyświetl plik

@ -143,7 +143,6 @@ public class S3ProxyHandler {
"cors", "cors",
"inventory", "inventory",
"lifecycle", "lifecycle",
"list-type",
"logging", "logging",
"metrics", "metrics",
"notification", "notification",
@ -1316,7 +1315,27 @@ public class S3ProxyHandler {
if (prefix != null && !prefix.isEmpty()) { if (prefix != null && !prefix.isEmpty()) {
options.prefix(prefix); options.prefix(prefix);
} }
String marker = request.getParameter("marker");
boolean isListV2 = false;
String marker;
String listType = request.getParameter("list-type");
String continuationToken = request.getParameter("continuation-token");
String startAfter = request.getParameter("start-after");
if (listType == null) {
marker = request.getParameter("marker");
} else if (listType.equals("2")) {
isListV2 = true;
if (continuationToken != null && startAfter != null) {
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
}
if (continuationToken != null) {
marker = continuationToken;
} else {
marker = startAfter;
}
} else {
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
}
if (marker != null) { if (marker != null) {
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) { if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
String realMarker = lastKeyToMarker.getIfPresent( String realMarker = lastKeyToMarker.getIfPresent(
@ -1327,6 +1346,12 @@ public class S3ProxyHandler {
} }
options.afterMarker(marker); options.afterMarker(marker);
} }
String fetchOwner = request.getParameter("fetch-owner");
if (fetchOwner != null && !fetchOwner.equals("false")) {
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
}
int maxKeys = 1000; int maxKeys = 1000;
String maxKeysString = request.getParameter("max-keys"); String maxKeysString = request.getParameter("max-keys");
if (maxKeysString != null) { if (maxKeysString != null) {
@ -1372,12 +1397,27 @@ public class S3ProxyHandler {
writeSimpleElement(xml, "MaxKeys", String.valueOf(maxKeys)); writeSimpleElement(xml, "MaxKeys", String.valueOf(maxKeys));
if (!isListV2) {
if (marker == null) { if (marker == null) {
xml.writeEmptyElement("Marker"); xml.writeEmptyElement("Marker");
} else { } else {
writeSimpleElement(xml, "Marker", encodeBlob( writeSimpleElement(xml, "Marker", encodeBlob(
encodingType, marker)); encodingType, marker));
} }
} else {
if (continuationToken == null) {
xml.writeEmptyElement("ContinuationToken");
} else {
writeSimpleElement(xml, "ContinuationToken", encodeBlob(
encodingType, continuationToken));
}
if (startAfter == null) {
xml.writeEmptyElement("StartAfter");
} else {
writeSimpleElement(xml, "StartAfter", encodeBlob(
encodingType, startAfter));
}
}
if (delimiter != null) { if (delimiter != null) {
writeSimpleElement(xml, "Delimiter", encodeBlob( writeSimpleElement(xml, "Delimiter", encodeBlob(
@ -1391,8 +1431,9 @@ public class S3ProxyHandler {
String nextMarker = set.getNextMarker(); String nextMarker = set.getNextMarker();
if (nextMarker != null) { if (nextMarker != null) {
writeSimpleElement(xml, "IsTruncated", "true"); writeSimpleElement(xml, "IsTruncated", "true");
writeSimpleElement(xml, "NextMarker", encodeBlob( writeSimpleElement(xml,
encodingType, nextMarker)); isListV2 ? "NextContinuationToken" : "NextMarker",
encodeBlob(encodingType, nextMarker));
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) { if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
lastKeyToMarker.put(Maps.immutableEntry(containerName, lastKeyToMarker.put(Maps.immutableEntry(containerName,
Iterables.getLast(set).getName()), nextMarker); Iterables.getLast(set).getName()), nextMarker);

Wyświetl plik

@ -70,6 +70,8 @@ import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
import com.amazonaws.services.s3.model.ListMultipartUploadsRequest; import com.amazonaws.services.s3.model.ListMultipartUploadsRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest; import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ListObjectsV2Request;
import com.amazonaws.services.s3.model.ListObjectsV2Result;
import com.amazonaws.services.s3.model.ListPartsRequest; import com.amazonaws.services.s3.model.ListPartsRequest;
import com.amazonaws.services.s3.model.MultipartUploadListing; import com.amazonaws.services.s3.model.MultipartUploadListing;
import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.s3.model.ObjectListing;
@ -904,6 +906,52 @@ public final class AwsSdkTest {
.isEqualTo("blob2"); .isEqualTo("blob2");
} }
@Test
public void testBlobListV2() throws Exception {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(BYTE_SOURCE.size());
for (int i = 1; i < 5; ++i) {
client.putObject(containerName, String.valueOf(i),
BYTE_SOURCE.openStream(), metadata);
}
ListObjectsV2Result result = client.listObjectsV2(
new ListObjectsV2Request()
.withBucketName(containerName)
.withMaxKeys(1)
.withStartAfter("1"));
assertThat(result.getContinuationToken()).isEmpty();
assertThat(result.getStartAfter()).isEqualTo("1");
assertThat(result.getNextContinuationToken()).isEqualTo("2");
assertThat(result.isTruncated()).isTrue();
assertThat(result.getObjectSummaries()).hasSize(1);
assertThat(result.getObjectSummaries().get(0).getKey()).isEqualTo("2");
result = client.listObjectsV2(
new ListObjectsV2Request()
.withBucketName(containerName)
.withMaxKeys(1)
.withContinuationToken(result.getNextContinuationToken()));
assertThat(result.getContinuationToken()).isEqualTo("2");
assertThat(result.getStartAfter()).isEmpty();
assertThat(result.getNextContinuationToken()).isEqualTo("3");
assertThat(result.isTruncated()).isTrue();
assertThat(result.getObjectSummaries()).hasSize(1);
assertThat(result.getObjectSummaries().get(0).getKey()).isEqualTo("3");
result = client.listObjectsV2(
new ListObjectsV2Request()
.withBucketName(containerName)
.withMaxKeys(1)
.withContinuationToken(result.getNextContinuationToken()));
assertThat(result.getContinuationToken()).isEqualTo("3");
assertThat(result.getStartAfter()).isEmpty();
assertThat(result.getNextContinuationToken()).isNull();
assertThat(result.isTruncated()).isFalse();
assertThat(result.getObjectSummaries()).hasSize(1);
assertThat(result.getObjectSummaries().get(0).getKey()).isEqualTo("4");
}
@Test @Test
public void testBlobMetadata() throws Exception { public void testBlobMetadata() throws Exception {
String blobName = "blob"; String blobName = "blob";