Add support for virtual host buckets

Fixes #8.
pull/16/head
Andrew Gaul 2014-08-26 18:59:40 -07:00
rodzic 34db5cae9a
commit 452d8d366c
4 zmienionych plików z 38 dodań i 9 usunięć

Wyświetl plik

@ -22,6 +22,7 @@ import java.io.InputStream;
import java.net.URI;
import java.util.Properties;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@ -51,7 +52,7 @@ public final class S3Proxy {
S3Proxy(BlobStore blobStore, URI endpoint, String identity,
String credential, String keyStorePath, String keyStorePassword,
boolean forceMultiPartUpload) {
boolean forceMultiPartUpload, Optional<String> virtualHost) {
Preconditions.checkNotNull(blobStore);
Preconditions.checkNotNull(endpoint);
// TODO: allow service paths?
@ -75,7 +76,7 @@ public final class S3Proxy {
connector.setPort(endpoint.getPort());
server.addConnector(connector);
server.setHandler(new S3ProxyHandler(blobStore, identity, credential,
forceMultiPartUpload));
forceMultiPartUpload, virtualHost));
}
public void start() throws Exception {
@ -158,6 +159,9 @@ public final class S3Proxy {
String forceMultiPartUpload = properties.getProperty(
S3ProxyConstants.PROPERTY_FORCE_MULTI_PART_UPLOAD);
Optional<String> virtualHost = Optional.fromNullable(
properties.getProperty(
S3ProxyConstants.PROPERTY_VIRTUAL_HOST));
ContextBuilder builder = ContextBuilder
.newBuilder(provider)
@ -171,7 +175,7 @@ public final class S3Proxy {
S3Proxy s3Proxy = new S3Proxy(context.getBlobStore(), s3ProxyEndpoint,
localIdentity, localCredential, keyStorePath,
keyStorePassword,
"true".equalsIgnoreCase(forceMultiPartUpload));
"true".equalsIgnoreCase(forceMultiPartUpload), virtualHost);
s3Proxy.start();
}
}

Wyświetl plik

@ -32,6 +32,14 @@ public final class S3ProxyConstants {
/** Force all proxy-server put objects to use multi-part upload. */
public static final String PROPERTY_FORCE_MULTI_PART_UPLOAD =
"s3proxy.force-multi-part-upload";
/**
* Configure servicing of virtual host buckets. Setting to localhost:8080
* allows bucket-in-hostname requests, e.g., bucketname.localhost:8080.
* This mode requires configuring DNS to forward all requests to the
* S3Proxy host.
*/
public static final String PROPERTY_VIRTUAL_HOST =
"s3proxy.virtual-host";
private S3ProxyConstants() {
throw new AssertionError("Cannot instantiate utility constructor");

Wyświetl plik

@ -99,13 +99,15 @@ final class S3ProxyHandler extends AbstractHandler {
private final String identity;
private final String credential;
private final boolean forceMultiPartUpload;
private final Optional<String> virtualHost;
S3ProxyHandler(BlobStore blobStore, String identity, String credential,
boolean forceMultiPartUpload) {
boolean forceMultiPartUpload, Optional<String> virtualHost) {
this.blobStore = Preconditions.checkNotNull(blobStore);
this.identity = identity;
this.credential = credential;
this.forceMultiPartUpload = forceMultiPartUpload;
this.virtualHost = Preconditions.checkNotNull(virtualHost);
}
@Override
@ -114,8 +116,17 @@ final class S3ProxyHandler extends AbstractHandler {
throws IOException {
String method = request.getMethod();
String uri = request.getRequestURI();
String[] path = uri.split("/", 3);
logger.debug("request: {}", request);
String hostHeader = request.getHeader(HttpHeaders.HOST);
if (hostHeader != null && virtualHost.isPresent()) {
String virtualHostSuffix = "." + virtualHost.get();
if (hostHeader.endsWith(virtualHostSuffix)) {
String bucket = hostHeader.substring(0,
hostHeader.length() - virtualHostSuffix.length());
uri = "/" + bucket + uri;
}
}
boolean hasDateHeader = false;
boolean hasXAmzDateHeader = false;
for (String headerName : Collections.list(request.getHeaderNames())) {
@ -167,7 +178,7 @@ final class S3ProxyHandler extends AbstractHandler {
if (identity != null) {
String expectedSignature = createAuthorizationSignature(request,
identity, credential);
uri, identity, credential);
String headerAuthorization = request.getHeader(
HttpHeaders.AUTHORIZATION);
String headerIdentity = null;
@ -224,6 +235,7 @@ final class S3ProxyHandler extends AbstractHandler {
}
}
String[] path = uri.split("/", 3);
switch (method) {
case "DELETE":
if (path.length <= 2 || path[2].isEmpty()) {
@ -927,7 +939,8 @@ final class S3ProxyHandler extends AbstractHandler {
* http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
*/
private static String createAuthorizationSignature(
HttpServletRequest request, String identity, String credential) {
HttpServletRequest request, String uri, String identity,
String credential) {
// sort Amazon headers
SortedSetMultimap<String, String> canonicalizedHeaders =
TreeMultimap.create();
@ -971,7 +984,7 @@ final class S3ProxyHandler extends AbstractHandler {
builder.append(entry.getKey()).append(':')
.append(entry.getValue()).append('\n');
}
builder.append(request.getRequestURI());
builder.append(uri);
if ("".equals(request.getParameter("acl"))) {
builder.append("?acl");
} else if ("".equals(request.getParameter("delete"))) {

Wyświetl plik

@ -25,6 +25,7 @@ import java.util.Random;
import javax.servlet.http.HttpServletResponse;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@ -88,6 +89,9 @@ public final class S3ProxyTest {
S3ProxyConstants.PROPERTY_KEYSTORE_PASSWORD);
String forceMultiPartUpload = s3ProxyProperties.getProperty(
S3ProxyConstants.PROPERTY_FORCE_MULTI_PART_UPLOAD);
Optional<String> virtualHost = Optional.fromNullable(
s3ProxyProperties.getProperty(
S3ProxyConstants.PROPERTY_VIRTUAL_HOST));
Properties properties = new Properties();
ContextBuilder builder = ContextBuilder
@ -116,7 +120,7 @@ public final class S3ProxyTest {
s3Proxy = new S3Proxy(blobStore, s3Endpoint, s3Identity, s3Credential,
Resources.getResource(keyStorePath).toString(),
keyStorePassword,
"true".equalsIgnoreCase(forceMultiPartUpload));
"true".equalsIgnoreCase(forceMultiPartUpload), virtualHost);
s3Proxy.start();
}