|
13 | 13 | import com.amazonaws.services.s3.model.EncryptionMaterials;
|
14 | 14 | import com.amazonaws.services.s3.model.EncryptionMaterialsProvider;
|
15 | 15 | import com.amazonaws.services.s3.model.StaticEncryptionMaterialsProvider;
|
| 16 | +import org.apache.commons.io.IOUtils; |
| 17 | +import org.bouncycastle.jce.provider.BouncyCastleProvider; |
16 | 18 | import org.junit.jupiter.api.BeforeAll;
|
17 | 19 | import org.junit.jupiter.api.Test;
|
18 | 20 | import software.amazon.awssdk.core.ResponseBytes;
|
|
29 | 31 | import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
|
30 | 32 | import software.amazon.awssdk.services.s3.model.PutObjectResponse;
|
31 | 33 | import software.amazon.awssdk.services.s3.model.S3Exception;
|
| 34 | +import software.amazon.encryption.s3.utils.BoundedInputStream; |
| 35 | +import software.amazon.encryption.s3.utils.TinyBufferAsyncRequestBody; |
32 | 36 |
|
33 | 37 | import javax.crypto.KeyGenerator;
|
34 | 38 | import javax.crypto.SecretKey;
|
| 39 | +import java.io.IOException; |
| 40 | +import java.io.InputStream; |
35 | 41 | import java.security.NoSuchAlgorithmException;
|
| 42 | +import java.security.Provider; |
| 43 | +import java.security.Security; |
36 | 44 | import java.util.ArrayList;
|
37 | 45 | import java.util.List;
|
38 | 46 | import java.util.concurrent.CompletableFuture;
|
39 | 47 | import java.util.concurrent.CompletionException;
|
| 48 | +import java.util.concurrent.ExecutorService; |
| 49 | +import java.util.concurrent.Executors; |
40 | 50 |
|
41 | 51 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
42 | 52 | import static org.junit.jupiter.api.Assertions.assertEquals;
|
43 | 53 | import static org.junit.jupiter.api.Assertions.assertThrows;
|
| 54 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
44 | 55 | import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.BUCKET;
|
45 | 56 | import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.appendTestSuffix;
|
46 | 57 | import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.deleteObject;
|
@@ -407,4 +418,50 @@ public void copyObjectTransparentlyAsync() {
|
407 | 418 | v3AsyncClient.close();
|
408 | 419 | }
|
409 | 420 |
|
| 421 | + /** |
| 422 | + * Test which artificially limits the size of buffers using {@link TinyBufferAsyncRequestBody}. |
| 423 | + * This tests edge cases where network conditions result in buffers with length shorter than |
| 424 | + * the cipher's block size. |
| 425 | + * @throws IOException |
| 426 | + */ |
| 427 | + @Test |
| 428 | + public void tinyBufferTest() throws IOException { |
| 429 | + // BouncyCastle actually returns null buffers, unlike ACCP and SunJCE, which return empty buffers |
| 430 | + Security.addProvider(new BouncyCastleProvider()); |
| 431 | + Provider provider = Security.getProvider("BC"); |
| 432 | + final String objectKey = appendTestSuffix("tiny-buffer-async"); |
| 433 | + |
| 434 | + S3AsyncClient v3AsyncClient = S3AsyncEncryptionClient.builder() |
| 435 | + .aesKey(AES_KEY) |
| 436 | + .cryptoProvider(provider) |
| 437 | + .build(); |
| 438 | + |
| 439 | + // need enough data to split up |
| 440 | + final long inputLength = 1024; |
| 441 | + final InputStream input = new BoundedInputStream(inputLength); |
| 442 | + final InputStream inputClean = new BoundedInputStream(inputLength); |
| 443 | + |
| 444 | + final ExecutorService exec = Executors.newSingleThreadExecutor(); |
| 445 | + |
| 446 | + // Use this request body to limit the buffer size |
| 447 | + TinyBufferAsyncRequestBody tinyBufferAsyncRequestBody = new TinyBufferAsyncRequestBody(AsyncRequestBody.fromInputStream(input, inputLength, exec)); |
| 448 | + CompletableFuture<PutObjectResponse> futurePut = v3AsyncClient.putObject(builder -> builder |
| 449 | + .bucket(BUCKET) |
| 450 | + .key(objectKey) |
| 451 | + .build(), tinyBufferAsyncRequestBody); |
| 452 | + futurePut.join(); |
| 453 | + |
| 454 | + CompletableFuture<ResponseBytes<GetObjectResponse>> futureGet = v3AsyncClient.getObject(builder -> builder |
| 455 | + .bucket(BUCKET) |
| 456 | + .key(objectKey) |
| 457 | + .build(), AsyncResponseTransformer.toBytes()); |
| 458 | + ResponseBytes<GetObjectResponse> getResponse = futureGet.join(); |
| 459 | + assertTrue(IOUtils.contentEquals(inputClean, getResponse.asInputStream())); |
| 460 | + |
| 461 | + // Cleanup |
| 462 | + deleteObject(BUCKET, objectKey, v3AsyncClient); |
| 463 | + v3AsyncClient.close(); |
| 464 | + exec.shutdown(); |
| 465 | + } |
| 466 | + |
410 | 467 | }
|
0 commit comments