Parse but do not enforce CRC32

This suffices to allow AWS SDK for Java v2 to work without additional
configuration.  A more complete solution would allow for additional
checksum algorithms and actually enforce the checksums.  Fixes #830.
pull/815/merge
Andrew Gaul 2025-08-27 15:43:41 -07:00
rodzic 6e5fbf5a5f
commit 02fbdc149b
7 zmienionych plików z 107 dodań i 3 usunięć

Wyświetl plik

@ -554,6 +554,11 @@
<version>7.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.30.33</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>

Wyświetl plik

@ -38,6 +38,7 @@ final class AwsHttpHeaders {
static final String REQUEST_ID = "x-amz-request-id";
static final String SDK_CHECKSUM_ALGORITHM = "x-amz-sdk-checksum-algorithm";
static final String STORAGE_CLASS = "x-amz-storage-class";
static final String TRAILER = "x-amz-trailer";
static final String TRANSFER_ENCODING = "x-amz-te";
static final String USER_AGENT = "x-amz-user-agent";

Wyświetl plik

@ -281,6 +281,8 @@ final class AwsSignature {
} else if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals(
xAmzContentSha256)) {
digest = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
} else if ("STREAMING-UNSIGNED-PAYLOAD-TRAILER".equals(xAmzContentSha256)) {
digest = "STREAMING-UNSIGNED-PAYLOAD-TRAILER";
} else if ("UNSIGNED-PAYLOAD".equals(xAmzContentSha256)) {
digest = "UNSIGNED-PAYLOAD";
} else {

Wyświetl plik

@ -49,8 +49,14 @@ final class ChunkedInputStream extends FilterInputStream {
return -1;
}
String[] parts = line.split(";", 2);
currentLength = Integer.parseInt(parts[0], 16);
currentSignature = parts[1];
if (parts[0].startsWith("x-amz-checksum-crc32:")) {
currentLength = 0;
} else {
currentLength = Integer.parseInt(parts[0], 16);
}
if (parts.length > 1) {
currentSignature = parts[1];
}
chunk = new byte[currentLength];
currentIndex = 0;
ByteStreams.readFully(in, chunk);

Wyświetl plik

@ -177,6 +177,7 @@ public class S3ProxyHandler {
AwsHttpHeaders.METADATA_DIRECTIVE,
AwsHttpHeaders.SDK_CHECKSUM_ALGORITHM, // TODO: ignoring header
AwsHttpHeaders.STORAGE_CLASS,
AwsHttpHeaders.TRAILER, // TODO: ignoring header
AwsHttpHeaders.TRANSFER_ENCODING, // TODO: ignoring header
AwsHttpHeaders.USER_AGENT
);
@ -514,6 +515,8 @@ public class S3ProxyHandler {
AwsHttpHeaders.CONTENT_SHA256);
if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals(contentSha256)) {
is = new ChunkedInputStream(is);
} else if ("STREAMING-UNSIGNED-PAYLOAD-TRAILER".equals(contentSha256)) {
is = new ChunkedInputStream(is);
}
} else if (requestIdentity == null) {
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
@ -603,6 +606,9 @@ public class S3ProxyHandler {
contentSha256)) {
payload = new byte[0];
is = new ChunkedInputStream(is);
} else if ("STREAMING-UNSIGNED-PAYLOAD-TRAILER".equals(contentSha256)) {
payload = new byte[0];
is = new ChunkedInputStream(is);
} else if ("UNSIGNED-PAYLOAD".equals(contentSha256)) {
payload = new byte[0];
} else {

Wyświetl plik

@ -0,0 +1,84 @@
/*
* Copyright 2014-2025 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 org.jclouds.blobstore.BlobStoreContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.utils.AttributeMap;
public final class AwsSdk2Test {
private BlobStoreContext context;
private S3Client s3Client;
private String containerName;
@Before
public void setUp() throws Exception {
var info = TestUtils.startS3Proxy(System.getProperty("s3proxy.test.conf", "s3proxy.conf"));
context = info.getBlobStore().getContext();
var attributeMap = AttributeMap.builder()
.put(SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES, true)
.build();
s3Client = S3Client.builder()
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(info.getS3Identity(), info.getS3Credential())))
.region(Region.US_EAST_1)
.endpointOverride(info.getSecureEndpoint())
.httpClient(ApacheHttpClient.builder()
.buildWithDefaults(attributeMap))
.build();
containerName = AwsSdkTest.createRandomContainerName();
info.getBlobStore().createContainerInLocation(null, containerName);
}
@After
public void tearDown() throws Exception {
if (s3Client != null) {
s3Client.close();
}
if (context != null) {
context.getBlobStore().deleteContainer(containerName);
context.close();
}
}
@Test
public void testPutObject() throws Exception {
var key = "testPutObject";
var byteSource = TestUtils.randomByteSource().slice(0, 1024);
var putRequest = PutObjectRequest.builder()
.bucket(containerName)
.key(key)
.build();
s3Client.putObject(putRequest, RequestBody.fromBytes(byteSource.read()));
}
}

Wyświetl plik

@ -1828,7 +1828,7 @@ public final class AwsSdkTest {
}
}
private static String createRandomContainerName() {
static String createRandomContainerName() {
return "s3proxy-" + new Random().nextInt(Integer.MAX_VALUE);
}
}