|
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