Skip to content

Commit da81d8b

Browse files
#494 - Set instruction file content length
The instruction files content length must be set correctly in the PutObjectRequest. This overwrites any previously set values and always uses the correct byte length for the content of the instruction file.
1 parent 7e3c89a commit da81d8b

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

src/main/java/software/amazon/encryption/s3/internal/InstructionFileConfig.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ PutObjectResponse putInstructionFile(PutObjectRequest request, String instructio
6565
instFileMetadata.put(INSTRUCTION_FILE, "");
6666

6767
// Use toBuilder to keep all other fields the same as the actual request
68+
// but set the content length, key, and metadata appropriately for the instruction file
6869
final PutObjectRequest instPutRequest = request.toBuilder()
69-
.key(request.key() + instructionFileSuffix)
70-
.metadata(instFileMetadata)
71-
.build();
70+
.key(request.key() + instructionFileSuffix)
71+
.contentLength((long) instructionFileContent.getBytes().length)
72+
.metadata(instFileMetadata)
73+
.build();
7274
switch (_clientType) {
7375
case SYNCHRONOUS:
7476
return _s3Client.putObject(instPutRequest, RequestBody.fromString(instructionFileContent));
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package software.amazon.encryption.s3.internal;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.mockito.ArgumentCaptor;
5+
import software.amazon.awssdk.core.async.AsyncRequestBody;
6+
import software.amazon.awssdk.core.sync.RequestBody;
7+
import software.amazon.awssdk.services.s3.S3AsyncClient;
8+
import software.amazon.awssdk.services.s3.S3Client;
9+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
10+
11+
import java.util.concurrent.CompletableFuture;
12+
13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.mockito.ArgumentMatchers.any;
15+
import static org.mockito.Mockito.*;
16+
17+
class InstructionFileConfigUploadTest {
18+
19+
@Test
20+
void uploadInstructionFileWithSetContentLengthSyncClient() {
21+
// Create a mock for the S3 client
22+
S3Client mockedS3Client = mock(S3Client.class);
23+
// The argument captor is used to capture the PutObjectRequest passed to the putObject method
24+
ArgumentCaptor<PutObjectRequest> instructionFilePutCaptor = ArgumentCaptor.forClass(PutObjectRequest.class);
25+
26+
// Create the InstructionFileConfig with the mocked S3 client
27+
InstructionFileConfig instructionFileConfig = InstructionFileConfig.builder()
28+
.instructionFileClient(mockedS3Client)
29+
.enableInstructionFilePutObject(true)
30+
.build();
31+
32+
// Build some data for the test
33+
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
34+
.key("someKey").build();
35+
String instructionFileContent = "some content that fakes an instruction file";
36+
37+
// call the actual method under test
38+
instructionFileConfig.putInstructionFile(putObjectRequest, instructionFileContent);
39+
40+
// Verify that the putObject method was called and the captured request has the correct content length
41+
verify(mockedS3Client).putObject(instructionFilePutCaptor.capture(), any(RequestBody.class));
42+
assertEquals(instructionFileContent.getBytes().length, instructionFilePutCaptor.getValue().contentLength());
43+
}
44+
45+
@Test
46+
void uploadInstructionFileWithSetContentLengthAsyncClient() {
47+
// Create a mock for the S3 client
48+
S3AsyncClient mockedS3Client = mock(S3AsyncClient.class);
49+
// The async putObject method returns a CompletableFuture, so we need to mock that behavior
50+
when(mockedS3Client.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)))
51+
.thenReturn(CompletableFuture.completedFuture(null));
52+
// The argument captor is used to capture the PutObjectRequest passed to the putObject method
53+
ArgumentCaptor<PutObjectRequest> instructionFilePutCaptor = ArgumentCaptor.forClass(PutObjectRequest.class);
54+
55+
// Create the InstructionFileConfig with the mocked S3 async client
56+
InstructionFileConfig instructionFileConfig = InstructionFileConfig.builder()
57+
.instructionFileAsyncClient(mockedS3Client)
58+
.enableInstructionFilePutObject(true)
59+
.build();
60+
61+
// Build some data for the test
62+
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
63+
.key("someKey").build();
64+
String instructionFileContent = "some content that fakes an instruction file";
65+
66+
// call the actual method under test
67+
instructionFileConfig.putInstructionFile(putObjectRequest, instructionFileContent);
68+
69+
// Verify that the putObject method was called and the captured request has the correct content length
70+
verify(mockedS3Client).putObject(instructionFilePutCaptor.capture(), any(AsyncRequestBody.class));
71+
assertEquals(instructionFileContent.getBytes().length, instructionFilePutCaptor.getValue().contentLength());
72+
}
73+
}

0 commit comments

Comments
 (0)