kopia lustrzana https://github.com/gaul/s3proxy
Porównaj commity
65 Commity
s3proxy-2.
...
master
Autor | SHA1 | Data |
---|---|---|
dependabot[bot] | 4175022ff8 | |
dependabot[bot] | 8c5fb41c6c | |
dependabot[bot] | 21d42f3768 | |
dependabot[bot] | 6a0182df7f | |
Nagy Vilmos | 6c2fa6fbcf | |
dependabot[bot] | f5889e4cd9 | |
dependabot[bot] | 40c7b43731 | |
dependabot[bot] | f2ba17586c | |
Timur Alperovich | 468cffaa67 | |
dependabot[bot] | abc6d0295d | |
dependabot[bot] | 45843ab330 | |
dependabot[bot] | d1ba64077c | |
sullis | 67f913d6f6 | |
sullis | 823dcc4e1c | |
Andrew Gaul | 6943bf8f21 | |
Andrew Gaul | c667eced02 | |
Andrew Gaul | a78c0df4f7 | |
Andrew Gaul | 00bb83b74f | |
Andrew Gaul | ec12ae0fe5 | |
dependabot[bot] | b147909ff3 | |
dependabot[bot] | ae7a20f845 | |
dependabot[bot] | 2bdd10ca88 | |
dependabot[bot] | fcda97197a | |
dependabot[bot] | ede9777a5f | |
dependabot[bot] | 33e8c55dc8 | |
dependabot[bot] | dcf7624e4c | |
Andrew Gaul | 90d6abc7fb | |
Andrew Gaul | bf879d653e | |
Andrew Gaul | 8c1fc80e9e | |
dependabot[bot] | dd73dff0ac | |
momez | 435eb37bf8 | |
Andrew Gaul | b1453bd8f6 | |
momez | 6bb0250103 | |
dependabot[bot] | 19897069b4 | |
dependabot[bot] | 35c0719383 | |
dependabot[bot] | 3ba2dc576b | |
Andrew Gaul | 10469f40c0 | |
Steffen | 671a8f539a | |
ali-firat.kilic | 8165be6b17 | |
Steffen | 0af09a557b | |
dependabot[bot] | 87d3db457c | |
Steffen | d35eb257b4 | |
snpz | 40bdb6c1f9 | |
dependabot[bot] | ea9fb9fd30 | |
dependabot[bot] | 309e08396d | |
dependabot[bot] | 7e8462d417 | |
Andrew Gaul | a7aa9a63ac | |
Andrew Gaul | 4aeff5fb28 | |
Andrew Gaul | a732dca4c2 | |
Andrew Gaul | 1dac9ccd12 | |
Andrew Gaul | e5fb3619df | |
dependabot[bot] | 4e8b57b227 | |
dependabot[bot] | b571235168 | |
jixinchi | 2a44bcd709 | |
dependabot[bot] | 608934309b | |
Lars Hagen | 2281c74150 | |
jixinchi | 60f8366d33 | |
Steven Sheehy | e6955afb43 | |
Lars Hagen | 521bc14ab6 | |
Raul Sampedro | 916af55366 | |
Raul Sampedro | 06702abf3f | |
Raul Sampedro | 5682b10c71 | |
dependabot[bot] | 25e79454c2 | |
Shane St Savage | d52ceb134b | |
Andrew Gaul | 1c6a1aca9e |
|
@ -6,34 +6,33 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
dockerhub_publish: ${{ secrets.DOCKER_PASS != '' }}
|
||||
|
||||
jobs:
|
||||
|
||||
meta:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
dockerhub-publish: ${{ steps.dockerhub-publish.outputs.defined }}
|
||||
registry: ghcr.io/${{ github.repository }}/container:${{ fromJSON(steps.docker_action_meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||
container_tags: ${{ steps.docker_action_meta.outputs.tags }}
|
||||
container_labels: ${{ steps.docker_action_meta.outputs.labels }}
|
||||
container_buildtime: ${{ fromJSON(steps.docker_action_meta.outputs.json).labels['org.opencontainers.image.created'] }}
|
||||
container_version: ${{ fromJSON(steps.docker_action_meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||
container_revision: ${{ fromJSON(steps.docker_action_meta.outputs.json).labels['org.opencontainers.image.revision'] }}
|
||||
container_base: ${{ fromJSON(steps.docker_action_meta.outputs.json).tags[0] }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
persist-credentials: false
|
||||
- id: dockerhub-publish
|
||||
if: "${{ env.MY_KEY != '' }}"
|
||||
run: echo "::set-output name=defined::true"
|
||||
env:
|
||||
MY_KEY: ${{ secrets.DOCKER_PASS }}
|
||||
- name: Docker meta
|
||||
id: docker_action_meta
|
||||
uses: docker/metadata-action@v4.0.1
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}/container
|
||||
images: |
|
||||
name=ghcr.io/${{ github.repository }}/container
|
||||
name=andrewgaul/s3proxy,enable=${{ env.dockerhub_publish }}
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
|
@ -49,11 +48,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
needs: [meta]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
|
@ -91,7 +90,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
needs: [runTests, meta]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: s3proxy
|
||||
|
@ -103,17 +102,17 @@ jobs:
|
|||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2.0.0
|
||||
if: github.event_name != 'pull_request' && needs.meta.outputs.dockerhub-publish == 'true'
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request' && env.dockerhub_publish == 'true'
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v2.0.0
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
|
@ -121,26 +120,16 @@ jobs:
|
|||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ needs.meta.outputs.container_base }}
|
||||
tags: ${{ needs.meta.outputs.container_tags }}
|
||||
labels: ${{ needs.meta.outputs.container_labels }}
|
||||
build-args: |
|
||||
BUILDTIME=${{ needs.meta.outputs.container_buildtime }}
|
||||
VERSION=${{ needs.meta.outputs.container_version }}
|
||||
REVISION=${{ needs.meta.outputs.container_revision }}
|
||||
cache-from: type=registry,ref=${{ needs.meta.outputs.container_base }}
|
||||
cache-to: type=inline
|
||||
|
||||
- name: Publish to Docker
|
||||
if: github.event_name != 'pull_request' && needs.meta.outputs.dockerhub-publish == 'true'
|
||||
run: |
|
||||
curl -L https://github.com/regclient/regclient/releases/download/v0.3.5/regctl-linux-amd64 >/tmp/regctl
|
||||
chmod 755 /tmp/regctl
|
||||
for line in $CONTAINER_DEST_TAGS; do echo working on "$line"; /tmp/regctl image copy $SOURCE_CONTAINER $line; done
|
||||
env:
|
||||
SOURCE_CONTAINER: ${{ needs.meta.outputs.container_version }}
|
||||
CONTAINER_DEST_TAGS: ${{ needs.meta.outputs.container_tags }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM openjdk:11-jre-slim
|
||||
FROM docker.io/library/eclipse-temurin:17-jre
|
||||
LABEL maintainer="Andrew Gaul <andrew@gaul.org>"
|
||||
|
||||
WORKDIR /opt/s3proxy
|
||||
|
@ -26,6 +26,7 @@ ENV \
|
|||
S3PROXY_ENCRYPTED_BLOBSTORE="" \
|
||||
S3PROXY_ENCRYPTED_BLOBSTORE_PASSWORD="" \
|
||||
S3PROXY_ENCRYPTED_BLOBSTORE_SALT="" \
|
||||
S3PROXY_READ_ONLY_BLOBSTORE="false" \
|
||||
JCLOUDS_PROVIDER="filesystem" \
|
||||
JCLOUDS_ENDPOINT="" \
|
||||
JCLOUDS_REGION="" \
|
||||
|
|
|
@ -96,17 +96,19 @@ s3proxy.bucket-locator.2=another-bucket
|
|||
In addition to the explicit names, [glob syntax](https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob) can be used to configure many
|
||||
buckets for a given backend.
|
||||
|
||||
A bucket (or a glob) cannot be assigned cannot be assigned to multiple backends.
|
||||
A bucket (or a glob) cannot be assigned to multiple backends.
|
||||
|
||||
## Middlewares
|
||||
|
||||
S3Proxy can modify its behavior based on middlewares:
|
||||
|
||||
* [bucket aliasing](https://github.com/gaul/s3proxy/wiki/Middleware-alias-blobstore)
|
||||
* [bucket locator](https://github.com/gaul/s3proxy/wiki/Middleware-bucket-locator)
|
||||
* [eventual consistency modeling](https://github.com/gaul/s3proxy/wiki/Middleware---eventual-consistency)
|
||||
* [large object mocking](https://github.com/gaul/s3proxy/wiki/Middleware-large-object-mocking)
|
||||
* [read-only](https://github.com/gaul/s3proxy/wiki/Middleware-read-only)
|
||||
* [sharded backend containers](https://github.com/gaul/s3proxy/wiki/Middleware-sharded-backend)
|
||||
* [regex rename blobs](https://github.com/gaul/s3proxy/wiki/Middleware-regex)
|
||||
|
||||
## SSL Support
|
||||
|
||||
|
@ -144,7 +146,7 @@ s3proxy.cors-allow-credential=true
|
|||
```
|
||||
|
||||
CORS cannot be configured per bucket. `s3proxy.cors-allow-all=true` will accept any origin and header.
|
||||
Actual CORS requests are supported for GET, PUT and POST methods.
|
||||
Actual CORS requests are supported for GET, PUT, POST, HEAD and DELETE methods.
|
||||
|
||||
The wiki collects
|
||||
[compatibility notes](https://github.com/gaul/s3proxy/wiki/Storage-backend-compatibility)
|
||||
|
|
41
pom.xml
41
pom.xml
|
@ -10,7 +10,7 @@
|
|||
|
||||
<groupId>org.gaul</groupId>
|
||||
<artifactId>s3proxy</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>S3Proxy</name>
|
||||
|
@ -125,14 +125,13 @@
|
|||
<headerLocation>src/main/resources/copyright_header.txt</headerLocation>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
<!-- TODO: remove once source fixed -->
|
||||
<failOnViolation>false</failOnViolation>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.3.1</version>
|
||||
<version>10.17.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
|
@ -280,7 +279,7 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<parallel>all</parallel>
|
||||
<parallel>classes</parallel>
|
||||
<threadCount>1</threadCount>
|
||||
<argLine>-Xmx512m</argLine>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
|
@ -298,7 +297,7 @@
|
|||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>4.7.3.0</version>
|
||||
<version>4.8.3.1</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<omitVisitors>CrossSiteScripting,DefaultEncodingDetector,FindNullDeref</omitVisitors>
|
||||
|
@ -314,8 +313,9 @@
|
|||
<plugin>
|
||||
<groupId>org.skife.maven</groupId>
|
||||
<artifactId>really-executable-jar-maven-plugin</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<version>2.1.1</version>
|
||||
<configuration>
|
||||
<inputFile>target/s3proxy-${version}-jar-with-dependencies.jar</inputFile>
|
||||
<programFile>s3proxy</programFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
|
@ -330,7 +330,7 @@
|
|||
<plugin>
|
||||
<groupId>org.gaul</groupId>
|
||||
<artifactId>modernizer-maven-plugin</artifactId>
|
||||
<version>2.6.0</version>
|
||||
<version>2.9.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>modernizer</id>
|
||||
|
@ -347,7 +347,7 @@
|
|||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.13</version>
|
||||
<version>1.7.0</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
|
@ -361,10 +361,11 @@
|
|||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>11</java.version>
|
||||
<jclouds.version>2.5.0</jclouds.version>
|
||||
<slf4j.version>2.0.7</slf4j.version>
|
||||
<jclouds.version>2.6.0</jclouds.version>
|
||||
<jetty.version>11.0.20</jetty.version>
|
||||
<slf4j.version>2.0.13</slf4j.version>
|
||||
<shade.prefix>${project.groupId}.shaded</shade.prefix>
|
||||
<surefire.version>3.0.0</surefire.version>
|
||||
<surefire.version>3.2.5</surefire.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
|
@ -393,12 +394,12 @@
|
|||
<dependency>
|
||||
<groupId>args4j</groupId>
|
||||
<artifactId>args4j</artifactId>
|
||||
<version>2.33</version>
|
||||
<version>2.37</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.4.11</version>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
|
@ -415,19 +416,19 @@
|
|||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.9.0</version>
|
||||
<version>5.10.2</version>
|
||||
<!-- Required for S3ProxyExtension -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.13.3</version>
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-annotations</artifactId>
|
||||
<version>4.7.1</version>
|
||||
<version>4.8.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -465,12 +466,12 @@
|
|||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<!-- we need to use the same version as in jclouds because we pull in their tests -->
|
||||
<version>1.7.1</version>
|
||||
<version>3.25.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>11.0.16</version>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
@ -508,7 +509,7 @@
|
|||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>6.8.21</version>
|
||||
<version>7.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
2
s3-tests
2
s3-tests
|
@ -1 +1 @@
|
|||
Subproject commit bae8f04cfa5328f608347617896890cd219c021a
|
||||
Subproject commit 7ea471905cad975cced20384ef404d3f26f62df0
|
|
@ -17,7 +17,6 @@
|
|||
package org.gaul.s3proxy;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.gaul.s3proxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
|
@ -249,8 +248,8 @@ final class AwsSignature {
|
|||
return headersWithValues.toString();
|
||||
}
|
||||
|
||||
private static String buildCanonicalQueryString(HttpServletRequest request)
|
||||
throws UnsupportedEncodingException {
|
||||
private static String buildCanonicalQueryString(
|
||||
HttpServletRequest request) {
|
||||
// The parameters are required to be sorted
|
||||
List<String> parameters = Collections.list(request.getParameterNames());
|
||||
Collections.sort(parameters);
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
public final class CrossOriginResourceSharing {
|
||||
protected static final Collection<String> SUPPORTED_METHODS =
|
||||
ImmutableList.of("GET", "HEAD", "PUT", "POST");
|
||||
ImmutableList.of("GET", "HEAD", "PUT", "POST", "DELETE");
|
||||
|
||||
private static final String HEADER_VALUE_SEPARATOR = ", ";
|
||||
private static final String ALLOW_ANY_ORIGIN = "*";
|
||||
|
|
|
@ -71,7 +71,7 @@ public final class EncryptedBlobStore extends ForwardingBlobStore {
|
|||
private SecretKeySpec secretKey;
|
||||
|
||||
private EncryptedBlobStore(BlobStore blobStore, Properties properties)
|
||||
throws IllegalArgumentException {
|
||||
throws IllegalArgumentException {
|
||||
super(blobStore);
|
||||
|
||||
String password = properties.getProperty(
|
||||
|
@ -92,7 +92,7 @@ public final class EncryptedBlobStore extends ForwardingBlobStore {
|
|||
}
|
||||
|
||||
private void initStore(String password, String salt)
|
||||
throws IllegalArgumentException {
|
||||
throws IllegalArgumentException {
|
||||
try {
|
||||
SecretKeyFactory factory =
|
||||
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.gaul.s3proxy;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.util.Deque;
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
|
@ -247,6 +248,13 @@ public final class Main {
|
|||
blobStore = AliasBlobStore.newAliasBlobStore(blobStore, aliases);
|
||||
}
|
||||
|
||||
ImmutableList<Map.Entry<Pattern, String>> regexs =
|
||||
RegexBlobStore.parseRegexs(properties);
|
||||
if (!regexs.isEmpty()) {
|
||||
System.err.println("Using regex backend");
|
||||
blobStore = RegexBlobStore.newRegexBlobStore(blobStore, regexs);
|
||||
}
|
||||
|
||||
ImmutableMap<String, Integer> shards =
|
||||
ShardedBlobStore.parseBucketShards(properties);
|
||||
ImmutableMap<String, String> prefixes =
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright 2014-2021 Andrew Gaul <andrew@gaul.org>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.gaul.s3proxy;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.blobstore.domain.BlobAccess;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.blobstore.options.CopyOptions;
|
||||
import org.jclouds.blobstore.options.PutOptions;
|
||||
import org.jclouds.blobstore.util.ForwardingBlobStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class implements a middleware to apply regex to blob names.
|
||||
* The regex are configured as:
|
||||
* s3proxy.regex-blobstore.match.<regex name> = <regex match
|
||||
* expression>
|
||||
* s3proxy.regex-blobstore.replace.<regex name> = <regex replace
|
||||
* expression>
|
||||
*
|
||||
* You can add multiple regex, they will be applied from the beginning to the
|
||||
* end,
|
||||
* stopping as soon as the first regex matches.
|
||||
*/
|
||||
public final class RegexBlobStore extends ForwardingBlobStore {
|
||||
private static final Logger logger = LoggerFactory.getLogger(
|
||||
RegexBlobStore.class);
|
||||
|
||||
private final ImmutableList<Entry<Pattern, String>> regexs;
|
||||
|
||||
private RegexBlobStore(BlobStore blobStore,
|
||||
ImmutableList<Entry<Pattern, String>> regexs) {
|
||||
super(blobStore);
|
||||
this.regexs = requireNonNull(regexs);
|
||||
}
|
||||
|
||||
static BlobStore newRegexBlobStore(BlobStore delegate,
|
||||
ImmutableList<Entry<Pattern, String>> regexs) {
|
||||
return new RegexBlobStore(delegate, regexs);
|
||||
}
|
||||
|
||||
public static ImmutableList<Map.Entry<Pattern, String>> parseRegexs(
|
||||
Properties properties) {
|
||||
List<Entry<String, String>> configRegex = new ArrayList<>();
|
||||
List<Entry<Pattern, String>> regexs = new ArrayList<>();
|
||||
|
||||
for (String key : properties.stringPropertyNames()) {
|
||||
if (key.startsWith(S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE)) {
|
||||
String propKey = key.substring(
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE.length() + 1);
|
||||
String value = properties.getProperty(key);
|
||||
|
||||
configRegex.add(new SimpleEntry<>(propKey, value));
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<String, String> entry : configRegex) {
|
||||
String key = entry.getKey();
|
||||
if (key.startsWith(
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE_MATCH)) {
|
||||
String regexName = key.substring(S3ProxyConstants
|
||||
.PROPERTY_REGEX_BLOBSTORE_MATCH.length() + 1);
|
||||
String regex = entry.getValue();
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
|
||||
String replace = properties.getProperty(String.join(
|
||||
".", S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE,
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE_REPLACE,
|
||||
regexName));
|
||||
|
||||
checkArgument(
|
||||
replace != null,
|
||||
"Regex %s has no replace property associated",
|
||||
regexName);
|
||||
|
||||
logger.info(
|
||||
"Adding new regex with name {} replaces with {} to {}",
|
||||
regexName, regex, replace);
|
||||
|
||||
regexs.add(new SimpleEntry<>(pattern, replace));
|
||||
}
|
||||
}
|
||||
|
||||
return ImmutableList.copyOf(regexs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean directoryExists(String container, String directory) {
|
||||
return super.directoryExists(container, replaceBlobName(directory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(String container, String directory) {
|
||||
super.createDirectory(container, replaceBlobName(directory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDirectory(String container, String directory) {
|
||||
super.deleteDirectory(container, replaceBlobName(directory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean blobExists(String container, String name) {
|
||||
return super.blobExists(container, replaceBlobName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String putBlob(String containerName, Blob blob) {
|
||||
String name = blob.getMetadata().getName();
|
||||
String newName = replaceBlobName(name);
|
||||
blob.getMetadata().setName(newName);
|
||||
|
||||
logger.debug("Renaming blob name from {} to {}", name, newName);
|
||||
|
||||
return super.putBlob(containerName, blob);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String putBlob(String containerName, Blob blob,
|
||||
PutOptions putOptions) {
|
||||
String name = blob.getMetadata().getName();
|
||||
String newName = replaceBlobName(name);
|
||||
blob.getMetadata().setName(newName);
|
||||
|
||||
logger.debug("Renaming blob name from {} to {}", name, newName);
|
||||
|
||||
return super.putBlob(containerName, blob, putOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String copyBlob(String fromContainer, String fromName,
|
||||
String toContainer, String toName, CopyOptions options) {
|
||||
return super.copyBlob(fromContainer, replaceBlobName(fromName),
|
||||
toContainer, replaceBlobName(toName), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlobMetadata blobMetadata(String container, String name) {
|
||||
return super.blobMetadata(container, replaceBlobName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob getBlob(String containerName, String name) {
|
||||
return super.getBlob(containerName, replaceBlobName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBlob(String container, String name) {
|
||||
super.removeBlob(container, replaceBlobName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBlobs(String container, Iterable<String> iterable) {
|
||||
List<String> blobs = new ArrayList<>();
|
||||
for (String name : iterable) {
|
||||
blobs.add(replaceBlobName(name));
|
||||
}
|
||||
super.removeBlobs(container, blobs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlobAccess getBlobAccess(String container, String name) {
|
||||
return super.getBlobAccess(container, replaceBlobName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlobAccess(String container, String name,
|
||||
BlobAccess access) {
|
||||
super.setBlobAccess(container, replaceBlobName(name), access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadBlob(String container, String name, File destination) {
|
||||
super.downloadBlob(container, replaceBlobName(name), destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadBlob(String container, String name, File destination,
|
||||
ExecutorService executor) {
|
||||
super.downloadBlob(container, replaceBlobName(name), destination,
|
||||
executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream streamBlob(String container, String name) {
|
||||
return super.streamBlob(container, replaceBlobName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream streamBlob(String container, String name,
|
||||
ExecutorService executor) {
|
||||
return super.streamBlob(container, replaceBlobName(name), executor);
|
||||
}
|
||||
|
||||
private String replaceBlobName(String name) {
|
||||
String newName = name;
|
||||
|
||||
for (Map.Entry<Pattern, String> entry : this.regexs) {
|
||||
Pattern pattern = entry.getKey();
|
||||
Matcher match = pattern.matcher(name);
|
||||
|
||||
if (match.find()) {
|
||||
return match.replaceAll(entry.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return newName;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
package org.gaul.s3proxy;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.net.URI;
|
||||
|
@ -32,6 +31,7 @@ import com.google.common.base.Strings;
|
|||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.http.UriCompliance;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
|
@ -87,6 +87,7 @@ public final class S3Proxy {
|
|||
|
||||
HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||
httpConfiguration.setHttpCompliance(HttpCompliance.LEGACY);
|
||||
httpConfiguration.setUriCompliance(UriCompliance.LEGACY);
|
||||
SecureRequestCustomizer src = new SecureRequestCustomizer();
|
||||
src.setSniHostCheck(false);
|
||||
httpConfiguration.addCustomizer(src);
|
||||
|
@ -140,7 +141,7 @@ public final class S3Proxy {
|
|||
private String keyStorePassword;
|
||||
private String virtualHost;
|
||||
private long maxSinglePartObjectSize = 5L * 1024 * 1024 * 1024;
|
||||
private long v4MaxNonChunkedRequestSize = 32 * 1024 * 1024;
|
||||
private long v4MaxNonChunkedRequestSize = 128 * 1024 * 1024;
|
||||
private boolean ignoreUnknownHeaders;
|
||||
private CrossOriginResourceSharing corsRules;
|
||||
private int jettyMaxThreads = 200; // sourced from QueuedThreadPool()
|
||||
|
@ -161,16 +162,18 @@ public final class S3Proxy {
|
|||
S3ProxyConstants.PROPERTY_ENDPOINT);
|
||||
String secureEndpoint = properties.getProperty(
|
||||
S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
|
||||
if (endpoint == null && secureEndpoint == null) {
|
||||
boolean hasEndpoint = !Strings.isNullOrEmpty(endpoint);
|
||||
boolean hasSecureEndpoint = !Strings.isNullOrEmpty(secureEndpoint);
|
||||
if (!hasEndpoint && !hasSecureEndpoint) {
|
||||
throw new IllegalArgumentException(
|
||||
"Properties file must contain: " +
|
||||
S3ProxyConstants.PROPERTY_ENDPOINT + " or " +
|
||||
S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
|
||||
}
|
||||
if (endpoint != null) {
|
||||
if (hasEndpoint) {
|
||||
builder.endpoint(new URI(endpoint));
|
||||
}
|
||||
if (secureEndpoint != null) {
|
||||
if (hasSecureEndpoint) {
|
||||
builder.secureEndpoint(new URI(secureEndpoint));
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,13 @@ public final class S3ProxyConstants {
|
|||
/** Alias a backend bucket to an alternate name. */
|
||||
public static final String PROPERTY_ALIAS_BLOBSTORE =
|
||||
"s3proxy.alias-blobstore";
|
||||
/** Alias a backend bucket to an alternate name. */
|
||||
public static final String PROPERTY_REGEX_BLOBSTORE =
|
||||
"s3proxy.regex-blobstore";
|
||||
public static final String PROPERTY_REGEX_BLOBSTORE_MATCH =
|
||||
"match";
|
||||
public static final String PROPERTY_REGEX_BLOBSTORE_REPLACE =
|
||||
"replace";
|
||||
/** Discard object data. */
|
||||
public static final String PROPERTY_NULL_BLOBSTORE =
|
||||
"s3proxy.null-blobstore";
|
||||
|
|
|
@ -612,8 +612,9 @@ public class S3ProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (!constantTimeEquals(expectedSignature,
|
||||
authHeader.getSignature())) {
|
||||
// AWS does not check signatures with OPTIONS verb
|
||||
if (!method.equals("OPTIONS") && !constantTimeEquals(
|
||||
expectedSignature, authHeader.getSignature())) {
|
||||
throw new S3Exception(S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
||||
}
|
||||
}
|
||||
|
@ -660,26 +661,28 @@ public class S3ProxyHandler {
|
|||
switch (method) {
|
||||
case "DELETE":
|
||||
if (path.length <= 2 || path[2].isEmpty()) {
|
||||
handleContainerDelete(response, blobStore, path[1]);
|
||||
handleContainerDelete(request, response, blobStore, path[1]);
|
||||
return;
|
||||
} else if (uploadId != null) {
|
||||
handleAbortMultipartUpload(request, response, blobStore,
|
||||
path[1], path[2], uploadId);
|
||||
return;
|
||||
} else {
|
||||
handleBlobRemove(response, blobStore, path[1], path[2]);
|
||||
handleBlobRemove(request, response, blobStore, path[1],
|
||||
path[2]);
|
||||
return;
|
||||
}
|
||||
case "GET":
|
||||
if (uri.equals("/")) {
|
||||
handleContainerList(response, blobStore);
|
||||
handleContainerList(request, response, blobStore);
|
||||
return;
|
||||
} else if (path.length <= 2 || path[2].isEmpty()) {
|
||||
if (request.getParameter("acl") != null) {
|
||||
handleGetContainerAcl(response, blobStore, path[1]);
|
||||
handleGetContainerAcl(request, response, blobStore,
|
||||
path[1]);
|
||||
return;
|
||||
} else if (request.getParameter("location") != null) {
|
||||
handleContainerLocation(response);
|
||||
handleContainerLocation(request, response);
|
||||
return;
|
||||
} else if (request.getParameter("policy") != null) {
|
||||
handleBucketPolicy(blobStore, path[1]);
|
||||
|
@ -693,7 +696,7 @@ public class S3ProxyHandler {
|
|||
return;
|
||||
} else {
|
||||
if (request.getParameter("acl") != null) {
|
||||
handleGetBlobAcl(response, blobStore, path[1],
|
||||
handleGetBlobAcl(request, response, blobStore, path[1],
|
||||
path[2]);
|
||||
return;
|
||||
} else if (uploadId != null) {
|
||||
|
@ -707,7 +710,7 @@ public class S3ProxyHandler {
|
|||
}
|
||||
case "HEAD":
|
||||
if (path.length <= 2 || path[2].isEmpty()) {
|
||||
handleContainerExists(blobStore, path[1]);
|
||||
handleContainerExists(request, response, blobStore, path[1]);
|
||||
return;
|
||||
} else {
|
||||
handleBlobMetadata(request, response, blobStore, path[1],
|
||||
|
@ -716,7 +719,8 @@ public class S3ProxyHandler {
|
|||
}
|
||||
case "POST":
|
||||
if (request.getParameter("delete") != null) {
|
||||
handleMultiBlobRemove(response, is, blobStore, path[1]);
|
||||
handleMultiBlobRemove(request, response, is, blobStore,
|
||||
path[1]);
|
||||
return;
|
||||
} else if (request.getParameter("uploads") != null) {
|
||||
handleInitiateMultipartUpload(request, response, blobStore,
|
||||
|
@ -848,15 +852,6 @@ public class S3ProxyHandler {
|
|||
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
||||
} else {
|
||||
String containerName = path[1];
|
||||
/*
|
||||
* Only check access on bucket level. The preflight request
|
||||
* might be for a PUT, so the object is not yet there.
|
||||
*/
|
||||
ContainerAccess access = blobStore.getContainerAccess(
|
||||
containerName);
|
||||
if (access == ContainerAccess.PRIVATE) {
|
||||
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
||||
}
|
||||
handleOptionsBlob(request, response, blobStore, containerName);
|
||||
return;
|
||||
}
|
||||
|
@ -868,15 +863,16 @@ public class S3ProxyHandler {
|
|||
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
private void handleGetContainerAcl(HttpServletResponse response,
|
||||
BlobStore blobStore, String containerName)
|
||||
throws IOException, S3Exception {
|
||||
private void handleGetContainerAcl(HttpServletRequest request,
|
||||
HttpServletResponse response, BlobStore blobStore,
|
||||
String containerName) throws IOException, S3Exception {
|
||||
if (!blobStore.containerExists(containerName)) {
|
||||
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||
}
|
||||
ContainerAccess access = blobStore.getContainerAccess(containerName);
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -967,14 +963,16 @@ public class S3ProxyHandler {
|
|||
}
|
||||
|
||||
blobStore.setContainerAccess(containerName, access);
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleGetBlobAcl(HttpServletResponse response,
|
||||
BlobStore blobStore, String containerName,
|
||||
String blobName) throws IOException {
|
||||
private void handleGetBlobAcl(HttpServletRequest request,
|
||||
HttpServletResponse response, BlobStore blobStore,
|
||||
String containerName, String blobName) throws IOException {
|
||||
BlobAccess access = blobStore.getBlobAccess(containerName, blobName);
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -1066,6 +1064,7 @@ public class S3ProxyHandler {
|
|||
}
|
||||
|
||||
blobStore.setBlobAccess(containerName, blobName, access);
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
/** Map XML ACLs to a canned policy if an exact tranformation exists. */
|
||||
|
@ -1105,11 +1104,13 @@ public class S3ProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleContainerList(HttpServletResponse response,
|
||||
BlobStore blobStore) throws IOException {
|
||||
private void handleContainerList(HttpServletRequest request,
|
||||
HttpServletResponse response, BlobStore blobStore)
|
||||
throws IOException {
|
||||
PageSet<? extends StorageMetadata> buckets = blobStore.list();
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -1148,9 +1149,10 @@ public class S3ProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleContainerLocation(HttpServletResponse response)
|
||||
throws IOException {
|
||||
private void handleContainerLocation(HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -1191,6 +1193,7 @@ public class S3ProxyHandler {
|
|||
container);
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -1251,11 +1254,13 @@ public class S3ProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private static void handleContainerExists(BlobStore blobStore,
|
||||
private void handleContainerExists(HttpServletRequest request,
|
||||
HttpServletResponse response, BlobStore blobStore,
|
||||
String containerName) throws IOException, S3Exception {
|
||||
if (!blobStore.containerExists(containerName)) {
|
||||
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||
}
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleContainerCreate(HttpServletRequest request,
|
||||
|
@ -1331,11 +1336,12 @@ public class S3ProxyHandler {
|
|||
}
|
||||
|
||||
response.addHeader(HttpHeaders.LOCATION, "/" + containerName);
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private static void handleContainerDelete(HttpServletResponse response,
|
||||
BlobStore blobStore, String containerName)
|
||||
throws IOException, S3Exception {
|
||||
private void handleContainerDelete(HttpServletRequest request,
|
||||
HttpServletResponse response, BlobStore blobStore,
|
||||
String containerName) throws IOException, S3Exception {
|
||||
if (!blobStore.containerExists(containerName)) {
|
||||
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||
}
|
||||
|
@ -1354,6 +1360,7 @@ public class S3ProxyHandler {
|
|||
throw new S3Exception(S3ErrorCode.BUCKET_NOT_EMPTY);
|
||||
}
|
||||
|
||||
addCorsResponseHeader(request, response);
|
||||
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
|
||||
|
@ -1488,10 +1495,13 @@ public class S3ProxyHandler {
|
|||
isListV2 ? "NextContinuationToken" : "NextMarker",
|
||||
encodeBlob(encodingType, nextMarker));
|
||||
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
|
||||
StorageMetadata sm = Streams.findLast(set.stream()).orElse(null);
|
||||
StorageMetadata sm = Streams.findLast(
|
||||
set.stream()).orElse(null);
|
||||
if (sm != null) {
|
||||
lastKeyToMarker.put(Maps.immutableEntry(containerName,
|
||||
encodeBlob(encodingType, nextMarker)), nextMarker);
|
||||
lastKeyToMarker.put(Maps.immutableEntry(
|
||||
containerName,
|
||||
encodeBlob(encodingType, nextMarker)),
|
||||
nextMarker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1504,8 +1514,11 @@ public class S3ProxyHandler {
|
|||
case FOLDER:
|
||||
// fallthrough
|
||||
case RELATIVE_PATH:
|
||||
commonPrefixes.add(metadata.getName());
|
||||
continue;
|
||||
if (delimiter != null) {
|
||||
commonPrefixes.add(metadata.getName());
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1526,10 +1539,16 @@ public class S3ProxyHandler {
|
|||
writeSimpleElement(xml, "ETag", maybeQuoteETag(eTag));
|
||||
}
|
||||
|
||||
writeSimpleElement(xml, "Size",
|
||||
String.valueOf(metadata.getSize()));
|
||||
writeSimpleElement(xml, "StorageClass",
|
||||
StorageClass.fromTier(metadata.getTier()).toString());
|
||||
Long size = metadata.getSize();
|
||||
if (size != null) {
|
||||
writeSimpleElement(xml, "Size", String.valueOf(size));
|
||||
}
|
||||
|
||||
Tier tier = metadata.getTier();
|
||||
if (tier != null) {
|
||||
writeSimpleElement(xml, "StorageClass",
|
||||
StorageClass.fromTier(tier).toString());
|
||||
}
|
||||
|
||||
if (fetchOwner) {
|
||||
writeOwnerStanza(xml);
|
||||
|
@ -1554,15 +1573,18 @@ public class S3ProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private static void handleBlobRemove(HttpServletResponse response,
|
||||
BlobStore blobStore, String containerName,
|
||||
String blobName) throws IOException, S3Exception {
|
||||
private void handleBlobRemove(HttpServletRequest request,
|
||||
HttpServletResponse response, BlobStore blobStore,
|
||||
String containerName, String blobName)
|
||||
throws IOException, S3Exception {
|
||||
blobStore.removeBlob(containerName, blobName);
|
||||
addCorsResponseHeader(request, response);
|
||||
response.sendError(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
|
||||
private void handleMultiBlobRemove(HttpServletResponse response,
|
||||
InputStream is, BlobStore blobStore, String containerName)
|
||||
private void handleMultiBlobRemove(HttpServletRequest request,
|
||||
HttpServletResponse response, InputStream is,
|
||||
BlobStore blobStore, String containerName)
|
||||
throws IOException, S3Exception {
|
||||
DeleteMultipleObjectsRequest dmor = mapper.readValue(
|
||||
is, DeleteMultipleObjectsRequest.class);
|
||||
|
@ -1579,6 +1601,7 @@ public class S3ProxyHandler {
|
|||
blobStore.removeBlobs(containerName, blobNames);
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -1605,7 +1628,7 @@ public class S3ProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private static void handleBlobMetadata(HttpServletRequest request,
|
||||
private void handleBlobMetadata(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
BlobStore blobStore, String containerName,
|
||||
String blobName) throws IOException, S3Exception {
|
||||
|
@ -1650,6 +1673,7 @@ public class S3ProxyHandler {
|
|||
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
addMetadataToResponse(request, response, metadata);
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleOptionsBlob(HttpServletRequest request,
|
||||
|
@ -1871,6 +1895,7 @@ public class S3ProxyHandler {
|
|||
BlobMetadata blobMetadata = blobStore.blobMetadata(destContainerName,
|
||||
destBlobName);
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -2200,6 +2225,7 @@ public class S3ProxyHandler {
|
|||
}
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -2217,8 +2243,6 @@ public class S3ProxyHandler {
|
|||
} catch (XMLStreamException xse) {
|
||||
throw new IOException(xse);
|
||||
}
|
||||
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleCompleteMultipartUpload(HttpServletRequest request,
|
||||
|
@ -2229,7 +2253,7 @@ public class S3ProxyHandler {
|
|||
PutOptions options;
|
||||
if (Quirks.MULTIPART_REQUIRES_STUB.contains(getBlobStoreType(
|
||||
blobStore))) {
|
||||
metadata = blobStore.getBlob(containerName, uploadId).getMetadata();
|
||||
metadata = blobStore.blobMetadata(containerName, uploadId);
|
||||
BlobAccess access = blobStore.getBlobAccess(containerName,
|
||||
uploadId);
|
||||
options = new PutOptions().setBlobAccess(access);
|
||||
|
@ -2325,6 +2349,7 @@ public class S3ProxyHandler {
|
|||
}
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (PrintWriter writer = response.getWriter()) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
|
@ -2388,8 +2413,6 @@ public class S3ProxyHandler {
|
|||
} catch (XMLStreamException xse) {
|
||||
throw new IOException(xse);
|
||||
}
|
||||
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleAbortMultipartUpload(HttpServletRequest request,
|
||||
|
@ -2455,6 +2478,7 @@ public class S3ProxyHandler {
|
|||
String encodingType = request.getParameter("encoding-type");
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -2512,8 +2536,6 @@ public class S3ProxyHandler {
|
|||
} catch (XMLStreamException xse) {
|
||||
throw new IOException(xse);
|
||||
}
|
||||
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleCopyPart(HttpServletRequest request,
|
||||
|
@ -2610,6 +2632,7 @@ public class S3ProxyHandler {
|
|||
blobName, uploadId, createFakeBlobMetadata(blobStore),
|
||||
new PutOptions());
|
||||
|
||||
// TODO: Blob can leak on precondition failures.
|
||||
Blob blob = blobStore.getBlob(sourceContainerName, sourceBlobName,
|
||||
options);
|
||||
if (blob == null) {
|
||||
|
@ -2689,6 +2712,7 @@ public class S3ProxyHandler {
|
|||
}
|
||||
|
||||
response.setCharacterEncoding(UTF_8);
|
||||
addCorsResponseHeader(request, response);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
response.setContentType(XML_CONTENT_TYPE);
|
||||
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||
|
@ -2707,8 +2731,6 @@ public class S3ProxyHandler {
|
|||
} catch (XMLStreamException xse) {
|
||||
throw new IOException(xse);
|
||||
}
|
||||
|
||||
addCorsResponseHeader(request, response);
|
||||
}
|
||||
|
||||
private void handleUploadPart(HttpServletRequest request,
|
||||
|
@ -2995,7 +3017,8 @@ public class S3ProxyHandler {
|
|||
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,
|
||||
corsRules.getAllowedMethods());
|
||||
if (corsRules.isAllowCredentials()) {
|
||||
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
|
||||
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
"true");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,9 +77,8 @@ public class DecryptionInputStream extends FilterInputStream {
|
|||
* @throws IOException if cipher fails
|
||||
*/
|
||||
public DecryptionInputStream(InputStream is, SecretKey key,
|
||||
TreeMap<Integer, PartPadding> parts, int skipParts,
|
||||
long skipPartBytes)
|
||||
throws IOException {
|
||||
TreeMap<Integer, PartPadding> parts, int skipParts,
|
||||
long skipPartBytes) throws IOException {
|
||||
super(is);
|
||||
in = is;
|
||||
this.parts = parts;
|
||||
|
|
|
@ -33,7 +33,7 @@ public class Encryption {
|
|||
private final int part;
|
||||
|
||||
public Encryption(SecretKeySpec key, InputStream isRaw, int partNumber)
|
||||
throws Exception {
|
||||
throws Exception {
|
||||
iv = generateIV();
|
||||
|
||||
Cipher cipher = Cipher.getInstance(Constants.AES_CIPHER);
|
||||
|
|
|
@ -40,7 +40,7 @@ public class PartPadding {
|
|||
private short version;
|
||||
|
||||
public static PartPadding readPartPaddingFromBlob(Blob blob)
|
||||
throws IOException {
|
||||
throws IOException {
|
||||
PartPadding partPadding = new PartPadding();
|
||||
|
||||
InputStream is = blob.getPayload().openStream();
|
||||
|
|
|
@ -116,9 +116,11 @@ public class S3ProxyJunitCore {
|
|||
builder.blobStoreProvider)
|
||||
.overrides(properties);
|
||||
if (!AuthenticationType.NONE.equals(builder.authType)) {
|
||||
blobStoreContextBuilder = blobStoreContextBuilder.credentials(accessKey, secretKey);
|
||||
blobStoreContextBuilder = blobStoreContextBuilder.credentials(
|
||||
accessKey, secretKey);
|
||||
}
|
||||
blobStoreContext = blobStoreContextBuilder.build(BlobStoreContext.class);
|
||||
blobStoreContext = blobStoreContextBuilder.build(
|
||||
BlobStoreContext.class);
|
||||
|
||||
S3Proxy.Builder s3ProxyBuilder = S3Proxy.builder()
|
||||
.blobStore(blobStoreContext.getBlobStore())
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<property name="classes" value="java.lang.Boolean,java.lang.Short,java.lang.Integer,java.lang.Long"/>
|
||||
</module>
|
||||
<module name="ImportOrder">
|
||||
<property name="groups" value="java,javax,com,org"/>
|
||||
<property name="groups" value="java,javax,com,jakarta,org"/>
|
||||
<property name="separated" value="true"/>
|
||||
<property name="option" value="top"/>
|
||||
</module>
|
||||
|
@ -118,7 +118,6 @@
|
|||
</module>
|
||||
<module name="TypecastParenPad"/>
|
||||
<module name="TypeName"/>
|
||||
<module name="UnnecessaryParentheses"/>
|
||||
<module name="UnusedImports"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="VisibilityModifier">
|
||||
|
|
|
@ -21,6 +21,7 @@ exec java \
|
|||
-Ds3proxy.encrypted-blobstore-password="${S3PROXY_ENCRYPTED_BLOBSTORE_PASSWORD}" \
|
||||
-Ds3proxy.encrypted-blobstore-salt="${S3PROXY_ENCRYPTED_BLOBSTORE_SALT}" \
|
||||
-Ds3proxy.v4-max-non-chunked-request-size="${S3PROXY_V4_MAX_NON_CHUNKED_REQ_SIZE:-33554432}" \
|
||||
-Ds3proxy.read-only-blobstore="${S3PROXY_READ_ONLY_BLOBSTORE:-false}" \
|
||||
-Djclouds.provider="${JCLOUDS_PROVIDER}" \
|
||||
-Djclouds.identity="${JCLOUDS_IDENTITY}" \
|
||||
-Djclouds.credential="${JCLOUDS_CREDENTIAL}" \
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.jclouds.blobstore.domain.StorageMetadata;
|
|||
import org.jclouds.blobstore.options.PutOptions;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
|
@ -35,7 +35,6 @@ import com.amazonaws.services.s3.model.S3Object;
|
|||
import com.google.common.io.ByteSource;
|
||||
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
|
@ -96,12 +96,10 @@ import com.google.common.collect.Maps;
|
|||
import com.google.common.io.ByteSource;
|
||||
|
||||
import org.assertj.core.api.Fail;
|
||||
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.rest.HttpClient;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
|
|
|
@ -40,7 +40,6 @@ import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
|
|||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import com.amazonaws.services.s3.model.CannedAccessControlList;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
|
@ -59,10 +58,8 @@ import org.apache.http.impl.client.CloseableHttpClient;
|
|||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -157,7 +154,7 @@ public final class CrossOriginResourceSharingAllowAllResponseTest {
|
|||
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue();
|
||||
assertThat(response.getFirstHeader(
|
||||
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS).getValue())
|
||||
.isEqualTo("GET, HEAD, PUT, POST");
|
||||
.isEqualTo("GET, HEAD, PUT, POST, DELETE");
|
||||
assertThat(response.containsHeader(
|
||||
HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)).isTrue();
|
||||
assertThat(response.getFirstHeader(
|
||||
|
@ -181,7 +178,7 @@ public final class CrossOriginResourceSharingAllowAllResponseTest {
|
|||
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue();
|
||||
assertThat(response.getFirstHeader(
|
||||
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS).getValue())
|
||||
.isEqualTo("GET, HEAD, PUT, POST");
|
||||
.isEqualTo("GET, HEAD, PUT, POST, DELETE");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -40,7 +40,6 @@ import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
|
|||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import com.amazonaws.services.s3.model.CannedAccessControlList;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
|
@ -59,10 +58,8 @@ import org.apache.http.impl.client.CloseableHttpClient;
|
|||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -140,58 +137,6 @@ public final class CrossOriginResourceSharingResponseTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorsPreflightNegative() throws Exception {
|
||||
// No CORS headers
|
||||
HttpOptions request = new HttpOptions(presignedGET);
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
/*
|
||||
* For non presigned URLs that should give a 400, but the
|
||||
* Access-Control-Request-Method header is needed for presigned URLs
|
||||
* to calculate the same signature. If this is missing it fails already
|
||||
* with 403 - Signature mismatch before processing the OPTIONS request
|
||||
* See testCorsPreflightPublicRead for that cases
|
||||
*/
|
||||
assertThat(response.getStatusLine().getStatusCode())
|
||||
.isEqualTo(HttpStatus.SC_FORBIDDEN);
|
||||
|
||||
// Not allowed origin
|
||||
request.reset();
|
||||
request.setHeader(HttpHeaders.ORIGIN, "https://example.org");
|
||||
request.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||
response = httpClient.execute(request);
|
||||
assertThat(response.getStatusLine().getStatusCode())
|
||||
.isEqualTo(HttpStatus.SC_FORBIDDEN);
|
||||
|
||||
// Not allowed method
|
||||
request.reset();
|
||||
request.setHeader(HttpHeaders.ORIGIN, "https://example.com");
|
||||
request.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PATCH");
|
||||
response = httpClient.execute(request);
|
||||
assertThat(response.getStatusLine().getStatusCode())
|
||||
.isEqualTo(HttpStatus.SC_FORBIDDEN);
|
||||
|
||||
// Not allowed header
|
||||
request.reset();
|
||||
request.setHeader(HttpHeaders.ORIGIN, "https://example.com");
|
||||
request.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||
request.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
"Accept-Encoding");
|
||||
response = httpClient.execute(request);
|
||||
assertThat(response.getStatusLine().getStatusCode())
|
||||
.isEqualTo(HttpStatus.SC_FORBIDDEN);
|
||||
|
||||
// Not allowed header combination
|
||||
request.reset();
|
||||
request.setHeader(HttpHeaders.ORIGIN, "https://example.com");
|
||||
request.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||
request.setHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
"Accept, Accept-Encoding");
|
||||
response = httpClient.execute(request);
|
||||
assertThat(response.getStatusLine().getStatusCode())
|
||||
.isEqualTo(HttpStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorsPreflight() throws Exception {
|
||||
// Allowed origin and method
|
||||
|
|
|
@ -107,6 +107,12 @@ public final class CrossOriginResourceSharingRuleTest {
|
|||
probe = "POST";
|
||||
assertThat(corsAll.isMethodAllowed(probe))
|
||||
.as("check '%s' as method", probe).isTrue();
|
||||
probe = "HEAD";
|
||||
assertThat(corsAll.isMethodAllowed(probe))
|
||||
.as("check '%s' as method", probe).isTrue();
|
||||
probe = "DELETE";
|
||||
assertThat(corsAll.isMethodAllowed(probe))
|
||||
.as("check '%s' as method", probe).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -48,7 +48,7 @@ import org.jclouds.s3.domain.ObjectMetadataBuilder;
|
|||
import org.jclouds.s3.domain.S3Object;
|
||||
import org.jclouds.s3.reference.S3Constants;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterSuite;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
|
@ -64,10 +64,11 @@ public final class EncryptedBlobStoreLiveTest extends S3ClientLiveTest {
|
|||
private S3Proxy s3Proxy;
|
||||
private BlobStoreContext context;
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() throws Exception {
|
||||
s3Proxy.stop();
|
||||
@AfterSuite
|
||||
@Override
|
||||
public void destroyResources() throws Exception {
|
||||
context.close();
|
||||
s3Proxy.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -242,7 +243,7 @@ public final class EncryptedBlobStoreLiveTest extends S3ClientLiveTest {
|
|||
@Override
|
||||
@Test
|
||||
public void testUpdateObjectACL() throws InterruptedException,
|
||||
ExecutionException, TimeoutException, IOException {
|
||||
ExecutionException, TimeoutException, IOException {
|
||||
try {
|
||||
super.testUpdateObjectACL();
|
||||
Fail.failBecauseExceptionWasNotThrown(AWSResponseException.class);
|
||||
|
@ -255,7 +256,7 @@ public final class EncryptedBlobStoreLiveTest extends S3ClientLiveTest {
|
|||
@Override
|
||||
@Test
|
||||
public void testPublicWriteOnObject() throws InterruptedException,
|
||||
ExecutionException, TimeoutException, IOException {
|
||||
ExecutionException, TimeoutException, IOException {
|
||||
try {
|
||||
super.testPublicWriteOnObject();
|
||||
Fail.failBecauseExceptionWasNotThrown(AWSResponseException.class);
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.jclouds.ContextBuilder;
|
|||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.jclouds.blobstore.BlobStoreContext;
|
|||
import org.jclouds.s3.reference.S3Constants;
|
||||
import org.jclouds.s3.services.BucketsLiveTest;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterSuite;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(testName = "JcloudsBucketsLiveTest")
|
||||
|
@ -46,10 +46,11 @@ public final class JcloudsBucketsLiveTest extends BucketsLiveTest {
|
|||
private BlobStoreContext context;
|
||||
private String blobStoreType;
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() throws Exception {
|
||||
s3Proxy.stop();
|
||||
@AfterSuite
|
||||
@Override
|
||||
public void destroyResources() throws Exception {
|
||||
context.close();
|
||||
s3Proxy.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.jclouds.blobstore.domain.Blob;
|
|||
import org.jclouds.s3.blobstore.integration.S3BlobIntegrationLiveTest;
|
||||
import org.jclouds.s3.reference.S3Constants;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterSuite;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(testName = "JcloudsS3BlobIntegrationLiveTest")
|
||||
|
@ -42,10 +42,11 @@ public final class JcloudsS3BlobIntegrationLiveTest
|
|||
private BlobStoreContext context;
|
||||
private String blobStoreType;
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() throws Exception {
|
||||
s3Proxy.stop();
|
||||
@AfterSuite
|
||||
@Override
|
||||
public void destroyResources() throws Exception {
|
||||
context.close();
|
||||
s3Proxy.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.jclouds.Constants;
|
|||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.s3.blobstore.integration.S3BlobSignerLiveTest;
|
||||
import org.jclouds.s3.reference.S3Constants;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterSuite;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(testName = "JcloudsS3BlobSignerLiveTest")
|
||||
|
@ -38,10 +38,11 @@ public final class JcloudsS3BlobSignerLiveTest extends S3BlobSignerLiveTest {
|
|||
private S3Proxy s3Proxy;
|
||||
private BlobStoreContext context;
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() throws Exception {
|
||||
s3Proxy.stop();
|
||||
@AfterSuite
|
||||
@Override
|
||||
public void destroyResources() throws Exception {
|
||||
context.close();
|
||||
s3Proxy.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.jclouds.s3.S3ClientLiveTest;
|
|||
import org.jclouds.s3.domain.S3Object;
|
||||
import org.jclouds.s3.reference.S3Constants;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterSuite;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(testName = "JcloudsS3ClientLiveTest")
|
||||
|
@ -50,10 +50,11 @@ public final class JcloudsS3ClientLiveTest extends S3ClientLiveTest {
|
|||
private BlobStoreContext context;
|
||||
private String blobStoreType;
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() throws Exception {
|
||||
s3Proxy.stop();
|
||||
@AfterSuite
|
||||
@Override
|
||||
public void destroyResources() throws Exception {
|
||||
context.close();
|
||||
s3Proxy.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.jclouds.blobstore.BlobStoreContext;
|
|||
import org.jclouds.s3.blobstore.integration.S3ContainerIntegrationLiveTest;
|
||||
import org.jclouds.s3.reference.S3Constants;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterSuite;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(testName = "JcloudsS3ContainerIntegrationLiveTest")
|
||||
|
@ -41,10 +41,11 @@ public final class JcloudsS3ContainerIntegrationLiveTest
|
|||
private BlobStoreContext context;
|
||||
private String blobStoreType;
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() throws Exception {
|
||||
s3Proxy.stop();
|
||||
@AfterSuite
|
||||
@Override
|
||||
public void destroyResources() throws Exception {
|
||||
context.close();
|
||||
s3Proxy.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright 2014-2021 Andrew Gaul <andrew@gaul.org>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.gaul.s3proxy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.inject.Module;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public final class RegexBlobStoreTest {
|
||||
private BlobStoreContext context;
|
||||
private BlobStore delegate;
|
||||
private String containerName;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
containerName = createRandomContainerName();
|
||||
|
||||
context = ContextBuilder
|
||||
.newBuilder("transient")
|
||||
.credentials("identity", "credential")
|
||||
.modules(ImmutableList.<Module>of(new SLF4JLoggingModule()))
|
||||
.build(BlobStoreContext.class);
|
||||
delegate = context.getBlobStore();
|
||||
delegate.createContainerInLocation(null, containerName);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (context != null) {
|
||||
delegate.deleteContainer(containerName);
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveSomeCharsFromName() throws IOException {
|
||||
ImmutableList.Builder<Map.Entry<Pattern, String>> regexBuilder =
|
||||
new ImmutableList.Builder<>();
|
||||
regexBuilder.add(new SimpleEntry<Pattern, String>(Pattern.compile(
|
||||
"[^a-zA-Z0-9/_.]"), "_"));
|
||||
BlobStore regexBlobStore = RegexBlobStore.newRegexBlobStore(delegate,
|
||||
regexBuilder.build());
|
||||
|
||||
String initialBlobName = "test/remove:badchars-folder/blob.txt";
|
||||
String targetBlobName = "test/remove_badchars_folder/blob.txt";
|
||||
ByteSource content = TestUtils.randomByteSource().slice(0, 1024);
|
||||
@SuppressWarnings("deprecation")
|
||||
String contentHash = Hashing.md5().hashBytes(content.read()).toString();
|
||||
Blob blob = regexBlobStore.blobBuilder(initialBlobName).payload(
|
||||
content).build();
|
||||
|
||||
String eTag = regexBlobStore.putBlob(containerName, blob);
|
||||
assertThat(eTag).isEqualTo(contentHash);
|
||||
|
||||
BlobMetadata blobMetadata = regexBlobStore.blobMetadata(
|
||||
containerName, targetBlobName);
|
||||
|
||||
assertThat(blobMetadata.getETag()).isEqualTo(contentHash);
|
||||
blob = regexBlobStore.getBlob(containerName, targetBlobName);
|
||||
try (InputStream actual = blob.getPayload().openStream();
|
||||
InputStream expected = content.openStream()) {
|
||||
assertThat(actual).hasContentEqualTo(expected);
|
||||
}
|
||||
|
||||
blob = regexBlobStore.getBlob(containerName, initialBlobName);
|
||||
try (InputStream actual = blob.getPayload().openStream();
|
||||
InputStream expected = content.openStream()) {
|
||||
assertThat(actual).hasContentEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMatchWithoutReplace() {
|
||||
Properties properties = new Properties();
|
||||
properties.put(
|
||||
String.format("%s.%s.sample1",
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE,
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE_MATCH),
|
||||
"test");
|
||||
properties.put(
|
||||
String.format("%s.%s.sample2",
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE,
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE_MATCH),
|
||||
"test");
|
||||
properties.put(
|
||||
String.format("%s.%s.sample1",
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE,
|
||||
S3ProxyConstants.PROPERTY_REGEX_BLOBSTORE_REPLACE),
|
||||
"test");
|
||||
|
||||
try {
|
||||
RegexBlobStore.parseRegexs(properties);
|
||||
Assertions.failBecauseExceptionWasNotThrown(
|
||||
IllegalArgumentException.class);
|
||||
} catch (IllegalArgumentException exc) {
|
||||
assertThat(exc.getMessage()).isEqualTo(
|
||||
"Regex sample2 has no replace property associated");
|
||||
}
|
||||
}
|
||||
|
||||
private static String createRandomContainerName() {
|
||||
return "container-" + new Random().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
|
@ -35,7 +35,6 @@ import org.jclouds.blobstore.domain.PageSet;
|
|||
import org.jclouds.blobstore.domain.StorageMetadata;
|
||||
import org.jclouds.blobstore.options.CopyOptions;
|
||||
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
|
@ -34,7 +34,6 @@ import com.google.common.io.Resources;
|
|||
import com.google.inject.Module;
|
||||
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.JcloudsVersion;
|
||||
|
|
Ładowanie…
Reference in New Issue