1 package com.github.davidmoten.aws.lw.client;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertFalse;
5 import static org.junit.Assert.assertTrue;
6
7 import java.io.ByteArrayInputStream;
8 import java.io.IOException;
9 import java.io.UncheckedIOException;
10 import java.nio.charset.StandardCharsets;
11 import java.util.Arrays;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import java.util.Optional;
17 import java.util.concurrent.TimeUnit;
18
19 import org.junit.Assert;
20 import org.junit.Test;
21
22 import com.github.davidmoten.http.test.server.Server;
23
24 public class ClientTest {
25
26 private static final HttpClientTesting hc = HttpClientTesting.INSTANCE;
27
28 private static final Client s3 = Client
29 .s3()
30 .region("ap-southeast-2")
31 .accessKey("123")
32 .secretKey("456")
33 .httpClient(hc)
34 .build();
35
36 @Test
37 public void test() {
38 Client client = Client
39 .s3()
40 .region("us-west-1")
41 .accessKey("123")
42 .secretKey("456")
43 .httpClient(hc)
44 .build();
45
46 client
47 .path("MyBucket")
48 .metadata("category", "something")
49 .query("type", "thing")
50 .attribute("color", "red")
51 .attribute("color", "blue")
52 .attributePrefix("Message")
53 .attribute("name", "hi")
54 .attribute("name", "there")
55 .method(HttpMethod.PUT)
56 .requestBody("hi there")
57 .region("ap-southeast-2")
58 .connectTimeout(5, TimeUnit.SECONDS)
59 .readTimeout(6, TimeUnit.SECONDS)
60 .retryMaxAttempts(1)
61 .retryBackoffFactor(1.0)
62 .retryInitialInterval(10, TimeUnit.MILLISECONDS)
63 .retryMaxInterval(1, TimeUnit.SECONDS)
64 .retryJitter(0)
65 .execute();
66 assertEquals(
67 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?type=thing&Attribute.1.Name=color&Attribute.1.Value=red&Attribute.2.Name=color&Attribute.2.Value=blue&Message.1.Name=name&Message.1.Value=hi&Message.2.Name=name&Message.2.Value=there",
68 hc.endpointUrl.toString());
69 assertEquals("PUT", hc.httpMethod);
70 assertEquals("9b96a1fe1d548cbbc960cc6a0286668fd74a763667b06366fb2324269fcabaa4",
71 hc.headers.get("x-amz-content-sha256"));
72 String authorization = hc.headers.get("Authorization");
73 assertTrue(authorization.startsWith("AWS4-HMAC-SHA256 Credential="));
74 assertTrue(authorization.contains(
75 "/ap-southeast-2/s3/aws4_request, SignedHeaders=content-length;host;x-amz-content-sha256;x-amz-date;x-amz-meta-category"));
76 assertEquals("8", hc.headers.get("content-length"));
77 assertEquals("s3.ap-southeast-2.amazonaws.com", hc.headers.get("Host"));
78 assertTrue(hc.headers.get("x-amz-date").endsWith("Z"));
79 assertEquals("something", hc.headers.get("x-amz-meta-category"));
80 assertEquals("hi there", hc.requestBodyString());
81 assertEquals(5000, hc.connectTimeoutMs);
82 assertEquals(6000, hc.readTimeoutMs);
83 }
84
85 @Test
86 public void testQueryParameterWithoutValue() {
87 Client client = Client
88 .s3()
89 .region("us-west-1")
90 .accessKey("123")
91 .secretKey("456")
92 .httpClient(hc)
93 .build();
94 client
95 .path("mybucket", "myobject")
96 .query("uploads")
97 .method(HttpMethod.POST)
98 .execute();
99
100 assertEquals("https://s3.us-west-1.amazonaws.com/mybucket/myobject?uploads",
101 hc.endpointUrl.toString());
102 assertEquals("POST", hc.httpMethod);
103 assertEquals("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
104 hc.headers.get("x-amz-content-sha256"));
105 String authorization = hc.headers.get("Authorization");
106 assertTrue(authorization.startsWith("AWS4-HMAC-SHA256 Credential="));
107 assertTrue(authorization.contains(
108 "/us-west-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date"));
109 assertEquals("s3.us-west-1.amazonaws.com", hc.headers.get("Host"));
110 assertTrue(hc.headers.get("x-amz-date").endsWith("Z"));
111 }
112
113 @Test
114 public void testUnsignedPayload() {
115 Client client = Client
116 .s3()
117 .region("us-west-1")
118 .accessKey("123")
119 .secretKey("456")
120 .httpClient(hc)
121 .build();
122
123 client
124 .path("MyBucket")
125 .metadata("category", "something")
126 .query("type", "thing")
127 .attribute("color", "red")
128 .attribute("color", "blue")
129 .attributePrefix("Message")
130 .attribute("name", "hi")
131 .attribute("name", "there")
132 .method(HttpMethod.PUT)
133 .requestBody("hi there")
134 .unsignedPayload()
135 .region("ap-southeast-2")
136 .connectTimeout(5, TimeUnit.SECONDS)
137 .readTimeout(6, TimeUnit.SECONDS)
138 .execute();
139 assertEquals(
140 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?type=thing&Attribute.1.Name=color&Attribute.1.Value=red&Attribute.2.Name=color&Attribute.2.Value=blue&Message.1.Name=name&Message.1.Value=hi&Message.2.Name=name&Message.2.Value=there",
141 hc.endpointUrl.toString());
142 assertEquals("PUT", hc.httpMethod);
143 assertEquals("UNSIGNED-PAYLOAD", hc.headers.get("x-amz-content-sha256"));
144 String authorization = hc.headers.get("Authorization");
145 assertTrue(authorization.startsWith("AWS4-HMAC-SHA256 Credential="));
146 assertTrue(authorization.contains(
147 "/ap-southeast-2/s3/aws4_request, SignedHeaders=content-length;host;x-amz-content-sha256;x-amz-date;x-amz-meta-category"));
148 assertEquals("8", hc.headers.get("content-length"));
149 assertEquals("s3.ap-southeast-2.amazonaws.com", hc.headers.get("Host"));
150 assertTrue(hc.headers.get("x-amz-date").endsWith("Z"));
151 assertEquals("something", hc.headers.get("x-amz-meta-category"));
152 assertEquals("hi there", hc.requestBodyString());
153 assertEquals(5000, hc.connectTimeoutMs);
154 assertEquals(6000, hc.readTimeoutMs);
155 }
156
157 @Test
158 public void testRegionNoneUsesUsEast1InSignature() {
159 Client client = Client
160 .iam()
161 .regionNone()
162 .accessKey("123")
163 .secretKey("456")
164 .httpClient(hc)
165 .build();
166
167 client
168 .query("Action", "GetUser")
169 .query("Version", "2010-05-08")
170 .execute();
171 assertEquals(
172 "https://iam.amazonaws.com/?Action=GetUser&Version=2010-05-08",
173 hc.endpointUrl.toString());
174 String authorization = hc.headers.get("Authorization");
175 assertTrue(authorization.contains("/us-east-1/iam/aws4_request"));
176 assertEquals("iam.amazonaws.com", hc.headers.get("Host"));
177 }
178
179 @Test(expected = IllegalArgumentException.class)
180 public void testBadConnectTimeout() {
181 Client
182 .s3()
183 .region("ap-southeast-2")
184 .accessKey("123")
185 .secretKey("456")
186 .connectTimeout(-1, TimeUnit.SECONDS);
187 }
188
189 @Test(expected = IllegalArgumentException.class)
190 public void testBadReadTimeout() {
191 Client
192 .s3()
193 .region("ap-southeast-2")
194 .accessKey("123")
195 .secretKey("456")
196 .readTimeout(-1, TimeUnit.SECONDS);
197 }
198
199 @Test(expected = IllegalArgumentException.class)
200 public void testBadConnectTimeout2() {
201 s3.path().connectTimeout(-1, TimeUnit.SECONDS);
202 }
203
204 @Test(expected = IllegalArgumentException.class)
205 public void testBadReadTimeout2() {
206 s3.path().readTimeout(-1, TimeUnit.SECONDS);
207 }
208
209 @Test(expected = IllegalArgumentException.class)
210 public void testBadRetryInitialInterval() {
211 s3.path().retryInitialInterval(-1, TimeUnit.SECONDS);
212 }
213
214 @Test(expected = IllegalArgumentException.class)
215 public void testBadRetryMaxInterval() {
216 s3.path().retryMaxInterval(-1, TimeUnit.SECONDS);
217 }
218
219 @Test(expected = IllegalArgumentException.class)
220 public void testBadRetryMaxAttempts() {
221 s3.path().retryMaxAttempts(-1);
222 }
223
224 @Test(expected = IllegalArgumentException.class)
225 public void testBadRetryJitter() {
226 s3.path().retryJitter(-1);
227 }
228
229 @Test(expected = IllegalArgumentException.class)
230 public void testBadRetryBackoffFactor() {
231 s3.path().retryBackoffFactor(-1);
232 }
233
234 @Test
235 public void testTimeoutsAtClientLevel() {
236 Client client = Client
237 .s3()
238 .region("ap-southeast-2")
239 .accessKey("123")
240 .secretKey("456")
241 .connectTimeout(5, TimeUnit.SECONDS)
242 .readTimeout(6, TimeUnit.SECONDS)
243 .httpClient(hc)
244 .build();
245
246 client
247 .path("MyBucket")
248 .method(HttpMethod.PUT)
249 .requestBody("hi there")
250 .execute();
251
252 assertEquals(5000, hc.connectTimeoutMs);
253 assertEquals(6000, hc.readTimeoutMs);
254 }
255
256 @Test(expected = MaxAttemptsExceededException.class)
257 public void testThrows() {
258 Client client = Client
259 .s3()
260 .region("ap-southeast-2")
261 .accessKey("123")
262 .secretKey("456")
263 .connectTimeout(5, TimeUnit.SECONDS)
264 .readTimeout(6, TimeUnit.SECONDS)
265 .httpClient(HttpClientTesting.THROWING)
266 .retryMaxAttempts(1)
267 .build();
268
269
270 client
271 .path("MyBucket")
272 .method(HttpMethod.PUT)
273 .requestBody("hi there")
274 .execute();
275 }
276
277 @Test
278 public void testDefaultClientFromEnvironment() {
279 Map<String, String> map = new HashMap<>();
280 map.put("AWS_REGION", "ap-southeast-2");
281 map.put("AWS_ACCESS_KEY_ID", "123");
282 map.put("AWS_SECRET_ACCESS_KEY", "abc");
283 Client client = Client.s3().environment(name -> map.get(name)).defaultClient().build();
284 assertEquals("ap-southeast-2", client.region().get());
285 Credentials c = client.credentials();
286 assertEquals("123", c.accessKey());
287 assertEquals("abc", c.secretKey());
288 assertFalse(c.sessionToken().isPresent());
289 }
290
291 @Test
292 public void testDefaultClientFromSystemProperties() {
293 System.setProperty("aws.accessKeyId", "123");
294 System.setProperty("aws.secretKey", "abc");
295 Client client = Client.s3().region("ap-southeast-2").credentialsFromSystemProperties()
296 .build();
297 assertEquals("ap-southeast-2", client.region().get());
298 Credentials c = client.credentials();
299 assertEquals("123", c.accessKey());
300 assertEquals("abc", c.secretKey());
301 assertFalse(c.sessionToken().isPresent());
302 }
303
304 @Test
305 public void testNoPathOrUrlSet() {
306 s3.query("number", "four")
307 .method(HttpMethod.PUT)
308 .execute();
309 assertEquals("https://s3.ap-southeast-2.amazonaws.com/?number=four",
310 hc.endpointUrl.toString());
311 }
312
313 @Test
314 public void testUrlSet() {
315 s3.url("https://blah") //
316 .method(HttpMethod.PUT)
317 .execute();
318 assertEquals("https://blah", hc.endpointUrl.toString());
319 }
320
321 @Test
322 public void testAttribute() {
323 s3.attribute("colour", "blue")
324 .attribute("color", "green")
325 .method(HttpMethod.PUT)
326 .execute();
327 assertEquals(
328 "https://s3.ap-southeast-2.amazonaws.com/?Attribute.1.Name=colour&Attribute.1.Value=blue&Attribute.2.Name=color&Attribute.2.Value=green",
329 hc.endpointUrl.toString());
330 }
331
332 @Test
333 public void testAttributePrefix() {
334 s3.attribute("colour", "blue")
335 .attribute("color", "green")
336 .attributePrefix("surface")
337 .attribute("texture", "rough")
338 .method(HttpMethod.PUT)
339 .execute();
340 assertEquals(
341 "https://s3.ap-southeast-2.amazonaws.com/?Attribute.1.Name=colour&Attribute.1.Value=blue&Attribute.2.Name=color&Attribute.2.Value=green&surface.1.Name=texture&surface.1.Value=rough",
342 hc.endpointUrl.toString());
343 }
344
345 @Test
346 public void testAttributePrefix2() {
347 s3.attributePrefix("surface")
348 .attribute("color", "green")
349 .method(HttpMethod.PUT)
350 .execute();
351 assertEquals(
352 "https://s3.ap-southeast-2.amazonaws.com/?surface.1.Name=color&surface.1.Value=green",
353 hc.endpointUrl.toString());
354 }
355
356 @Test
357 public void testServerOkResponse2() throws InterruptedException {
358 for (int i = 0; i < 10; i++) {
359 Client client = Client
360 .s3()
361 .region("ap-southeast-2")
362 .accessKey("123")
363 .secretKey("456")
364 .clock(() -> 1622695846902L)
365 .connectTimeout(10, TimeUnit.SECONDS)
366 .readTimeout(10, TimeUnit.SECONDS)
367 .retryMaxAttempts(1)
368 .build();
369 try (Server server = Server.start()) {
370 server.response().body("<a>hello</a>").add();
371 String text = client
372 .url(server.baseUrl())
373 .requestBody("hi there")
374 .responseAsXml()
375 .content();
376 assertEquals("hello", text);
377 }
378 }
379 }
380
381 @Test
382 public void testServerErrorResponse() throws IOException {
383 Client client = Client
384 .s3()
385 .region("ap-southeast-2")
386 .accessKey("123")
387 .secretKey("456")
388 .clock(() -> 1622695846902L)
389 .retryMaxAttempts(1)
390 .build();
391 try (Server server = Server.start()) {
392 server.response().body("hello").statusCode(500).add();
393 try {
394 client.url(server.baseUrl())
395 .requestBody("hi there")
396 .responseAsUtf8();
397 Assert.fail();
398 } catch (ServiceException e) {
399 assertEquals(500, e.statusCode());
400 assertEquals("hello", e.message());
401 }
402 }
403 }
404
405 @Test
406 public void testResponseExists() throws IOException {
407 Client client = Client
408 .s3()
409 .region("ap-southeast-2")
410 .accessKey("123")
411 .secretKey("456")
412 .clock(() -> 1622695846902L)
413 .build();
414 try (Server server = Server.start()) {
415 server.response().body("hello").statusCode(404).add();
416 assertFalse(client.url(server.baseUrl()).exists());
417 }
418 }
419
420 @Test
421 public void testServerErrorCustomExceptions() throws IOException {
422 Client client = Client
423 .s3()
424 .region("ap-southeast-2")
425 .accessKey("123")
426 .secretKey("456")
427 .clock(() -> 1622695846902L)
428 .exception(r -> !r.isOk(), r -> new UnsupportedOperationException())
429 .retryMaxAttempts(1)
430 .build();
431 try (Server server = Server.start()) {
432 server.response().body("hello").statusCode(500).add();
433 try {
434 client.url(server.baseUrl())
435 .requestBody("hi there")
436 .responseAsUtf8();
437 Assert.fail();
438 } catch (UnsupportedOperationException e) {
439
440 }
441 }
442 }
443
444 @Test
445 public void testServerErrorCustomExceptionsPassThrough() throws IOException {
446 Client client = Client
447 .s3()
448 .region("ap-southeast-2")
449 .accessKey("123")
450 .secretKey("456")
451 .clock(() -> 1622695846902L)
452 .exception(r -> !r.isOk() && r.statusCode() == 404,
453 r -> new UnsupportedOperationException())
454 .retryMaxAttempts(1)
455 .build();
456 try (Server server = Server.start()) {
457 server.response().body("hello").statusCode(500).add();
458 try {
459 client.url(server.baseUrl())
460 .requestBody("hi there")
461 .responseAsUtf8();
462 Assert.fail();
463 } catch (ServiceException e) {
464
465 }
466 }
467 }
468
469 @Test
470 public void testServerErrorExceptionFactory() throws IOException {
471 Client client = Client
472 .s3()
473 .region("ap-southeast-2")
474 .accessKey("123")
475 .secretKey("456")
476 .clock(() -> 1622695846902L)
477 .exceptionFactory(r -> {
478 if (r.isOk()) {
479 return Optional.empty();
480 } else {
481 return Optional.of(new UnsupportedOperationException());
482 }
483 })
484 .retryMaxAttempts(1)
485 .build();
486 try (Server server = Server.start()) {
487 server.response().body("hello").statusCode(500).add();
488 try {
489 client.url(server.baseUrl())
490 .requestBody("hi there")
491 .responseAsUtf8();
492 Assert.fail();
493 } catch (UnsupportedOperationException e) {
494
495 }
496 }
497 }
498
499 @Test
500 public void testWithServerNoResponseBody() throws IOException {
501 try {
502 Client client = Client
503 .s3()
504 .region("ap-southeast-2")
505 .accessKey("123")
506 .secretKey("456")
507 .clock(() -> 1622695846902L)
508 .build();
509 try (Server server = Server.start()) {
510 server.response().statusCode(200).add();
511 String text = client.url(server.baseUrl())
512 .method(HttpMethod.PUT)
513 .requestBody("hi there")
514 .responseAsUtf8();
515 assertEquals("", text);
516 }
517 } catch (Throwable t) {
518 t.printStackTrace();
519 throw t;
520 }
521 }
522
523 @Test
524 public void testPresignedUrlWithRequestBody() {
525 Client client = Client
526 .s3()
527 .region("us-west-1")
528 .accessKey("123")
529 .secretKey("456")
530 .clock(() -> 1622695846902L)
531 .build();
532
533 String presignedUrl = client
534 .path("MyBucket")
535 .query("type", "thing")
536 .method(HttpMethod.PUT)
537 .requestBody("hi there")
538 .region("ap-southeast-2")
539 .presignedUrl(5, TimeUnit.DAYS);
540 assertEquals(
541 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?type=thing&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=123/20210603/ap-southeast-2/s3/aws4_request&X-Amz-Date=20210603T045046Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=content-length;host;x-amz-content-sha256&X-Amz-Signature=3f27d3fe5e595d787990866d05112cd73e21be2275bf02269b640bc9b7c35ec6",
542 presignedUrl);
543 }
544
545 @Test
546 public void testPresignedUrlWithRequestBodyUnsignedPayload() {
547 Client client = Client
548 .s3()
549 .region("us-west-1")
550 .accessKey("123")
551 .secretKey("456")
552 .clock(() -> 1622695846902L)
553 .build();
554
555 String presignedUrl = client
556 .path("MyBucket")
557 .query("type", "thing")
558 .method(HttpMethod.PUT)
559 .requestBody("hi there")
560 .region("ap-southeast-2")
561 .unsignedPayload()
562 .presignedUrl(5, TimeUnit.DAYS);
563 assertEquals(
564 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?type=thing&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=123/20210603/ap-southeast-2/s3/aws4_request&X-Amz-Date=20210603T045046Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host;x-amz-content-sha256&X-Amz-Signature=7cf6d7fe3bab3b9f23c08aec974bd8007a1c93d8e5009d4d77d0b742f3a3dcb2",
565 presignedUrl);
566 }
567
568 @Test
569 public void testPresignedUrlWithoutRequestBody() {
570 Client client = Client
571 .s3()
572 .region("us-west-1")
573 .accessKey("123")
574 .secretKey("456")
575 .clock(() -> 1622695846902L)
576 .build();
577
578 String presignedUrl = client
579 .path("MyBucket")
580 .query("type", "thing")
581 .method(HttpMethod.PUT)
582 .region("ap-southeast-2")
583 .presignedUrl(5, TimeUnit.DAYS);
584 assertEquals(
585 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?type=thing&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=123/20210603/ap-southeast-2/s3/aws4_request&X-Amz-Date=20210603T045046Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host;x-amz-content-sha256&X-Amz-Signature=b14df0b38e6a1dadce2f340483bd61db69730da26d571e1f0cfacea993372085",
586 presignedUrl);
587 }
588
589 @Test
590 public void testPresignedUrlWithoutRequestBodyWithSessionToken() {
591 Client client = Client
592 .s3()
593 .region("us-west-1")
594 .credentials(Credentials.of("123", "456", "abc"))
595 .clock(() -> 1622695846902L)
596 .build();
597
598 String presignedUrl = client
599 .path("MyBucket")
600 .query("type", "thing")
601 .method(HttpMethod.PUT)
602 .region("ap-southeast-2")
603 .presignedUrl(5, TimeUnit.DAYS);
604 assertEquals(
605 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?type=thing&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=123/20210603/ap-southeast-2/s3/aws4_request&X-Amz-Date=20210603T045046Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host;x-amz-content-sha256&X-Amz-Signature=93dd388b414d719042afedc24393baeaf1d7d37bfe6394f125a26aa2b29d3426&X-Amz-Security-Token=abc",
606 presignedUrl);
607 }
608
609 @Test
610 public void testPresignedUrlWhenUrlHasPort() {
611 Client client = Client
612 .s3()
613 .region("us-west-1")
614 .accessKey("123")
615 .secretKey("456")
616 .clock(() -> 1622695846902L)
617 .build();
618
619 String presignedUrl = client
620 .url("https://s3.myserver.com:8443") //
621 .method(HttpMethod.PUT)
622 .region("ap-southeast-2")
623 .presignedUrl(5, TimeUnit.DAYS);
624 assertEquals(
625 "https://s3.myserver.com:8443?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=123/20210603/ap-southeast-2/s3/aws4_request&X-Amz-Date=20210603T045046Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host;x-amz-content-sha256&X-Amz-Signature=e78feecf8da1d5c8029f117bb8bd10779d420ce02e838461e3abfafe7d565a5c",
626 presignedUrl);
627 }
628
629 @Test
630 public void testPresignedUrlWithoutQueryParameters() {
631 Client client = Client
632 .s3()
633 .region("us-west-1")
634 .accessKey("123")
635 .secretKey("456")
636 .clock(() -> 1622695846902L)
637 .build();
638
639 String presignedUrl = client
640 .path("MyBucket")
641 .method(HttpMethod.PUT)
642 .region("ap-southeast-2")
643 .presignedUrl(5, TimeUnit.DAYS);
644 assertEquals(
645 "https://s3.ap-southeast-2.amazonaws.com/MyBucket?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=123/20210603/ap-southeast-2/s3/aws4_request&X-Amz-Date=20210603T045046Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host;x-amz-content-sha256&X-Amz-Signature=4ffc22f4b86b0514a29994c92bbf0342e2dccc66cdceae670414c847baa338ef",
646 presignedUrl);
647 }
648
649 @Test
650 public void testAuthorizationSignedRequest() {
651 Client s3 = Client
652 .s3()
653 .region("ap-southeast-2")
654 .credentials(Credentials.of("123", "456", "789")).clock(() -> 1622695846902L)
655 .httpClient(hc)
656 .build();
657
658 s3
659 .path("myBucket/myObject.txt")
660 .query("Type", "Thing")
661 .header("my-header", "blah")
662 .header("my-header", "blah2")
663 .requestBody("something")
664 .response();
665
666 assertEquals("GET", hc.httpMethod);
667 assertEquals("something", hc.requestBodyString());
668 assertEquals("https://s3.ap-southeast-2.amazonaws.com/myBucket/myObject.txt?Type=Thing",
669 hc.endpointUrl.toString());
670 Map<String, String> a = new HashMap<>();
671 a.put("my-header", "blah,blah2");
672 a.put("x-amz-content-sha256",
673 "3fc9b689459d738f8c88a3a48aa9e33542016b7a4052e001aaa536fca74813cb");
674 a.put("Authorization",
675 "AWS4-HMAC-SHA256 Credential=123/20210603/ap-southeast-2/s3/aws4_request, SignedHeaders=content-length;host;my-header;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=72983b3d44575f7b8fea5dd7148a764a7031122154387a30764c56d171906c80");
676 a.put("Host", "s3.ap-southeast-2.amazonaws.com");
677 a.put("x-amz-date", "20210603T045046Z");
678 a.put("content-length", "" + 9);
679 a.put("x-amz-security-token", "789");
680 for (Entry<String, String> entry : hc.headers.entrySet()) {
681 assertEquals(a.get(entry.getKey()), entry.getValue());
682 }
683 assertEquals(a.size(), hc.headers.size());
684 }
685
686 @Test(expected = MaxAttemptsExceededException.class)
687 public void testUrlDoesNotExist() {
688 Client s3 = Client.s3().region("ap-southeast-2").accessKey("123").secretKey("456").retryMaxAttempts(1).build();
689 s3.url("https://doesnotexist.z21894649.com").execute();
690 }
691
692 @Test
693 public void testOtherServiceNames() {
694 Client s3 = Client.s3().region("ap-southeast-2").accessKey("123").secretKey("abc").build();
695 assertEquals("iam", Client.iam().from(s3).build().serviceName());
696 assertEquals("ec2", Client.ec2().from(s3).build().serviceName());
697 assertEquals("lambda", Client.lambda().from(s3).build().serviceName());
698 assertEquals("s3", Client.s3().from(s3).build().serviceName());
699 assertEquals("sns", Client.sns().from(s3).build().serviceName());
700 assertEquals("sqs", Client.sqs().from(s3).build().serviceName());
701 assertEquals("hi", Client.service("hi").from(s3).build().serviceName());
702 }
703
704 @Test
705 public void testRetriesFailTwiceThenSucceed() {
706 Client client = Client
707 .s3()
708 .region("ap-southeast-2")
709 .accessKey("123")
710 .secretKey("456")
711 .clock(() -> 1622695846902L)
712 .retryInitialInterval(100, TimeUnit.MILLISECONDS)
713 .build();
714 try (Server server = Server.start()) {
715 server.response().statusCode(408).body("timed out").add();
716 server.response().statusCode(408).body("timed out").add();
717 server.response().statusCode(200).body("stuff").add();
718 String text = client.url(server.baseUrl())
719 .method(HttpMethod.PUT)
720 .requestBody("hi there")
721 .responseAsUtf8();
722 assertEquals("stuff", text);
723 }
724 }
725
726 @Test(expected=IllegalArgumentException.class)
727 public void testNegativeRetryInitialInterval() {
728 Client
729 .s3()
730 .region("ap-southeast-2")
731 .accessKey("123")
732 .secretKey("456")
733 .clock(() -> 1622695846902L)
734 .retryInitialInterval(-1, TimeUnit.MILLISECONDS);
735 }
736
737 @Test(expected=IllegalArgumentException.class)
738 public void testNegativeRetryMaxInterval() {
739 Client
740 .s3()
741 .region("ap-southeast-2")
742 .accessKey("123")
743 .secretKey("456")
744 .clock(() -> 1622695846902L)
745 .retryMaxInterval(-1, TimeUnit.MILLISECONDS);
746 }
747
748 @Test(expected=IllegalArgumentException.class)
749 public void testNegativeRetryJitter() {
750 Client
751 .s3()
752 .region("ap-southeast-2")
753 .accessKey("123")
754 .secretKey("456")
755 .clock(() -> 1622695846902L)
756 .retryJitter(-1);
757 }
758
759 @Test(expected=IllegalArgumentException.class)
760 public void testTooLargeRetryJitter() {
761 Client
762 .s3()
763 .region("ap-southeast-2")
764 .accessKey("123")
765 .secretKey("456")
766 .clock(() -> 1622695846902L)
767 .retryJitter(2);
768 }
769
770 @Test(expected=IllegalArgumentException.class)
771 public void testNegativeRetryMaxAttempts() {
772 Client
773 .s3()
774 .region("ap-southeast-2")
775 .accessKey("123")
776 .secretKey("456")
777 .clock(() -> 1622695846902L)
778 .retryMaxAttempts(-1);
779 }
780
781 @Test(expected=IllegalArgumentException.class)
782 public void testNegativeRetryBackoffFactor() {
783 Client
784 .s3()
785 .region("ap-southeast-2")
786 .accessKey("123")
787 .secretKey("456")
788 .clock(() -> 1622695846902L)
789 .retryBackoffFactor(-1.0);
790 }
791
792 @Test
793 public void testRetriesFailTwiceThenHitMaxAttempts() {
794 Client client = Client
795 .s3()
796 .region("ap-southeast-2")
797 .accessKey("123")
798 .secretKey("456")
799 .clock(() -> 1622695846902L)
800 .retryInitialInterval(100, TimeUnit.MILLISECONDS)
801 .retryMaxAttempts(2)
802 .retryStatusCodes(408)
803 .build();
804 try (Server server = Server.start()) {
805 server.response().statusCode(408).body("timed out").add();
806 server.response().statusCode(408).body("timed out").add();
807 server.response().statusCode(200).body("stuff").add();
808 client.url(server.baseUrl())
809 .method(HttpMethod.PUT)
810 .requestBody("hi there")
811 .responseAsUtf8();
812 } catch (ServiceException e) {
813 assertEquals(408, e.statusCode());
814 }
815 }
816
817 @Test
818 public void testRetriesFailTwiceThenSucceedGivenIOExceptions() {
819 HttpClientTestingWithQueue hc = new HttpClientTestingWithQueue();
820 hc.add(new IOException("boo"));
821 hc.add(new IOException("boo2"));
822 hc.add(createResponseInputStream(200, "stuff"));
823 Client client = Client
824 .s3()
825 .region("ap-southeast-2")
826 .accessKey("123")
827 .secretKey("456")
828 .clock(() -> 1622695846902L)
829 .retryInitialInterval(100, TimeUnit.MILLISECONDS)
830 .retryMaxAttempts(0)
831 .httpClient(hc)
832 .build();
833 String text = client
834 .path("myBucket", "myObject.txt")
835 .responseAsUtf8();
836 hc.urls().forEach(System.out::println);
837 assertEquals("stuff", text);
838 }
839
840 @Test
841 public void testRetriesFailTwiceThenThrowFinalIOExceptions() {
842 HttpClientTestingWithQueue hc = new HttpClientTestingWithQueue();
843 hc.add(new IOException("boo"));
844 hc.add(new IOException("boo2"));
845 hc.add(createResponseInputStream(200, "stuff"));
846 Client client = Client
847 .s3()
848 .region("ap-southeast-2")
849 .accessKey("123")
850 .secretKey("456")
851 .clock(() -> 1622695846902L)
852 .retryInitialInterval(100, TimeUnit.MILLISECONDS)
853 .retryMaxAttempts(2)
854 .retryBackoffFactor(2.0)
855 .retryMaxInterval(3, TimeUnit.SECONDS)
856 .retryJitter(0)
857 .retryStatusCodes(400)
858 .retryException(e -> e instanceof IOException && e.getMessage().startsWith("boo"))
859 .httpClient(hc)
860 .build();
861 try {
862 client
863 .path("myBucket", "myObject.txt")
864 .responseAsUtf8();
865 Assert.fail();
866 } catch (MaxAttemptsExceededException e) {
867 assertEquals("boo2", e.getCause().getMessage());
868 }
869 }
870
871 @Test
872 public void testDontRetryException() {
873 HttpClientTestingWithQueue hc = new HttpClientTestingWithQueue();
874 hc.add(new IOException("boo"));
875 Client client = Client
876 .s3()
877 .region("ap-southeast-2")
878 .accessKey("123")
879 .secretKey("456")
880 .clock(() -> 1622695846902L)
881 .retryInitialInterval(100, TimeUnit.MILLISECONDS)
882 .retryMaxAttempts(2)
883 .retryBackoffFactor(2.0)
884 .retryMaxInterval(3, TimeUnit.SECONDS)
885 .retryJitter(0)
886 .retryStatusCodes(400)
887 .retryException(e -> false)
888 .httpClient(hc)
889 .build();
890 try {
891 client
892 .path("myBucket", "myObject.txt")
893 .responseAsUtf8();
894 Assert.fail();
895 } catch (UncheckedIOException e) {
896 assertEquals("boo", e.getCause().getMessage());
897 }
898 }
899
900 private static ResponseInputStream createResponseInputStream(int statusCode, String text) {
901 Map<String, List<String>> headers = new HashMap<>();
902 byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
903 headers.put("Content-Length", Arrays.asList(Integer.toString(bytes.length)));
904 return new ResponseInputStream(
905 () -> {
906 },
907 statusCode,
908 headers,
909 new ByteArrayInputStream(bytes));
910 }
911
912 }