kopia lustrzana https://github.com/gaul/s3proxy
Allow multiple account providers
This allows different backend blobstores to be configured at runtime, based on the request access key. As a result different frontend auth can also be used. Currently this can only be done programmatically and not through properties config file.pull/51/merge
rodzic
2dc2cd60eb
commit
87bed283cb
|
@ -21,6 +21,7 @@ import static java.util.Objects.requireNonNull;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
@ -39,6 +40,7 @@ import org.jclouds.blobstore.BlobStore;
|
||||||
*/
|
*/
|
||||||
public final class S3Proxy {
|
public final class S3Proxy {
|
||||||
private final Server server;
|
private final Server server;
|
||||||
|
private final S3ProxyHandler handler;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Prevent Jetty from rewriting headers:
|
// Prevent Jetty from rewriting headers:
|
||||||
|
@ -81,8 +83,9 @@ public final class S3Proxy {
|
||||||
connector.setHost(endpoint.getHost());
|
connector.setHost(endpoint.getHost());
|
||||||
connector.setPort(endpoint.getPort());
|
connector.setPort(endpoint.getPort());
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
server.setHandler(new S3ProxyHandler(blobStore, identity, credential,
|
handler = new S3ProxyHandler(blobStore, identity, credential,
|
||||||
virtualHost));
|
virtualHost);
|
||||||
|
server.setHandler(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
@ -150,4 +153,9 @@ public final class S3Proxy {
|
||||||
public String getState() {
|
public String getState() {
|
||||||
return server.getState();
|
return server.getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProviders(
|
||||||
|
Map<String, Map.Entry<String, BlobStore>> providers) {
|
||||||
|
handler.setProviders(providers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -60,6 +61,7 @@ import com.google.common.base.Strings;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.SortedSetMultimap;
|
import com.google.common.collect.SortedSetMultimap;
|
||||||
import com.google.common.collect.TreeMultimap;
|
import com.google.common.collect.TreeMultimap;
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
|
@ -149,10 +151,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
"log-delivery-write"
|
"log-delivery-write"
|
||||||
);
|
);
|
||||||
|
|
||||||
private final BlobStore blobStore;
|
private Map<String, Map.Entry<String, BlobStore>> providers =
|
||||||
private final String blobStoreType;
|
new HashMap<>();
|
||||||
private final String identity;
|
private final BlobStore defaultBlobStore;
|
||||||
private final String credential;
|
|
||||||
private final Optional<String> virtualHost;
|
private final Optional<String> virtualHost;
|
||||||
private final XMLInputFactory xmlInputFactory =
|
private final XMLInputFactory xmlInputFactory =
|
||||||
XMLInputFactory.newInstance();
|
XMLInputFactory.newInstance();
|
||||||
|
@ -161,16 +162,22 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
S3ProxyHandler(BlobStore blobStore, String identity, String credential,
|
S3ProxyHandler(BlobStore blobStore, String identity, String credential,
|
||||||
Optional<String> virtualHost) {
|
Optional<String> virtualHost) {
|
||||||
this.blobStore = requireNonNull(blobStore);
|
requireNonNull(blobStore);
|
||||||
this.blobStoreType =
|
if (identity != null) {
|
||||||
blobStore.getContext().unwrap().getProviderMetadata().getId();
|
providers.put(identity, Maps.immutableEntry(credential, blobStore));
|
||||||
this.identity = identity;
|
defaultBlobStore = null;
|
||||||
this.credential = credential;
|
} else {
|
||||||
|
defaultBlobStore = blobStore;
|
||||||
|
}
|
||||||
this.virtualHost = requireNonNull(virtualHost);
|
this.virtualHost = requireNonNull(virtualHost);
|
||||||
xmlOutputFactory.setProperty("javax.xml.stream.isRepairingNamespaces",
|
xmlOutputFactory.setProperty("javax.xml.stream.isRepairingNamespaces",
|
||||||
Boolean.FALSE);
|
Boolean.FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getBlobStoreType(BlobStore blobStore) {
|
||||||
|
return blobStore.getContext().unwrap().getProviderMetadata().getId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest,
|
public void handle(String target, Request baseRequest,
|
||||||
HttpServletRequest request, HttpServletResponse response)
|
HttpServletRequest request, HttpServletResponse response)
|
||||||
|
@ -220,7 +227,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity != null && !hasDateHeader && !hasXAmzDateHeader &&
|
if (defaultBlobStore == null && !hasDateHeader && !hasXAmzDateHeader &&
|
||||||
request.getParameter("Expires") == null) {
|
request.getParameter("Expires") == null) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.ACCESS_DENIED,
|
sendSimpleErrorResponse(response, S3ErrorCode.ACCESS_DENIED,
|
||||||
"AWS authentication requires a valid Date or" +
|
"AWS authentication requires a valid Date or" +
|
||||||
|
@ -247,44 +254,45 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity != null) {
|
BlobStore blobStore;
|
||||||
String expectedSignature = createAuthorizationSignature(request,
|
String requestIdentity = null;
|
||||||
uri, identity, credential);
|
String requestSignature = null;
|
||||||
String headerAuthorization = request.getHeader(
|
String headerAuthorization = request.getHeader(
|
||||||
HttpHeaders.AUTHORIZATION);
|
HttpHeaders.AUTHORIZATION);
|
||||||
String headerIdentity = null;
|
|
||||||
String headerSignature = null;
|
if (headerAuthorization != null) {
|
||||||
if (headerAuthorization != null &&
|
if (headerAuthorization.startsWith("AWS ")) {
|
||||||
headerAuthorization.startsWith("AWS ")) {
|
|
||||||
String[] values =
|
String[] values =
|
||||||
headerAuthorization.substring(4).split(":", 2);
|
headerAuthorization.substring(4).split(":", 2);
|
||||||
if (values.length != 2) {
|
if (values.length != 2) {
|
||||||
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
}
|
}
|
||||||
headerIdentity = values[0];
|
requestIdentity = values[0];
|
||||||
headerSignature = values[1];
|
requestSignature = values[1];
|
||||||
} else if (headerAuthorization != null &&
|
} else if (headerAuthorization.startsWith("AWS4-HMAC-SHA256 ")) {
|
||||||
headerAuthorization.startsWith("AWS4-HMAC-SHA256 ")) {
|
|
||||||
// Fail V4 signature requests to allow clients to retry with V2.
|
// Fail V4 signature requests to allow clients to retry with V2.
|
||||||
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
}
|
}
|
||||||
String parameterIdentity = request.getParameter("AWSAccessKeyId");
|
} else {
|
||||||
String parameterSignature = request.getParameter("Signature");
|
requestIdentity = request.getParameter("AWSAccessKeyId");
|
||||||
|
requestSignature = request.getParameter("Signature");
|
||||||
|
}
|
||||||
|
|
||||||
if (headerIdentity != null && headerSignature != null) {
|
if (requestIdentity != null) {
|
||||||
if (!identity.equals(headerIdentity)) {
|
Map.Entry<String, BlobStore> provider =
|
||||||
|
providers.get(requestIdentity);
|
||||||
|
if (provider == null) {
|
||||||
throw new S3Exception(S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
throw new S3Exception(S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
||||||
} else if (!expectedSignature.equals(headerSignature)) {
|
}
|
||||||
throw new S3Exception(S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
|
||||||
}
|
String expectedSignature = createAuthorizationSignature(request,
|
||||||
} else if (parameterIdentity != null &&
|
uri, requestIdentity, provider.getKey());
|
||||||
parameterSignature != null) {
|
if (!expectedSignature.equals(requestSignature)) {
|
||||||
if (!identity.equals(parameterIdentity)) {
|
|
||||||
throw new S3Exception(S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
|
||||||
} else if (!expectedSignature.equals(parameterSignature)) {
|
|
||||||
throw new S3Exception(S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
throw new S3Exception(S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blobStore = provider.getValue();
|
||||||
|
|
||||||
String expiresString = request.getParameter("Expires");
|
String expiresString = request.getParameter("Expires");
|
||||||
if (expiresString != null) {
|
if (expiresString != null) {
|
||||||
long expires = Long.parseLong(expiresString);
|
long expires = Long.parseLong(expiresString);
|
||||||
|
@ -294,7 +302,10 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (defaultBlobStore == null) {
|
||||||
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
||||||
|
} else {
|
||||||
|
blobStore = defaultBlobStore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,79 +327,82 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
if (path.length <= 2 || path[2].isEmpty()) {
|
if (path.length <= 2 || path[2].isEmpty()) {
|
||||||
handleContainerDelete(response, path[1]);
|
handleContainerDelete(response, blobStore, path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if (uploadId != null) {
|
} else if (uploadId != null) {
|
||||||
handleAbortMultipartUpload(request, response, path[1], path[2],
|
handleAbortMultipartUpload(request, response, blobStore,
|
||||||
uploadId);
|
path[1], path[2], uploadId);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
handleBlobRemove(response, path[1], path[2]);
|
handleBlobRemove(response, blobStore, path[1], path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "GET":
|
case "GET":
|
||||||
if (uri.equals("/")) {
|
if (uri.equals("/")) {
|
||||||
handleContainerList(response);
|
handleContainerList(response, blobStore);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if (path.length <= 2 || path[2].isEmpty()) {
|
} else if (path.length <= 2 || path[2].isEmpty()) {
|
||||||
if ("".equals(request.getParameter("acl"))) {
|
if ("".equals(request.getParameter("acl"))) {
|
||||||
handleGetContainerAcl(response, path[1]);
|
handleGetContainerAcl(response, blobStore, path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if ("".equals(request.getParameter("location"))) {
|
} else if ("".equals(request.getParameter("location"))) {
|
||||||
handleContainerLocation(response, path[1]);
|
handleContainerLocation(response, blobStore, path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if ("".equals(request.getParameter("uploads"))) {
|
} else if ("".equals(request.getParameter("uploads"))) {
|
||||||
handleListMultipartUploads(response, uploadId);
|
handleListMultipartUploads(response, blobStore,
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleBlobList(request, response, path[1]);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if ("".equals(request.getParameter("acl"))) {
|
|
||||||
handleGetBlobAcl(response, path[1], path[2]);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
} else if (uploadId != null) {
|
|
||||||
handleListParts(request, response, path[1], path[2],
|
|
||||||
uploadId);
|
uploadId);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleGetBlob(request, response, path[1], path[2]);
|
handleBlobList(request, response, blobStore, path[1]);
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if ("".equals(request.getParameter("acl"))) {
|
||||||
|
handleGetBlobAcl(response, path[1], blobStore,
|
||||||
|
path[2]);
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
return;
|
||||||
|
} else if (uploadId != null) {
|
||||||
|
handleListParts(request, response, blobStore, path[1],
|
||||||
|
path[2], uploadId);
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleGetBlob(request, response, blobStore, path[1],
|
||||||
|
path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "HEAD":
|
case "HEAD":
|
||||||
if (path.length <= 2 || path[2].isEmpty()) {
|
if (path.length <= 2 || path[2].isEmpty()) {
|
||||||
handleContainerExists(response, path[1]);
|
handleContainerExists(response, blobStore, path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
handleBlobMetadata(response, path[1], path[2]);
|
handleBlobMetadata(response, blobStore, path[1], path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "POST":
|
case "POST":
|
||||||
if ("".equals(request.getParameter("delete"))) {
|
if ("".equals(request.getParameter("delete"))) {
|
||||||
handleMultiBlobRemove(request, response, path[1]);
|
handleMultiBlobRemove(request, response, blobStore, path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if ("".equals(request.getParameter("uploads"))) {
|
} else if ("".equals(request.getParameter("uploads"))) {
|
||||||
handleInitiateMultipartUpload(request, response, path[1],
|
handleInitiateMultipartUpload(request, response, blobStore,
|
||||||
path[2]);
|
path[1], path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if (uploadId != null) {
|
} else if (uploadId != null) {
|
||||||
handleCompleteMultipartUpload(request, response, path[1],
|
handleCompleteMultipartUpload(request, response, blobStore,
|
||||||
path[2], uploadId);
|
path[1], path[2], uploadId);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -396,29 +410,31 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
case "PUT":
|
case "PUT":
|
||||||
if (path.length <= 2 || path[2].isEmpty()) {
|
if (path.length <= 2 || path[2].isEmpty()) {
|
||||||
if ("".equals(request.getParameter("acl"))) {
|
if ("".equals(request.getParameter("acl"))) {
|
||||||
handleSetContainerAcl(request, response, path[1]);
|
handleSetContainerAcl(request, response, blobStore,
|
||||||
|
path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleContainerCreate(request, response, path[1]);
|
handleContainerCreate(request, response, blobStore, path[1]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if (uploadId != null) {
|
} else if (uploadId != null) {
|
||||||
handleUploadPart(request, response, path[1], path[2],
|
handleUploadPart(request, response, blobStore, path[1],
|
||||||
uploadId);
|
path[2], uploadId);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else if (request.getHeader("x-amz-copy-source") != null) {
|
} else if (request.getHeader("x-amz-copy-source") != null) {
|
||||||
handleCopyBlob(request, response, path[1], path[2]);
|
handleCopyBlob(request, response, blobStore, path[1], path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if ("".equals(request.getParameter("acl"))) {
|
if ("".equals(request.getParameter("acl"))) {
|
||||||
handleSetBlobAcl(request, response, path[1], path[2]);
|
handleSetBlobAcl(request, response, blobStore, path[1],
|
||||||
|
path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handlePutBlob(request, response, path[1], path[2]);
|
handlePutBlob(request, response, blobStore, path[1], path[2]);
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -430,8 +446,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetContainerAcl(HttpServletResponse response,
|
private void handleGetContainerAcl(HttpServletResponse response,
|
||||||
String containerName) throws IOException {
|
BlobStore blobStore, String containerName) throws IOException {
|
||||||
ContainerAccess access;
|
ContainerAccess access;
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("filesystem") ||
|
if (blobStoreType.equals("filesystem") ||
|
||||||
blobStoreType.equals("transient")) {
|
blobStoreType.equals("transient")) {
|
||||||
access = ContainerAccess.PRIVATE;
|
access = ContainerAccess.PRIVATE;
|
||||||
|
@ -505,8 +522,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSetContainerAcl(HttpServletRequest request,
|
private void handleSetContainerAcl(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
throws IOException, S3Exception {
|
String containerName) throws IOException, S3Exception {
|
||||||
ContainerAccess access;
|
ContainerAccess access;
|
||||||
|
|
||||||
String cannedAcl = request.getHeader("x-amz-acl");
|
String cannedAcl = request.getHeader("x-amz-acl");
|
||||||
|
@ -521,6 +538,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (!(blobStoreType.equals("filesystem") ||
|
if (!(blobStoreType.equals("filesystem") ||
|
||||||
blobStoreType.equals("transient"))) {
|
blobStoreType.equals("transient"))) {
|
||||||
blobStore.setContainerAccess(containerName, access);
|
blobStore.setContainerAccess(containerName, access);
|
||||||
|
@ -528,8 +546,10 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetBlobAcl(HttpServletResponse response,
|
private void handleGetBlobAcl(HttpServletResponse response,
|
||||||
String containerName, String blobName) throws IOException {
|
String containerName, BlobStore blobStore,
|
||||||
|
String blobName) throws IOException {
|
||||||
BlobAccess access;
|
BlobAccess access;
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("filesystem") ||
|
if (blobStoreType.equals("filesystem") ||
|
||||||
blobStoreType.equals("transient")) {
|
blobStoreType.equals("transient")) {
|
||||||
access = BlobAccess.PRIVATE;
|
access = BlobAccess.PRIVATE;
|
||||||
|
@ -603,8 +623,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSetBlobAcl(HttpServletRequest request,
|
private void handleSetBlobAcl(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName) throws IOException, S3Exception {
|
String containerName, String blobName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
BlobAccess access;
|
BlobAccess access;
|
||||||
|
|
||||||
String cannedAcl = request.getHeader("x-amz-acl");
|
String cannedAcl = request.getHeader("x-amz-acl");
|
||||||
|
@ -619,14 +640,15 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (!(blobStoreType.equals("filesystem") ||
|
if (!(blobStoreType.equals("filesystem") ||
|
||||||
blobStoreType.equals("transient"))) {
|
blobStoreType.equals("transient"))) {
|
||||||
blobStore.setBlobAccess(containerName, blobName, access);
|
blobStore.setBlobAccess(containerName, blobName, access);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerList(HttpServletResponse response)
|
private void handleContainerList(HttpServletResponse response,
|
||||||
throws IOException {
|
BlobStore blobStore) throws IOException {
|
||||||
try (Writer writer = response.getWriter()) {
|
try (Writer writer = response.getWriter()) {
|
||||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||||
writer);
|
writer);
|
||||||
|
@ -667,7 +689,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerLocation(HttpServletResponse response,
|
private void handleContainerLocation(HttpServletResponse response,
|
||||||
String containerName) throws IOException {
|
BlobStore blobStore, String containerName) throws IOException {
|
||||||
try (Writer writer = response.getWriter()) {
|
try (Writer writer = response.getWriter()) {
|
||||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||||
writer);
|
writer);
|
||||||
|
@ -683,21 +705,23 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleListMultipartUploads(HttpServletResponse response,
|
private void handleListMultipartUploads(HttpServletResponse response,
|
||||||
String uploadId) throws IOException, S3Exception {
|
BlobStore blobStore, String uploadId)
|
||||||
|
throws IOException, S3Exception {
|
||||||
// TODO: list all blobs starting with uploadId
|
// TODO: list all blobs starting with uploadId
|
||||||
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerExists(HttpServletResponse response,
|
private void handleContainerExists(HttpServletResponse response,
|
||||||
String containerName) throws IOException, S3Exception {
|
BlobStore blobStore, String containerName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
if (!blobStore.containerExists(containerName)) {
|
if (!blobStore.containerExists(containerName)) {
|
||||||
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerCreate(HttpServletRequest request,
|
private void handleContainerCreate(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
throws IOException, S3Exception {
|
String containerName) throws IOException, S3Exception {
|
||||||
if (containerName.isEmpty()) {
|
if (containerName.isEmpty()) {
|
||||||
throw new S3Exception(S3ErrorCode.METHOD_NOT_ALLOWED);
|
throw new S3Exception(S3ErrorCode.METHOD_NOT_ALLOWED);
|
||||||
}
|
}
|
||||||
|
@ -771,7 +795,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerDelete(HttpServletResponse response,
|
private void handleContainerDelete(HttpServletResponse response,
|
||||||
String containerName) throws IOException, S3Exception {
|
BlobStore blobStore, String containerName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
if (!blobStore.containerExists(containerName)) {
|
if (!blobStore.containerExists(containerName)) {
|
||||||
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||||
}
|
}
|
||||||
|
@ -782,8 +807,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBlobList(HttpServletRequest request,
|
private void handleBlobList(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
throws IOException, S3Exception {
|
String containerName) throws IOException, S3Exception {
|
||||||
ListContainerOptions options = new ListContainerOptions();
|
ListContainerOptions options = new ListContainerOptions();
|
||||||
String delimiter = request.getParameter("delimiter");
|
String delimiter = request.getParameter("delimiter");
|
||||||
if (!(delimiter != null && delimiter.equals("/"))) {
|
if (!(delimiter != null && delimiter.equals("/"))) {
|
||||||
|
@ -903,6 +928,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
String eTag = metadata.getETag();
|
String eTag = metadata.getETag();
|
||||||
if (eTag != null) {
|
if (eTag != null) {
|
||||||
xml.writeStartElement("ETag");
|
xml.writeStartElement("ETag");
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("google-cloud-storage")) {
|
if (blobStoreType.equals("google-cloud-storage")) {
|
||||||
eTag = BaseEncoding.base16().lowerCase().encode(
|
eTag = BaseEncoding.base16().lowerCase().encode(
|
||||||
BaseEncoding.base64().decode(eTag));
|
BaseEncoding.base64().decode(eTag));
|
||||||
|
@ -942,15 +968,15 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBlobRemove(HttpServletResponse response,
|
private void handleBlobRemove(HttpServletResponse response,
|
||||||
String containerName, String blobName)
|
BlobStore blobStore, String containerName,
|
||||||
throws IOException, S3Exception {
|
String blobName) throws IOException, S3Exception {
|
||||||
blobStore.removeBlob(containerName, blobName);
|
blobStore.removeBlob(containerName, blobName);
|
||||||
response.sendError(HttpServletResponse.SC_NO_CONTENT);
|
response.sendError(HttpServletResponse.SC_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMultiBlobRemove(HttpServletRequest request,
|
private void handleMultiBlobRemove(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
throws IOException {
|
String containerName) throws IOException {
|
||||||
try (InputStream is = request.getInputStream();
|
try (InputStream is = request.getInputStream();
|
||||||
Writer writer = new OutputStreamWriter(response.getOutputStream(),
|
Writer writer = new OutputStreamWriter(response.getOutputStream(),
|
||||||
StandardCharsets.UTF_8)) {
|
StandardCharsets.UTF_8)) {
|
||||||
|
@ -978,8 +1004,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBlobMetadata(HttpServletResponse response,
|
private void handleBlobMetadata(HttpServletResponse response,
|
||||||
String containerName, String blobName)
|
BlobStore blobStore, String containerName,
|
||||||
throws IOException, S3Exception {
|
String blobName) throws IOException, S3Exception {
|
||||||
BlobMetadata metadata = blobStore.blobMetadata(containerName, blobName);
|
BlobMetadata metadata = blobStore.blobMetadata(containerName, blobName);
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
|
||||||
|
@ -990,8 +1016,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetBlob(HttpServletRequest request,
|
private void handleGetBlob(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName) throws IOException, S3Exception {
|
String containerName, String blobName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
int status = HttpServletResponse.SC_OK;
|
int status = HttpServletResponse.SC_OK;
|
||||||
GetOptions options = new GetOptions();
|
GetOptions options = new GetOptions();
|
||||||
String range = request.getHeader(HttpHeaders.RANGE);
|
String range = request.getHeader(HttpHeaders.RANGE);
|
||||||
|
@ -1026,8 +1053,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCopyBlob(HttpServletRequest request,
|
private void handleCopyBlob(HttpServletRequest request,
|
||||||
HttpServletResponse response, String destContainerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String destBlobName) throws IOException, S3Exception {
|
String destContainerName, String destBlobName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
String copySourceHeader = request.getHeader("x-amz-copy-source");
|
String copySourceHeader = request.getHeader("x-amz-copy-source");
|
||||||
if (copySourceHeader.startsWith("/")) {
|
if (copySourceHeader.startsWith("/")) {
|
||||||
// Some clients like boto do not include the leading slash
|
// Some clients like boto do not include the leading slash
|
||||||
|
@ -1071,6 +1099,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
PutOptions options = new PutOptions();
|
PutOptions options = new PutOptions();
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("azureblob") &&
|
if (blobStoreType.equals("azureblob") &&
|
||||||
contentLength > 64 * 1024 * 1024) {
|
contentLength > 64 * 1024 * 1024) {
|
||||||
options.multipart(true);
|
options.multipart(true);
|
||||||
|
@ -1103,8 +1132,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePutBlob(HttpServletRequest request,
|
private void handlePutBlob(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName) throws IOException, S3Exception {
|
String containerName, String blobName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
// Flag headers present since HttpServletResponse.getHeader returns
|
// Flag headers present since HttpServletResponse.getHeader returns
|
||||||
// null for empty headers values.
|
// null for empty headers values.
|
||||||
String contentLengthString = null;
|
String contentLengthString = null;
|
||||||
|
@ -1156,6 +1186,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
PutOptions options = new PutOptions();
|
PutOptions options = new PutOptions();
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("azureblob") &&
|
if (blobStoreType.equals("azureblob") &&
|
||||||
contentLength > 64 * 1024 * 1024) {
|
contentLength > 64 * 1024 * 1024) {
|
||||||
options.multipart(true);
|
options.multipart(true);
|
||||||
|
@ -1199,13 +1230,14 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
// TODO: jclouds should include this in PutOptions
|
// TODO: jclouds should include this in PutOptions
|
||||||
String cannedAcl = request.getHeader("x-amz-acl");
|
String cannedAcl = request.getHeader("x-amz-acl");
|
||||||
if (cannedAcl != null && !cannedAcl.equals("private")) {
|
if (cannedAcl != null && !cannedAcl.equals("private")) {
|
||||||
handleSetBlobAcl(request, response, containerName, blobName);
|
handleSetBlobAcl(request, response, blobStore, containerName,
|
||||||
|
blobName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInitiateMultipartUpload(HttpServletRequest request,
|
private void handleInitiateMultipartUpload(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName) throws IOException {
|
String containerName, String blobName) throws IOException {
|
||||||
String uploadId = FAKE_UPLOAD_ID + UUID.randomUUID().toString();
|
String uploadId = FAKE_UPLOAD_ID + UUID.randomUUID().toString();
|
||||||
ByteSource payload = ByteSource.empty();
|
ByteSource payload = ByteSource.empty();
|
||||||
BlobBuilder.PayloadBlobBuilder builder = blobStore
|
BlobBuilder.PayloadBlobBuilder builder = blobStore
|
||||||
|
@ -1242,8 +1274,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCompleteMultipartUpload(HttpServletRequest request,
|
private void handleCompleteMultipartUpload(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName, String uploadId) throws IOException, S3Exception {
|
String containerName, String blobName, String uploadId)
|
||||||
|
throws IOException, S3Exception {
|
||||||
Collection<String> partNames = new ArrayList<>();
|
Collection<String> partNames = new ArrayList<>();
|
||||||
long totalContentLength = 0;
|
long totalContentLength = 0;
|
||||||
try (InputStream is = request.getInputStream()) {
|
try (InputStream is = request.getInputStream()) {
|
||||||
|
@ -1317,6 +1350,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
if (eTag != null) {
|
if (eTag != null) {
|
||||||
xml.writeStartElement("ETag");
|
xml.writeStartElement("ETag");
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("google-cloud-storage")) {
|
if (blobStoreType.equals("google-cloud-storage")) {
|
||||||
eTag = BaseEncoding.base16().lowerCase().encode(
|
eTag = BaseEncoding.base16().lowerCase().encode(
|
||||||
BaseEncoding.base64().decode(eTag));
|
BaseEncoding.base64().decode(eTag));
|
||||||
|
@ -1333,8 +1367,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAbortMultipartUpload(HttpServletRequest request,
|
private void handleAbortMultipartUpload(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName, String uploadId) throws IOException, S3Exception {
|
String containerName, String blobName, String uploadId)
|
||||||
|
throws IOException, S3Exception {
|
||||||
if (!blobStore.blobExists(containerName, uploadId)) {
|
if (!blobStore.blobExists(containerName, uploadId)) {
|
||||||
throw new S3Exception(S3ErrorCode.NO_SUCH_UPLOAD);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_UPLOAD);
|
||||||
}
|
}
|
||||||
|
@ -1353,8 +1388,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleListParts(HttpServletRequest request,
|
private void handleListParts(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName, String uploadId) throws IOException {
|
String containerName, String blobName, String uploadId)
|
||||||
|
throws IOException {
|
||||||
try (Writer writer = response.getWriter()) {
|
try (Writer writer = response.getWriter()) {
|
||||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||||
writer);
|
writer);
|
||||||
|
@ -1441,6 +1477,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
String eTag = sm.getETag();
|
String eTag = sm.getETag();
|
||||||
if (eTag != null) {
|
if (eTag != null) {
|
||||||
xml.writeStartElement("ETag");
|
xml.writeStartElement("ETag");
|
||||||
|
String blobStoreType = getBlobStoreType(blobStore);
|
||||||
if (blobStoreType.equals("google-cloud-storage")) {
|
if (blobStoreType.equals("google-cloud-storage")) {
|
||||||
eTag = BaseEncoding.base16().lowerCase().encode(
|
eTag = BaseEncoding.base16().lowerCase().encode(
|
||||||
BaseEncoding.base64().decode(eTag));
|
BaseEncoding.base64().decode(eTag));
|
||||||
|
@ -1465,8 +1502,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleUploadPart(HttpServletRequest request,
|
private void handleUploadPart(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
String blobName, String uploadId)
|
String containerName, String blobName, String uploadId)
|
||||||
throws IOException, S3Exception {
|
throws IOException, S3Exception {
|
||||||
// TODO: duplicated from handlePutBlob
|
// TODO: duplicated from handlePutBlob
|
||||||
String contentLengthString = null;
|
String contentLengthString = null;
|
||||||
|
@ -1610,6 +1647,11 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProviders(
|
||||||
|
Map<String, Map.Entry<String, BlobStore>> providers) {
|
||||||
|
this.providers = ImmutableMap.copyOf(providers);
|
||||||
|
}
|
||||||
|
|
||||||
static class S3Exception extends Exception {
|
static class S3Exception extends Exception {
|
||||||
private final S3ErrorCode error;
|
private final S3ErrorCode error;
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue