Skip to content

Commit 2c27a30

Browse files
authored
handle STREAMING_AWS4_HMAC_SHA256_PAYLOAD_TRAILER payload. (#180)
1 parent 454b650 commit 2c27a30

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

local-s3-integrationtest/src/test/java/com/robothy/s3/test/PutObjectIntegrationTest.java

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,39 @@
11
package com.robothy.s3.test;
22

33
import static org.junit.jupiter.api.Assertions.*;
4+
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES;
45
import com.robothy.s3.jupiter.LocalS3;
6+
import com.robothy.s3.jupiter.LocalS3Endpoint;
7+
import java.net.URI;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
511
import org.apache.commons.codec.digest.DigestUtils;
612
import org.junit.jupiter.api.Test;
13+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
14+
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
15+
import software.amazon.awssdk.auth.signer.Aws4UnsignedPayloadSigner;
16+
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
17+
import software.amazon.awssdk.auth.signer.S3SignerExecutionAttribute;
718
import software.amazon.awssdk.core.ResponseBytes;
19+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
20+
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
821
import software.amazon.awssdk.core.sync.RequestBody;
22+
import software.amazon.awssdk.http.apache.ApacheHttpClient;
23+
import software.amazon.awssdk.http.auth.aws.crt.internal.signer.AwsChunkedV4aPayloadSigner;
24+
import software.amazon.awssdk.http.auth.aws.crt.internal.signer.V4aPayloadSigner;
25+
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
26+
import software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner;
27+
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
28+
import software.amazon.awssdk.regions.Region;
929
import software.amazon.awssdk.services.s3.S3Client;
30+
import software.amazon.awssdk.services.s3.S3Configuration;
31+
import software.amazon.awssdk.services.s3.internal.handlers.EnableChunkedEncodingInterceptor;
1032
import software.amazon.awssdk.services.s3.model.*;
1133

1234
import java.io.IOException;
1335
import java.util.Map;
36+
import software.amazon.awssdk.utils.AttributeMap;
1437

1538
public class PutObjectIntegrationTest {
1639

@@ -90,4 +113,100 @@ void testPutObjectWithS3Client(S3Client client) throws Exception {
90113
assertEquals(content, gotObjectAsBytes.asUtf8String());
91114
}
92115

116+
117+
@Test
118+
@LocalS3
119+
void test_STREAMING_AWS4_HMAC_SHA256_PAYLOAD_TRAILER(LocalS3Endpoint endpoint) throws Exception {
120+
121+
try (S3Client s3Client = S3Client.builder()
122+
.region(Region.of(endpoint.region()))
123+
.overrideConfiguration(ClientOverrideConfiguration.builder()
124+
.putAdvancedOption(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, true)
125+
.build())
126+
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("foo", "bar")))
127+
.serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build())
128+
.endpointOverride(URI.create(endpoint.endpoint()))
129+
.httpClient(ApacheHttpClient.builder().buildWithDefaults(AttributeMap.builder()
130+
.put(TRUST_ALL_CERTIFICATES, Boolean.TRUE)
131+
.build()))
132+
.build()) {
133+
134+
String bucketName = "my-bucket";
135+
String key = "hello.txt";
136+
byte[] content = new byte[1024 * 1024 * 6];
137+
s3Client.createBucket(b -> b.bucket(bucketName));
138+
s3Client.putObject(b -> b.bucket(bucketName).key(key)
139+
.contentLength((long) content.length)
140+
.contentType("text/plain"),
141+
RequestBody.fromBytes(content));
142+
143+
ResponseBytes<GetObjectResponse> object = s3Client.getObjectAsBytes(b -> b.bucket(bucketName).key(key));
144+
assertArrayEquals(content, object.asByteArray());
145+
}
146+
147+
}
148+
149+
150+
@Test
151+
@LocalS3
152+
void test_STREAMING_AWS4_HMAC_SHA256_PAYLOAD(S3Client s3Client) throws Exception {
153+
String bucketName = "my-bucket";
154+
String key = "hello.txt";
155+
byte[] content = new byte[1024 * 1024 * 6];
156+
s3Client.createBucket(b -> b.bucket(bucketName));
157+
s3Client.putObject(b -> b.bucket(bucketName).key(key)
158+
.overrideConfiguration(c -> {
159+
c.signer(AwsS3V4Signer.create());
160+
c.executionAttributes()
161+
.putAttribute(S3SignerExecutionAttribute.ENABLE_PAYLOAD_SIGNING, true)
162+
.putAttribute(S3SignerExecutionAttribute.ENABLE_CHUNKED_ENCODING, true)
163+
;
164+
c.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("foo", "bar")));
165+
})
166+
.contentLength((long) content.length)
167+
.contentType("text/plain"),
168+
RequestBody.fromBytes(content));
169+
170+
ResponseBytes<GetObjectResponse> object = s3Client.getObjectAsBytes(b -> b.bucket(bucketName).key(key));
171+
assertArrayEquals(content, object.asByteArray());
172+
}
173+
174+
175+
@Test
176+
@LocalS3
177+
void test_UNSIGNED_PAYLOAD(S3Client s3Client) throws Exception {
178+
String bucketName = "my-bucket";
179+
String key = "hello.txt";
180+
byte[] content = new byte[1024 * 1024 * 6];
181+
s3Client.createBucket(b -> b.bucket(bucketName));
182+
s3Client.putObject(b -> b.bucket(bucketName).key(key)
183+
.overrideConfiguration(c -> {
184+
c.signer(Aws4UnsignedPayloadSigner.create());
185+
c.executionAttributes()
186+
.putAttribute(S3SignerExecutionAttribute.ENABLE_PAYLOAD_SIGNING, true)
187+
.putAttribute(S3SignerExecutionAttribute.ENABLE_CHUNKED_ENCODING, true)
188+
;
189+
c.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("foo", "bar")));
190+
})
191+
.contentLength((long) content.length)
192+
.contentType("text/plain"),
193+
RequestBody.fromBytes(content));
194+
195+
ResponseBytes<GetObjectResponse> object = s3Client.getObjectAsBytes(b -> b.bucket(bucketName).key(key));
196+
assertArrayEquals(content, object.asByteArray());
197+
}
198+
199+
//@Test
200+
//@LocalS3
201+
void test_STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD(S3Client s3Client) throws Exception {
202+
// TODO: implement this test
203+
}
204+
205+
// @Test
206+
// @LocalS3
207+
void test_STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD_TAILER(LocalS3Endpoint localS3Endpoint) throws Exception {
208+
// TODO: implement this test
209+
}
210+
211+
93212
}

local-s3-rest/src/main/java/com/robothy/s3/rest/constants/AmzHeaderValues.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ public class AmzHeaderValues {
88
/**
99
* Value of {@linkplain AmzHeaderNames#X_AMZ_CONTENT_SHA256}. This value means
1010
* the payload are encoded with signatures.
11+
* <p>
12+
* <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html">Streaming Signature Version 4</a>
1113
*/
1214
public static final String STREAMING_AWS4_HMAC_SHA_256_PAYLOAD = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
1315

16+
public static final String STREAMING_AWS4_HMAC_SHA256_PAYLOAD_TRAILER = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER";
17+
1418
public static final String STREAMING_UNSIGNED_PAYLOAD_TRAILER = "STREAMING-UNSIGNED-PAYLOAD-TRAILER";
1519

20+
public static final String STREAMING_UNSIGNED_PAYLOAD = "STREAMING-UNSIGNED-PAYLOAD";
21+
22+
public static final String STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD = "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD";
23+
24+
public static final String STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD_TRAILER = "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER";
25+
1626
}

local-s3-rest/src/main/java/com/robothy/s3/rest/utils/RequestUtils.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,23 @@ public class RequestUtils {
2727
public static DecodedAmzRequestBody getBody(HttpRequest request) {
2828
DecodedAmzRequestBody result = new DecodedAmzRequestBody();
2929

30-
String amzContentSha256 = request.header(AmzHeaderNames.X_AMZ_CONTENT_SHA256).orElse("");
30+
String amzContentSha256 = request.header(AmzHeaderNames.X_AMZ_CONTENT_SHA256).orElse("").trim();
3131
switch (amzContentSha256) {
3232
case AmzHeaderValues.STREAMING_AWS4_HMAC_SHA_256_PAYLOAD:
33+
case AmzHeaderValues.STREAMING_AWS4_HMAC_SHA256_PAYLOAD_TRAILER:
3334
result.setDecodedBody(new AwsChunkedDecodingInputStream(new ByteBufInputStream(request.getBody())));
3435
result.setDecodedContentLength(request.header(AmzHeaderNames.X_AMZ_DECODED_CONTENT_LENGTH).map(Long::parseLong)
3536
.orElseThrow(() -> new IllegalArgumentException(AmzHeaderNames.X_AMZ_DECODED_CONTENT_LENGTH + "header not exist.")));
3637
break;
3738
case AmzHeaderValues.STREAMING_UNSIGNED_PAYLOAD_TRAILER:
39+
case AmzHeaderValues.STREAMING_UNSIGNED_PAYLOAD:
3840
result.setDecodedBody(new AwsUnsignedChunkedDecodingInputStream(new ByteBufInputStream(request.getBody())));
3941
result.setDecodedContentLength(request.header(AmzHeaderNames.X_AMZ_DECODED_CONTENT_LENGTH).map(Long::parseLong)
4042
.orElseThrow(() -> new IllegalArgumentException(AmzHeaderNames.X_AMZ_DECODED_CONTENT_LENGTH + "header not exist.")));
4143
break;
44+
case AmzHeaderValues.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD:
45+
case AmzHeaderValues.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD_TRAILER:
46+
throw new UnsupportedOperationException("Unsupported payload encoding: " + amzContentSha256);
4247
default:
4348
result.setDecodedBody(new ByteBufInputStream(request.getBody()));
4449
result.setDecodedContentLength(request.header(HttpHeaderNames.CONTENT_LENGTH.toString()).map(Long::parseLong)

0 commit comments

Comments
 (0)