1
+ /*
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License").
5
+ * You may not use this file except in compliance with the License.
6
+ * A copy of the License is located at
7
+ *
8
+ * http://aws.amazon.com/apache2.0
9
+ *
10
+ * or in the "license" file accompanying this file. This file is distributed
11
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ * express or implied. See the License for the specific language governing
13
+ * permissions and limitations under the License.
14
+ */
15
+
16
+ package software .amazon .awssdk .services .s3 .internal .presignedurl ;
17
+
18
+ import static org .assertj .core .api .Assertions .assertThat ;
19
+ import static org .assertj .core .api .Assertions .assertThatThrownBy ;
20
+ import static org .mockito .ArgumentMatchers .any ;
21
+ import static org .mockito .Mockito .mock ;
22
+ import static org .mockito .Mockito .when ;
23
+ import java .net .URL ;
24
+ import org .junit .jupiter .api .BeforeEach ;
25
+ import org .junit .jupiter .api .Test ;
26
+ import org .junit .jupiter .params .ParameterizedTest ;
27
+ import org .junit .jupiter .params .provider .NullAndEmptySource ;
28
+ import org .junit .jupiter .params .provider .ValueSource ;
29
+ import software .amazon .awssdk .core .exception .SdkClientException ;
30
+ import software .amazon .awssdk .http .SdkHttpFullRequest ;
31
+ import software .amazon .awssdk .http .SdkHttpMethod ;
32
+ import software .amazon .awssdk .protocols .core .OperationInfo ;
33
+ import software .amazon .awssdk .protocols .core .ProtocolMarshaller ;
34
+ import software .amazon .awssdk .protocols .xml .AwsXmlProtocolFactory ;
35
+ import software .amazon .awssdk .services .s3 .internal .presignedurl .model .PresignedUrlGetObjectRequestWrapper ;
36
+
37
+ class PresignedUrlGetObjectRequestMarshallerTest {
38
+
39
+ private PresignedUrlGetObjectRequestMarshaller marshaller ;
40
+ private AwsXmlProtocolFactory mockProtocolFactory ;
41
+ private ProtocolMarshaller <SdkHttpFullRequest > mockProtocolMarshaller ;
42
+ private URL testUrl ;
43
+
44
+ @ BeforeEach
45
+ void setUp () throws Exception {
46
+ mockProtocolFactory = mock (AwsXmlProtocolFactory .class );
47
+ mockProtocolMarshaller = mock (ProtocolMarshaller .class );
48
+ when (mockProtocolFactory .createProtocolMarshaller (any (OperationInfo .class )))
49
+ .thenReturn (mockProtocolMarshaller );
50
+ marshaller = new PresignedUrlGetObjectRequestMarshaller (mockProtocolFactory );
51
+
52
+ testUrl = new URL ("https://test-bucket.s3.us-east-1.amazonaws.com/test-key?" +
53
+ "X-Amz-Date=20231215T000000Z&" +
54
+ "X-Amz-Signature=example-signature&" +
55
+ "X-Amz-Algorithm=AWS4-HMAC-SHA256&" +
56
+ "X-Amz-SignedHeaders=host&" +
57
+ "X-Amz-Security-Token=xxx&" +
58
+ "X-Amz-Credential=EXAMPLE12345678901234%2F20231215%2Fus-east-1%2Fs3%2Faws4_request&" +
59
+ "X-Amz-Expires=3600" );
60
+ }
61
+
62
+ @ Test
63
+ void marshall_withBasicRequest_shouldCreateCorrectHttpRequest () throws Exception {
64
+ // Setup the mock marshaller to return a properly configured request
65
+ SdkHttpFullRequest baseRequest = SdkHttpFullRequest .builder ()
66
+ .method (SdkHttpMethod .GET )
67
+ .protocol ("https" )
68
+ .host ("example.com" )
69
+ .build ();
70
+ when (mockProtocolMarshaller .marshall (any (PresignedUrlGetObjectRequestWrapper .class )))
71
+ .thenReturn (baseRequest );
72
+
73
+ PresignedUrlGetObjectRequestWrapper request = PresignedUrlGetObjectRequestWrapper .builder ()
74
+ .url (testUrl )
75
+ .build ();
76
+ SdkHttpFullRequest result = marshaller .marshall (request );
77
+
78
+ // Verify HTTP method and URI components
79
+ assertThat (result .method ()).isEqualTo (SdkHttpMethod .GET );
80
+ assertThat (result .getUri ())
81
+ .satisfies (uri -> {
82
+ assertThat (uri .getScheme ()).isEqualTo ("https" );
83
+ assertThat (uri .getHost ()).isEqualTo ("test-bucket.s3.us-east-1.amazonaws.com" );
84
+ assertThat (uri .getPath ()).isEqualTo ("/test-key" );
85
+ });
86
+
87
+ // Verify query parameters are preserved
88
+ assertThat (result .getUri ().getQuery ())
89
+ .contains ("X-Amz-Date=20231215T000000Z" )
90
+ .contains ("X-Amz-Signature=example-signature" )
91
+ .contains ("X-Amz-Algorithm=AWS4-HMAC-SHA256" )
92
+ .contains ("X-Amz-SignedHeaders=host" )
93
+ .contains ("X-Amz-Security-Token=xxx" )
94
+ .contains ("X-Amz-Credential=EXAMPLE12345678901234" )
95
+ .contains ("X-Amz-Expires=3600" );
96
+
97
+ assertThat (result .headers ()).doesNotContainKey ("Range" );
98
+ }
99
+
100
+ @ ParameterizedTest
101
+ @ ValueSource (strings = {
102
+ "bytes=0-100" , // First 101 bytes
103
+ "bytes=100-" , // From byte 100 to end
104
+ "bytes=-100" , // Last 100 bytes
105
+ "bytes=0-0" , // Single byte
106
+ "bytes=100-200" // Specific range
107
+ })
108
+ void marshall_withValidRangeFormats_shouldAddRangeHeader (String rangeValue ) throws Exception {
109
+ // Setup the mock marshaller to return a request with the Range header already set
110
+ SdkHttpFullRequest baseRequest = SdkHttpFullRequest .builder ()
111
+ .method (SdkHttpMethod .GET )
112
+ .protocol ("https" )
113
+ .host ("example.com" )
114
+ .putHeader ("Range" , rangeValue ) // Add the Range header to the mock response
115
+ .build ();
116
+
117
+ when (mockProtocolMarshaller .marshall (any (PresignedUrlGetObjectRequestWrapper .class )))
118
+ .thenReturn (baseRequest );
119
+
120
+ PresignedUrlGetObjectRequestWrapper request = PresignedUrlGetObjectRequestWrapper .builder ()
121
+ .url (testUrl )
122
+ .range (rangeValue )
123
+ .build ();
124
+
125
+ SdkHttpFullRequest result = marshaller .marshall (request );
126
+
127
+ // Verify the Range header is preserved
128
+ assertThat (result .headers ())
129
+ .containsKey ("Range" )
130
+ .satisfies (headers -> assertThat (headers .get ("Range" )).contains (rangeValue ));
131
+ }
132
+
133
+ @ ParameterizedTest
134
+ @ NullAndEmptySource
135
+ void marshall_withNullOrEmptyRange_shouldNotAddRangeHeader (String rangeValue ) throws Exception {
136
+ // Setup the mock marshaller to return a properly configured request
137
+ SdkHttpFullRequest baseRequest = SdkHttpFullRequest .builder ()
138
+ .method (SdkHttpMethod .GET )
139
+ .protocol ("https" )
140
+ .host ("example.com" )
141
+ .build ();
142
+ when (mockProtocolMarshaller .marshall (any (PresignedUrlGetObjectRequestWrapper .class )))
143
+ .thenReturn (baseRequest );
144
+
145
+ PresignedUrlGetObjectRequestWrapper request = PresignedUrlGetObjectRequestWrapper .builder ()
146
+ .url (testUrl )
147
+ .range (rangeValue )
148
+ .build ();
149
+ SdkHttpFullRequest result = marshaller .marshall (request );
150
+
151
+ assertThat (result .headers ()).doesNotContainKey ("Range" );
152
+ }
153
+
154
+ @ Test
155
+ void marshall_withNullRequest_shouldThrowException () {
156
+ assertThatThrownBy (() -> marshaller .marshall (null ))
157
+ .isInstanceOf (NullPointerException .class )
158
+ .hasMessageContaining ("presignedUrlGetObjectRequestWrapper must not be null" );
159
+ }
160
+
161
+ @ Test
162
+ void marshall_withMalformedUrl_shouldThrowSdkClientException () throws Exception {
163
+ // Setup the mock marshaller to return a properly configured request
164
+ SdkHttpFullRequest baseRequest = SdkHttpFullRequest .builder ()
165
+ .method (SdkHttpMethod .GET )
166
+ .protocol ("https" )
167
+ .host ("example.com" )
168
+ .build ();
169
+ when (mockProtocolMarshaller .marshall (any (PresignedUrlGetObjectRequestWrapper .class )))
170
+ .thenReturn (baseRequest );
171
+
172
+ URL malformedUrl = new URL ("https" , "test-bucket.s3.us-east-1.amazonaws.com" , -1 , "/test key with spaces" );
173
+ PresignedUrlGetObjectRequestWrapper request = PresignedUrlGetObjectRequestWrapper .builder ()
174
+ .url (malformedUrl )
175
+ .build ();
176
+
177
+ assertThatThrownBy (() -> marshaller .marshall (request ))
178
+ .isInstanceOf (SdkClientException .class )
179
+ .hasMessageContaining ("Unable to marshall pre-signed URL Request" );
180
+ }
181
+ }
0 commit comments