1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.test.util.http;
20
21 import javax.net.ssl.KeyManagerFactory;
22 import javax.net.ssl.SSLContext;
23 import javax.net.ssl.TrustManagerFactory;
24
25 import java.io.File;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.UncheckedIOException;
30 import java.net.ServerSocket;
31 import java.net.URI;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.nio.file.StandardCopyOption;
37 import java.security.KeyStore;
38 import java.security.NoSuchAlgorithmException;
39 import java.time.Duration;
40 import java.util.Enumeration;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.atomic.AtomicReference;
45 import java.util.function.Supplier;
46 import java.util.stream.Stream;
47
48 import org.awaitility.Awaitility;
49 import org.eclipse.aether.ConfigurationProperties;
50 import org.eclipse.aether.DefaultRepositoryCache;
51 import org.eclipse.aether.DefaultRepositorySystemSession;
52 import org.eclipse.aether.DefaultSessionData;
53 import org.eclipse.aether.internal.impl.transport.http.DefaultChecksumExtractor;
54 import org.eclipse.aether.internal.impl.transport.http.Nx2ChecksumExtractor;
55 import org.eclipse.aether.internal.impl.transport.http.XChecksumExtractor;
56 import org.eclipse.aether.internal.test.util.TestFileUtils;
57 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
58 import org.eclipse.aether.repository.Authentication;
59 import org.eclipse.aether.repository.Proxy;
60 import org.eclipse.aether.repository.RemoteRepository;
61 import org.eclipse.aether.spi.connector.transport.GetTask;
62 import org.eclipse.aether.spi.connector.transport.PeekTask;
63 import org.eclipse.aether.spi.connector.transport.PutTask;
64 import org.eclipse.aether.spi.connector.transport.Transporter;
65 import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
66 import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy;
67 import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
68 import org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
69 import org.eclipse.aether.spi.connector.transport.http.HttpTransporterFactory;
70 import org.eclipse.aether.spi.connector.transport.http.RFC9457.HttpRFC9457Exception;
71 import org.eclipse.aether.transfer.HttpTransportProperty;
72 import org.eclipse.aether.transfer.NoTransporterException;
73 import org.eclipse.aether.transfer.TransferCancelledException;
74 import org.eclipse.aether.util.repository.AuthenticationBuilder;
75 import org.eclipse.jetty.http.HttpVersion;
76 import org.junit.jupiter.api.AfterAll;
77 import org.junit.jupiter.api.AfterEach;
78 import org.junit.jupiter.api.BeforeAll;
79 import org.junit.jupiter.api.BeforeEach;
80 import org.junit.jupiter.api.MethodOrderer;
81 import org.junit.jupiter.api.Test;
82 import org.junit.jupiter.api.TestInfo;
83 import org.junit.jupiter.api.TestMethodOrder;
84 import org.junit.jupiter.api.Timeout;
85 import org.junit.jupiter.params.ParameterizedTest;
86 import org.junit.jupiter.params.provider.ValueSource;
87
88 import static java.util.Objects.requireNonNull;
89 import static org.junit.jupiter.api.Assertions.assertEquals;
90 import static org.junit.jupiter.api.Assertions.assertNotNull;
91 import static org.junit.jupiter.api.Assertions.assertNull;
92 import static org.junit.jupiter.api.Assertions.assertThrows;
93 import static org.junit.jupiter.api.Assertions.assertTrue;
94 import static org.junit.jupiter.api.Assertions.fail;
95 import static org.junit.jupiter.api.Assumptions.assumeTrue;
96
97
98
99
100 @SuppressWarnings({"checkstyle:MethodName"})
101 @TestMethodOrder(MethodOrderer.MethodName.class)
102 public abstract class HttpTransporterTest {
103
104 protected static final Path SERVER_STORE_PATH = Paths.get("target/server-store");
105
106 protected static final Path CLIENT_STORE_PATH = Paths.get("target/client-store");
107
108 protected static final Path PEM_QUICHE_SERVER_PATH = Paths.get("target/pems");
109
110 protected static SSLContext defaultSslContext;
111
112 static {
113
114
115 }
116
117 @BeforeAll
118 protected static void beforeAll() throws NoSuchAlgorithmException {
119
120 try {
121 try (InputStream keyStoreStream =
122 HttpTransporterTest.class.getClassLoader().getResourceAsStream("ssl/server-store");
123 InputStream trustStoreStream =
124 HttpTransporterTest.class.getClassLoader().getResourceAsStream("ssl/client-store"); ) {
125 Files.copy(keyStoreStream, SERVER_STORE_PATH, StandardCopyOption.REPLACE_EXISTING);
126 Files.copy(trustStoreStream, CLIENT_STORE_PATH, StandardCopyOption.REPLACE_EXISTING);
127 Files.createDirectories(PEM_QUICHE_SERVER_PATH);
128 }
129 } catch (IOException e) {
130 throw new UncheckedIOException(e);
131 }
132
133
134 defaultSslContext = SSLContext.getDefault();
135 SSLContext.setDefault(createClientSSLContext());
136 }
137
138 @AfterAll
139 protected static void afterAll() {
140 if (defaultSslContext != null) {
141 SSLContext.setDefault(defaultSslContext);
142 }
143 }
144
145
146
147
148
149
150
151
152 protected static SSLContext createClientSSLContext() {
153 try {
154
155 KeyStore customTrustStore = KeyStore.getInstance("pkcs12");
156 try (InputStream is = Files.newInputStream(SERVER_STORE_PATH)) {
157 customTrustStore.load(is, "server-pwd".toCharArray());
158 }
159
160
161 KeyStore customKeyStore = KeyStore.getInstance("pkcs12");
162 try (InputStream is = Files.newInputStream(CLIENT_STORE_PATH)) {
163 customKeyStore.load(is, "client-pwd".toCharArray());
164 }
165
166
167 KeyStore defaultTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
168 Path defaultTrustStorePath = Path.of(System.getProperty("java.home"), "lib", "security", "cacerts");
169 if (Files.exists(defaultTrustStorePath)) {
170 try (InputStream is = Files.newInputStream(defaultTrustStorePath)) {
171 defaultTrustStore.load(is, "changeit".toCharArray());
172 }
173 } else {
174 defaultTrustStore.load(null, null);
175 }
176 Enumeration<String> aliases = customTrustStore.aliases();
177 while (aliases.hasMoreElements()) {
178 String alias = aliases.nextElement();
179 defaultTrustStore.setCertificateEntry("custom-trust-" + alias, customTrustStore.getCertificate(alias));
180 }
181
182
183 KeyStore mergedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
184 mergedKeyStore.load(null, null);
185 Enumeration<String> keyAliases = customKeyStore.aliases();
186 while (keyAliases.hasMoreElements()) {
187 String alias = keyAliases.nextElement();
188 if (customKeyStore.isKeyEntry(alias)) {
189 mergedKeyStore.setKeyEntry(
190 alias,
191 customKeyStore.getKey(alias, "client-pwd".toCharArray()),
192 "client-pwd".toCharArray(),
193 customKeyStore.getCertificateChain(alias));
194 } else {
195 mergedKeyStore.setCertificateEntry(alias, customKeyStore.getCertificate(alias));
196 }
197 }
198
199 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
200 tmf.init(defaultTrustStore);
201
202 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
203 kmf.init(mergedKeyStore, "client-pwd".toCharArray());
204
205 SSLContext sslContext = SSLContext.getInstance("TLS");
206 sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
207 return sslContext;
208 } catch (Exception e) {
209 throw new RuntimeException("Failed to create SSLContext", e);
210 }
211 }
212
213 private final Supplier<HttpTransporterFactory> transporterFactorySupplier;
214
215 protected DefaultRepositorySystemSession session;
216
217 protected HttpTransporterFactory factory;
218
219 protected HttpTransporter transporter;
220
221 protected Runnable closer;
222
223 protected File repoDir;
224
225 protected HttpServer httpServer;
226
227 protected Authentication auth;
228
229 protected Proxy proxy;
230
231 protected HttpTransporterTest(Supplier<HttpTransporterFactory> transporterFactorySupplier) {
232 this.transporterFactorySupplier = requireNonNull(transporterFactorySupplier);
233 }
234
235 protected static ChecksumExtractor standardChecksumExtractor() {
236 HashMap<String, ChecksumExtractorStrategy> strategies = new HashMap<>();
237 strategies.put("1", new Nx2ChecksumExtractor());
238 strategies.put("2", new XChecksumExtractor());
239 return new DefaultChecksumExtractor(strategies);
240 }
241
242 protected RemoteRepository newRepo(String url) {
243 return new RemoteRepository.Builder("test", "default", url)
244 .setAuthentication(auth)
245 .setProxy(proxy)
246 .build();
247 }
248
249 protected void newTransporter(String url) throws Exception {
250 if (transporter != null) {
251 transporter.close();
252 transporter = null;
253 }
254 if (closer != null) {
255 closer.run();
256 closer = null;
257 }
258 session = new DefaultRepositorySystemSession(session);
259 session.setData(new DefaultSessionData());
260 transporter = factory.newInstance(session, newRepo(url));
261 }
262
263 protected static final long OLD_FILE_TIMESTAMP = 160660800000L;
264
265
266 private static final int SC_TOO_MANY_REQUESTS = 429;
267
268 @BeforeEach
269 protected void setUp(TestInfo testInfo) throws Exception {
270 System.out.println("=== " + testInfo.getDisplayName() + " ===");
271 session = new DefaultRepositorySystemSession(h -> {
272 this.closer = h;
273 return true;
274 });
275 session.setLocalRepositoryManager(new TestLocalRepositoryManager());
276 factory = transporterFactorySupplier.get();
277 repoDir = TestFileUtils.createTempDir();
278 TestFileUtils.writeString(new File(repoDir, "file.txt"), "test");
279 TestFileUtils.writeString(new File(repoDir, "artifact.pom"), "<xml>pom</xml>");
280 TestFileUtils.writeString(new File(repoDir, "dir/file.txt"), "test");
281 TestFileUtils.writeString(new File(repoDir, "dir/oldFile.txt"), "oldTest", OLD_FILE_TIMESTAMP);
282 TestFileUtils.writeString(new File(repoDir, "empty.txt"), "");
283 TestFileUtils.writeString(new File(repoDir, "some space.txt"), "space");
284 try (InputStream is = getCompressibleFileStream()) {
285 Files.copy(is, repoDir.toPath().resolve("compressible-file.xml"));
286 }
287 File resumable = new File(repoDir, "resume.txt");
288 TestFileUtils.writeString(resumable, "resumable");
289 resumable.setLastModified(System.currentTimeMillis() - 90 * 1000);
290 httpServer = new HttpServer().setRepoDir(repoDir).start();
291
292 newTransporter(httpServer.getHttpUrl());
293 }
294
295 private static InputStream getCompressibleFileStream() {
296 return HttpTransporterTest.class.getClassLoader().getResourceAsStream("compressible-file.xml");
297 }
298
299 @AfterEach
300 protected void tearDown() throws Exception {
301 if (transporter != null) {
302 transporter.close();
303 transporter = null;
304 }
305 if (closer != null) {
306 closer.run();
307 closer = null;
308 }
309 if (httpServer != null) {
310 if (closesAllConnectionsOnTransporterClose()) {
311
312 Awaitility.await().atMost(3, TimeUnit.SECONDS).until(() -> httpServer.getNumConnectedEndPoints() == 0);
313 }
314 httpServer.stop();
315 httpServer = null;
316 }
317 factory = null;
318 session = null;
319 }
320
321
322
323
324
325 protected boolean closesAllConnectionsOnTransporterClose() {
326 return true;
327 }
328
329
330
331
332
333 protected boolean supportsPreemptiveAuth() {
334 return true;
335 }
336
337
338
339
340
341 protected boolean supportsHttp3() {
342 return true;
343 }
344
345
346
347
348
349 protected boolean supportsHttp2() {
350 return true;
351 }
352
353 @Test
354 protected void testClassify() {
355 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException()));
356 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new HttpTransporterException(403)));
357 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new HttpTransporterException(404)));
358 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new HttpTransporterException(410)));
359 }
360
361 @Test
362 protected void testPeek() throws Exception {
363 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
364 }
365
366 @Test
367 protected void testPeek_DoesNotAcceptRfc9457() throws Exception {
368
369 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
370 String accept = httpServer.getLogEntries().get(0).getRequestHeaders().get("Accept");
371 assertNull(accept, "No accept header expected for HEAD request, but was: " + accept);
372 }
373
374 @Test
375 protected void testRetryHandler_defaultCount_positive() throws Exception {
376 httpServer.setConnectionsToClose(3);
377 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
378 }
379
380 @Test
381 protected void testRetryHandler_defaultCount_negative() throws Exception {
382 httpServer.setConnectionsToClose(4);
383 try {
384 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
385 fail("Expected error");
386 } catch (Exception expected) {
387 }
388 }
389
390 @Test
391 protected void testRetryHandler_tooManyRequests_explicitCount_positive() throws Exception {
392
393 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 1);
394 int retryIntervalMs = 500;
395 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL, retryIntervalMs);
396 newTransporter(httpServer.getHttpUrl());
397 httpServer.setServerErrorsBeforeWorks(1, SC_TOO_MANY_REQUESTS);
398 long startTime = System.currentTimeMillis();
399 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
400 assertTrue(
401 System.currentTimeMillis() - startTime >= retryIntervalMs,
402 "Expected back off delay of at least " + retryIntervalMs);
403 }
404
405 @Test
406 protected void testRetryHandler_tooManyRequests_withRetryAfterHeader_explicitCount_positive() throws Exception {
407
408 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 1);
409 int retryIntervalMs = 5000;
410 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL, retryIntervalMs);
411 newTransporter(httpServer.getHttpUrl());
412 Duration retryAfterDuration = Duration.ofSeconds(1);
413 httpServer.setServerErrorsBeforeWorks(
414 1, SC_TOO_MANY_REQUESTS, Map.of("Retry-After", String.valueOf(retryAfterDuration.getSeconds())));
415 long startTime = System.currentTimeMillis();
416 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
417 long elapsedTimeMs = System.currentTimeMillis() - startTime;
418 assertTrue(
419 elapsedTimeMs >= retryAfterDuration.toMillis(),
420 "Expected back off delay of at least " + retryAfterDuration.toMillis() + " ms");
421 assertTrue(elapsedTimeMs < retryIntervalMs, "Expected back off delay of less than " + retryIntervalMs + " ms");
422 }
423
424 @Test
425 protected void testRetryHandler_tooManyRequests_explicitCount_negative() throws Exception {
426
427 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 3);
428 int retryIntervalMs = 100;
429 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL, retryIntervalMs);
430 newTransporter(httpServer.getHttpUrl());
431 httpServer.setServerErrorsBeforeWorks(4, SC_TOO_MANY_REQUESTS);
432 long startTime = System.currentTimeMillis();
433 try {
434 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
435 fail("Expected error");
436 } catch (Exception expected) {
437 }
438
439 long expectedMinimumDuration = retryIntervalMs * (1 + 2 + 3);
440 assertTrue(
441 System.currentTimeMillis() - startTime >= expectedMinimumDuration,
442 "Expected back off delay of at least " + expectedMinimumDuration);
443 }
444
445 @Test
446 protected void testRetryHandler_explicitCount_positive() throws Exception {
447 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 10);
448 newTransporter(httpServer.getHttpUrl());
449 httpServer.setConnectionsToClose(10);
450 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
451 }
452
453 @Test
454 protected void testRetryHandler_disabled() throws Exception {
455 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 0);
456 newTransporter(httpServer.getHttpUrl());
457 httpServer.setConnectionsToClose(1);
458 try {
459 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
460 } catch (Exception expected) {
461 }
462 }
463
464 @Test
465 protected void testPeek_NotFound() throws Exception {
466 try {
467 transporter.peek(new PeekTask(URI.create("repo/missing.txt")));
468 fail("Expected error");
469 } catch (HttpTransporterException e) {
470 assertEquals(404, e.getStatusCode());
471 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
472 }
473 }
474
475 @Test
476 protected void testPeek_Closed() throws Exception {
477 transporter.close();
478 try {
479 transporter.peek(new PeekTask(URI.create("repo/missing.txt")));
480 fail("Expected error");
481 } catch (IllegalStateException e) {
482 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
483 }
484 }
485
486 @Test
487 protected void testPeek_Authenticated() throws Exception {
488 httpServer.setAuthentication("testuser", "testpass");
489 auth = new AuthenticationBuilder()
490 .addUsername("testuser")
491 .addPassword("testpass")
492 .build();
493 newTransporter(httpServer.getHttpUrl());
494 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
495 }
496
497 @Test
498 protected void testPeek_Unauthenticated() throws Exception {
499 httpServer.setAuthentication("testuser", "testpass");
500 try {
501 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
502 fail("Expected error");
503 } catch (HttpTransporterException e) {
504 assertEquals(401, e.getStatusCode());
505 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
506 }
507 }
508
509 @Test
510 protected void testPeek_ProxyAuthenticated() throws Exception {
511 httpServer.setProxyAuthentication("testuser", "testpass");
512 auth = new AuthenticationBuilder()
513 .addUsername("testuser")
514 .addPassword("testpass")
515 .build();
516 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
517 newTransporter("http://bad.localhost:1/");
518 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
519 }
520
521 @Test
522 protected void testPeek_ProxyUnauthenticated() throws Exception {
523 httpServer.setProxyAuthentication("testuser", "testpass");
524 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
525 newTransporter("http://bad.localhost:1/");
526 try {
527 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
528 fail("Expected error");
529 } catch (HttpTransporterException e) {
530 assertEquals(407, e.getStatusCode());
531 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
532 }
533 }
534
535 @Test
536 protected void testPeek_SSL() throws Exception {
537 httpServer.addHttp2ConnectorWithMutualTLS();
538 newTransporter(httpServer.getHttpsUrl());
539 RecordingTransportListener listener = new RecordingTransportListener();
540 PeekTask task = new PeekTask(URI.create("repo/file.txt")).setListener(listener);
541 transporter.peek(task);
542 if (exposeContentCodingInTransportProperties()) {
543 assertEquals(
544 HttpTransportProperty.SslProtocol.TLS_1_3,
545 listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_PROTOCOL));
546 }
547 }
548
549 @Test
550 protected void testPeek_Redirect() throws Exception {
551 httpServer.addHttp2ConnectorWithMutualTLS();
552 transporter.peek(new PeekTask(URI.create("redirect/file.txt")));
553 transporter.peek(new PeekTask(URI.create("redirect/file.txt?scheme=https")));
554 }
555
556 @Test
557 protected void testGet_ToMemory() throws Exception {
558 RecordingTransportListener listener = new RecordingTransportListener();
559 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
560 transporter.get(task);
561 assertEquals("test", task.getDataString());
562 assertEquals(0L, listener.getDataOffset());
563 assertEquals(4L, listener.getDataLength());
564 assertEquals(1, listener.getStartedCount());
565 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
566 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
567 }
568
569 @Test
570 protected void testGet_ToFile() throws Exception {
571 File file = TestFileUtils.createTempFile("failure");
572 RecordingTransportListener listener = new RecordingTransportListener();
573 GetTask task = new GetTask(URI.create("repo/file.txt"))
574 .setDataPath(file.toPath())
575 .setListener(listener);
576 transporter.get(task);
577 assertEquals("test", TestFileUtils.readString(file));
578 assertEquals(0L, listener.getDataOffset());
579 assertEquals(4L, listener.getDataLength());
580 assertEquals(1, listener.getStartedCount());
581 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
582 assertEquals("test", listener.getBaos().toString(StandardCharsets.UTF_8));
583 }
584
585 @Test
586 protected void testGet_ToFileTimestamp() throws Exception {
587 File file = TestFileUtils.createTempFile("failure");
588 RecordingTransportListener listener = new RecordingTransportListener();
589 GetTask task = new GetTask(URI.create("repo/dir/oldFile.txt"))
590 .setDataPath(file.toPath())
591 .setListener(listener);
592 transporter.get(task);
593 assertEquals("oldTest", TestFileUtils.readString(file));
594 assertEquals(0L, listener.getDataOffset());
595 assertEquals(7L, listener.getDataLength());
596 assertEquals(1, listener.getStartedCount());
597 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
598 assertEquals("oldTest", listener.getBaos().toString(StandardCharsets.UTF_8));
599 assertEquals(OLD_FILE_TIMESTAMP, file.lastModified());
600 }
601
602 @Test
603 protected void testGet_AcceptsRfc9457() throws Exception {
604 GetTask task = new GetTask(URI.create("repo/file.txt"));
605 transporter.get(task);
606 String accept = httpServer.getLogEntries().get(0).getRequestHeaders().get("Accept");
607 assertNotNull(accept, "Missing Accept header when retrieving artifact");
608 assertTrue(
609 accept.contains("application/problem+json"),
610 "Expected Accept header to contain application/problem+json, but was: " + accept);
611 }
612
613 @Test
614 protected void testGet_ParseRfc9457() throws Exception {
615
616 newTransporter("https://repo.maven.apache.org");
617 try {
618
619 GetTask task = new GetTask(URI.create("cdn-cgi/error/1020"));
620 transporter.get(task);
621 fail("Should have throw HttpRFC9457Exception");
622 } catch (HttpRFC9457Exception e) {
623
624 assertEquals(403, e.getStatusCode());
625 assertEquals("Error 1020: Access denied", e.getPayload().getTitle());
626 assertEquals(
627 "The request was blocked by a Cloudflare firewall rule configured by the site owner.",
628 e.getPayload().getDetail());
629 }
630 }
631
632
633
634
635
636
637
638
639 protected abstract Stream<String> supportedCompressionAlgorithms();
640
641 protected boolean exposeContentCodingInTransportProperties() {
642 return true;
643 }
644
645 @ParameterizedTest
646
647 @ValueSource(strings = {"br", "gzip", "zstd"})
648 protected void testGet_WithCompression(String encoding) throws Exception {
649 assumeTrue(
650 supportedCompressionAlgorithms().anyMatch(supported -> supported.equals(encoding)),
651 () -> "Transporter does not support compression algorithm: " + encoding);
652 RecordingTransportListener listener = new RecordingTransportListener();
653
654
655 GetTask task = new GetTask(URI.create(encoding + "/repo/compressible-file.xml")).setListener(listener);
656 transporter.get(task);
657 String acceptEncoding =
658 httpServer.getLogEntries().get(0).getRequestHeaders().get("Accept-Encoding");
659 assertNotNull(acceptEncoding, "Missing Accept-Encoding header when retrieving pom");
660 assertTrue(acceptEncoding.contains(encoding));
661
662
663
664 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
665 assertEquals(encoding, log.getResponseHeaders().get("Content-Encoding"));
666 }
667 String expectedResourceData;
668 try (InputStream is = getCompressibleFileStream()) {
669 expectedResourceData = new String(is.readAllBytes(), StandardCharsets.UTF_8);
670 }
671 assertEquals(expectedResourceData, task.getDataString());
672 assertEquals(0L, listener.getDataOffset());
673
674 assertEquals(-1, listener.getDataLength());
675 assertEquals(1, listener.getStartedCount());
676 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
677 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
678 if (exposeContentCodingInTransportProperties()) {
679 assertEquals(encoding, listener.getTransportProperties().get(HttpTransportProperty.Key.CONTENT_CODING));
680 }
681 }
682
683 @Test
684 protected void testGet_EmptyResource() throws Exception {
685 File file = TestFileUtils.createTempFile("failure");
686 RecordingTransportListener listener = new RecordingTransportListener();
687 GetTask task = new GetTask(URI.create("repo/empty.txt"))
688 .setDataPath(file.toPath())
689 .setListener(listener);
690 transporter.get(task);
691 assertEquals("", TestFileUtils.readString(file));
692 assertEquals(0L, listener.getDataOffset());
693 assertEquals(0L, listener.getDataLength());
694 assertEquals(1, listener.getStartedCount());
695 assertEquals(0, listener.getProgressedCount());
696 assertEquals("", listener.getBaos().toString(StandardCharsets.UTF_8));
697 }
698
699 @Test
700 protected void testGet_EncodedResourcePath() throws Exception {
701 GetTask task = new GetTask(URI.create("repo/some%20space.txt"));
702 transporter.get(task);
703 assertEquals("space", task.getDataString());
704 }
705
706 @Test
707 protected void testGet_Authenticated() throws Exception {
708 httpServer.setAuthentication("testuser", "testpass");
709 auth = new AuthenticationBuilder()
710 .addUsername("testuser")
711 .addPassword("testpass")
712 .build();
713 newTransporter(httpServer.getHttpUrl());
714 RecordingTransportListener listener = new RecordingTransportListener();
715 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
716 transporter.get(task);
717 assertEquals("test", task.getDataString());
718 assertEquals(0L, listener.getDataOffset());
719 assertEquals(4L, listener.getDataLength());
720 assertEquals(1, listener.getStartedCount());
721 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
722 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
723 }
724
725 @Test
726 protected void testGet_Unauthenticated() throws Exception {
727 httpServer.setAuthentication("testuser", "testpass");
728 try {
729 transporter.get(new GetTask(URI.create("repo/file.txt")));
730 fail("Expected error");
731 } catch (HttpTransporterException e) {
732 assertEquals(401, e.getStatusCode());
733 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
734 }
735 }
736
737 @Test
738 protected void testGet_ProxyAuthenticated() throws Exception {
739 httpServer.setProxyAuthentication("testuser", "testpass");
740 Authentication auth = new AuthenticationBuilder()
741 .addUsername("testuser")
742 .addPassword("testpass")
743 .build();
744 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
745 newTransporter("http://bad.localhost:1/");
746 RecordingTransportListener listener = new RecordingTransportListener();
747 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
748 transporter.get(task);
749 assertEquals("test", task.getDataString());
750 assertEquals(0L, listener.getDataOffset());
751 assertEquals(4L, listener.getDataLength());
752 assertEquals(1, listener.getStartedCount());
753 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
754 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
755 }
756
757 @Test
758 protected void testGet_ProxyUnauthenticated() throws Exception {
759 httpServer.setProxyAuthentication("testuser", "testpass");
760 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
761 newTransporter("http://bad.localhost:1/");
762 try {
763 transporter.get(new GetTask(URI.create("repo/file.txt")));
764 fail("Expected error");
765 } catch (HttpTransporterException e) {
766 assertEquals(407, e.getStatusCode());
767 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
768 }
769 }
770
771 @Test
772 protected void testGet_RFC9457Response() throws Exception {
773 try {
774 transporter.get(new GetTask(URI.create("rfc9457/file.txt")));
775 fail("Expected error");
776 } catch (HttpRFC9457Exception e) {
777 assertEquals(403, e.getStatusCode());
778 assertEquals(e.getPayload().getType(), URI.create("https://example.com/probs/out-of-credit"));
779 assertEquals(403, e.getPayload().getStatus());
780 assertEquals("You do not have enough credit.", e.getPayload().getTitle());
781 assertEquals(
782 "Your current balance is 30, but that costs 50.",
783 e.getPayload().getDetail());
784 assertEquals(URI.create("/account/12345/msgs/abc"), e.getPayload().getInstance());
785 }
786 }
787
788 @Test
789 protected void testGet_RFC9457Response_with_missing_fields() throws Exception {
790 try {
791 transporter.get(new GetTask(URI.create("rfc9457/missing_fields.txt")));
792 fail("Expected error");
793 } catch (HttpRFC9457Exception e) {
794 assertEquals(403, e.getStatusCode());
795 assertEquals(e.getPayload().getType(), URI.create("about:blank"));
796 assertNull(e.getPayload().getStatus());
797 assertNull(e.getPayload().getTitle());
798 assertNull(e.getPayload().getDetail());
799 assertNull(e.getPayload().getInstance());
800 }
801 }
802
803 @Test
804 protected void testGet_SSL() throws Exception {
805 httpServer.addHttp2ConnectorWithMutualTLS();
806 newTransporter(httpServer.getHttpsUrl());
807 RecordingTransportListener listener = new RecordingTransportListener();
808 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
809 transporter.get(task);
810 assertEquals("test", task.getDataString());
811 assertEquals(0L, listener.getDataOffset());
812 assertEquals(4L, listener.getDataLength());
813 assertEquals(1, listener.getStartedCount());
814 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
815 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
816 if (exposeContentCodingInTransportProperties()) {
817 assertEquals(
818 HttpTransportProperty.SslProtocol.TLS_1_3,
819 listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_PROTOCOL));
820 assertEquals(
821 "TLS_AES_256_GCM_SHA384",
822 listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_CIPHER_SUITE));
823 }
824 }
825
826 @Test
827 protected void testGet_SSL_WithServerErrors() throws Exception {
828 httpServer.setServerErrorsBeforeWorks(1);
829 httpServer.addHttp2ConnectorWithMutualTLS();
830 newTransporter(httpServer.getHttpsUrl());
831 for (int i = 1; i < 3; i++) {
832 try {
833 RecordingTransportListener listener = new RecordingTransportListener();
834 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
835 transporter.get(task);
836 assertEquals("test", task.getDataString());
837 assertEquals(0L, listener.getDataOffset());
838 assertEquals(4L, listener.getDataLength());
839 assertEquals(1, listener.getStartedCount());
840 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
841 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
842 } catch (HttpTransporterException e) {
843 assertEquals(500, e.getStatusCode());
844 }
845 }
846 }
847
848 @Test
849 protected void testGet_HTTPS_Unknown_SecurityMode() throws Exception {
850 session.setConfigProperty(ConfigurationProperties.HTTPS_SECURITY_MODE, "unknown");
851 httpServer.addHttp2Connector();
852 try {
853 newTransporter(httpServer.getHttpsUrl());
854 fail("Unsupported security mode");
855 } catch (IllegalArgumentException a) {
856
857 }
858 }
859
860 @Test
861 protected void testGet_HTTPS_Insecure_SecurityMode() throws Exception {
862
863
864 SSLContext.setDefault(defaultSslContext);
865 try {
866 session.setConfigProperty(
867 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE);
868 httpServer.addHttp2Connector();
869 newTransporter(httpServer.getHttpsUrl());
870 RecordingTransportListener listener = new RecordingTransportListener();
871 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
872 transporter.get(task);
873 assertEquals("test", task.getDataString());
874 assertEquals(0L, listener.getDataOffset());
875 assertEquals(4L, listener.getDataLength());
876 assertEquals(1, listener.getStartedCount());
877 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
878 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
879 } finally {
880
881 SSLContext.setDefault(createClientSSLContext());
882 }
883 }
884
885 @Test
886 protected void testGet_HTTPS_HTTP2Only_Insecure_SecurityMode() throws Exception {
887 assumeTrue(supportsHttp2(), "Transporter does not support HTTP/2");
888
889
890 SSLContext.setDefault(defaultSslContext);
891 try {
892 session.setConfigProperty(ConfigurationProperties.HTTP_VERSION, ConfigurationProperties.HttpVersion.HTTP_2);
893 session.setConfigProperty(
894 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE);
895 httpServer.addHttp2OnlyConnector();
896 newTransporter(httpServer.getHttpsUrl());
897 RecordingTransportListener listener = new RecordingTransportListener();
898 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
899 transporter.get(task);
900 assertEquals("test", task.getDataString());
901 assertEquals(0L, listener.getDataOffset());
902 assertEquals(4L, listener.getDataLength());
903 assertEquals(1, listener.getStartedCount());
904 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
905 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
906 httpServer.getLogEntries().forEach(log -> {
907 assertEquals(HttpVersion.HTTP_2, log.getVersion());
908 });
909 } finally {
910
911 SSLContext.setDefault(createClientSSLContext());
912 }
913 }
914
915 @Test
916 protected void testGet_HTTP3Only() throws Exception {
917 assumeTrue(supportsHttp3(), "Transporter does not support HTTP/3");
918 session.setConfigProperty(ConfigurationProperties.HTTP_VERSION, ConfigurationProperties.HttpVersion.HTTP_3);
919 httpServer.addHttp3Connector(false);
920 httpServer.start();
921 newTransporter(httpServer.getHttp3Url());
922 RecordingTransportListener listener = new RecordingTransportListener();
923 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
924 transporter.get(task);
925 assertEquals("test", task.getDataString());
926 assertEquals(0L, listener.getDataOffset());
927 assertEquals(4L, listener.getDataLength());
928 assertEquals(1, listener.getStartedCount());
929 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
930 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
931 httpServer.getLogEntries().forEach(log -> {
932 assertEquals(HttpVersion.HTTP_3, log.getVersion());
933 });
934 }
935
936 @Test
937 protected void testGet_HTTP3() throws Exception {
938 assumeTrue(supportsHttp3(), "Transporter does not support HTTP/3");
939 session.setConfigProperty(ConfigurationProperties.HTTP_VERSION, ConfigurationProperties.HttpVersion.HTTP_3);
940
941
942
943 int port = httpServer.findFreeTcpAndUdpPort();
944 httpServer.addHttp2OnlyConnectorWithMutualTLS(port);
945 httpServer.addHttp3Connector(false, port);
946
947 httpServer.start();
948 newTransporter(httpServer.getHttp3Url());
949 RecordingTransportListener listener = new RecordingTransportListener();
950 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
951
952
953 transporter.get(task);
954 transporter.get(task);
955 assertEquals("test", task.getDataString());
956 assertEquals(0L, listener.getDataOffset());
957 assertEquals(4L, listener.getDataLength());
958 assertEquals(2, listener.getStartedCount());
959 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
960 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
961
962 assertEquals(
963 HttpVersion.HTTP_3,
964 httpServer
965 .getLogEntries()
966 .get(httpServer.getLogEntries().size() - 1)
967 .getVersion());
968 }
969
970 @Test
971 protected void testGet_HTTP3FallbackToHTTP2() throws Exception {
972 assumeTrue(supportsHttp3(), "Transporter does not support HTTP/3");
973 session.setConfigProperty(ConfigurationProperties.HTTP_VERSION, ConfigurationProperties.HttpVersion.HTTP_3);
974 httpServer.addHttp2OnlyConnectorWithMutualTLS();
975 httpServer.start();
976 newTransporter(httpServer.getHttpsUrl());
977 RecordingTransportListener listener = new RecordingTransportListener();
978 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
979 transporter.get(task);
980 assertEquals("test", task.getDataString());
981 assertEquals(0L, listener.getDataOffset());
982 assertEquals(4L, listener.getDataLength());
983 assertEquals(1, listener.getStartedCount());
984 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
985 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
986 assertEquals(
987 HttpTransportProperty.HttpVersion.HTTP_2,
988 listener.getTransportProperties().get(HttpTransportProperty.Key.HTTP_VERSION));
989 httpServer.getLogEntries().forEach(log -> {
990 assertEquals(HttpVersion.HTTP_2, log.getVersion());
991 });
992 }
993
994 @Test
995 protected void testGet_Redirect() throws Exception {
996 httpServer.addHttp2ConnectorWithMutualTLS();
997 RecordingTransportListener listener = new RecordingTransportListener();
998 GetTask task = new GetTask(URI.create("redirect/file.txt?scheme=https")).setListener(listener);
999 transporter.get(task);
1000 assertEquals("test", task.getDataString());
1001 assertEquals(0L, listener.getDataOffset());
1002 assertEquals(4L, listener.getDataLength());
1003 assertEquals(1, listener.getStartedCount());
1004 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1005 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
1006 }
1007
1008 @Test
1009 protected void testGet_Resume() throws Exception {
1010 File file = TestFileUtils.createTempFile("re");
1011 RecordingTransportListener listener = new RecordingTransportListener();
1012 GetTask task = new GetTask(URI.create("repo/resume.txt"))
1013 .setDataPath(file.toPath(), true)
1014 .setListener(listener);
1015 transporter.get(task);
1016 assertEquals("resumable", TestFileUtils.readString(file));
1017 assertEquals(1L, listener.getStartedCount());
1018 assertEquals(2L, listener.getDataOffset());
1019 assertEquals(9, listener.getDataLength());
1020 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1021 assertEquals("sumable", listener.getBaos().toString(StandardCharsets.UTF_8));
1022 }
1023
1024 @Test
1025 protected void testGet_ResumeLocalContentsOutdated() throws Exception {
1026 File file = TestFileUtils.createTempFile("re");
1027 file.setLastModified(System.currentTimeMillis() - 5 * 60 * 1000);
1028 RecordingTransportListener listener = new RecordingTransportListener();
1029 GetTask task = new GetTask(URI.create("repo/resume.txt"))
1030 .setDataPath(file.toPath(), true)
1031 .setListener(listener);
1032 transporter.get(task);
1033 assertEquals("resumable", TestFileUtils.readString(file));
1034 assertEquals(1L, listener.getStartedCount());
1035 assertEquals(0L, listener.getDataOffset());
1036 assertEquals(9, listener.getDataLength());
1037 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1038 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8));
1039 }
1040
1041 @Test
1042 protected void testGet_ResumeRangesNotSupportedByServer() throws Exception {
1043 httpServer.setRangeSupport(false);
1044 File file = TestFileUtils.createTempFile("re");
1045 RecordingTransportListener listener = new RecordingTransportListener();
1046 GetTask task = new GetTask(URI.create("repo/resume.txt"))
1047 .setDataPath(file.toPath(), true)
1048 .setListener(listener);
1049 transporter.get(task);
1050 assertEquals("resumable", TestFileUtils.readString(file));
1051 assertEquals(1L, listener.getStartedCount());
1052 assertEquals(0L, listener.getDataOffset());
1053 assertEquals(9, listener.getDataLength());
1054 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1055 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8));
1056 }
1057
1058 @Test
1059 protected void testGet_Checksums_Nexus() throws Exception {
1060 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.NEXUS);
1061 GetTask task = new GetTask(URI.create("repo/file.txt"));
1062 transporter.get(task);
1063 assertEquals("test", task.getDataString());
1064 assertEquals(
1065 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
1066 }
1067
1068 @Test
1069 protected void testGet_Checksums_XChecksum() throws Exception {
1070 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM);
1071 GetTask task = new GetTask(URI.create("repo/file.txt"));
1072 transporter.get(task);
1073 assertEquals("test", task.getDataString());
1074 assertEquals(
1075 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
1076 }
1077
1078 @Test
1079 protected void testGet_Checksums_XChecksum_Google() throws Exception {
1080 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM_GOOGLE);
1081 GetTask task = new GetTask(URI.create("repo/file.txt"));
1082 transporter.get(task);
1083 assertEquals("test", task.getDataString());
1084 assertEquals(
1085 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
1086 }
1087
1088 @Test
1089 protected void testGet_Checksums_XChecksum_Amazon() throws Exception {
1090 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM_AMAZON);
1091 GetTask task = new GetTask(URI.create("repo/file.txt"));
1092 transporter.get(task);
1093 assertEquals("test", task.getDataString());
1094 assertEquals(
1095 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
1096 }
1097
1098 @Test
1099 protected void testGet_FileHandleLeak() throws Exception {
1100 for (int i = 0; i < 100; i++) {
1101 File file = TestFileUtils.createTempFile("failure");
1102 transporter.get(new GetTask(URI.create("repo/file.txt")).setDataPath(file.toPath()));
1103 assertTrue(file.delete(), i + ", " + file.getAbsolutePath());
1104 }
1105 }
1106
1107 @Test
1108 protected void testGet_NotFound() throws Exception {
1109 try {
1110 transporter.get(new GetTask(URI.create("repo/missing.txt")));
1111 fail("Expected error");
1112 } catch (HttpTransporterException e) {
1113 assertEquals(404, e.getStatusCode());
1114 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
1115 }
1116 }
1117
1118 @Test
1119 protected void testGet_Closed() throws Exception {
1120 transporter.close();
1121 try {
1122 transporter.get(new GetTask(URI.create("repo/file.txt")));
1123 fail("Expected error");
1124 } catch (IllegalStateException e) {
1125 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1126 }
1127 }
1128
1129 @Test
1130 protected void testGet_StartCancelled() throws Exception {
1131 RecordingTransportListener listener = new RecordingTransportListener();
1132 listener.cancelStart();
1133 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
1134 try {
1135 transporter.get(task);
1136 fail("Expected error");
1137 } catch (TransferCancelledException e) {
1138 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1139 }
1140 assertEquals(0L, listener.getDataOffset());
1141 assertEquals(4L, listener.getDataLength());
1142 assertEquals(1, listener.getStartedCount());
1143 assertEquals(0, listener.getProgressedCount());
1144 }
1145
1146 @Test
1147 protected void testGet_ProgressCancelled() throws Exception {
1148 RecordingTransportListener listener = new RecordingTransportListener();
1149 listener.cancelProgress();
1150 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
1151 try {
1152 transporter.get(task);
1153 fail("Expected error");
1154 } catch (TransferCancelledException e) {
1155 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1156 }
1157 assertEquals(0L, listener.getDataOffset());
1158 assertEquals(4L, listener.getDataLength());
1159 assertEquals(1, listener.getStartedCount());
1160 assertEquals(1, listener.getProgressedCount());
1161 }
1162
1163 @Test
1164 protected void testPut_FromMemory() throws Exception {
1165 RecordingTransportListener listener = new RecordingTransportListener();
1166 String payload = "upload";
1167 PutTask task =
1168 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString(payload);
1169 transporter.put(task);
1170 assertEquals(0L, listener.getDataOffset());
1171 assertEquals(6L, listener.getDataLength());
1172 assertEquals(1, listener.getStartedCount());
1173 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1174 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1175 assertEquals(
1176 String.valueOf(payload.getBytes(StandardCharsets.UTF_8).length),
1177 httpServer.getLogEntries().get(0).getRequestHeaders().get("Content-Length"));
1178 }
1179
1180 @Test
1181 protected void testPut_AcceptsRfc9457() throws Exception {
1182 String payload = "upload";
1183 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString(payload);
1184 transporter.put(task);
1185 String accept = httpServer.getLogEntries().get(0).getRequestHeaders().get("Accept");
1186 assertNotNull(accept, "Missing Accept header when retrieving artifact");
1187 assertTrue(
1188 accept.contains("application/problem+json"),
1189 "Expected Accept header to contain application/problem+json, but was: " + accept);
1190 }
1191
1192 @Test
1193 protected void testPut_FromFile() throws Exception {
1194 File file = TestFileUtils.createTempFile("upload");
1195 RecordingTransportListener listener = new RecordingTransportListener();
1196 PutTask task =
1197 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataPath(file.toPath());
1198 transporter.put(task);
1199 assertEquals(0L, listener.getDataOffset());
1200 assertEquals(6L, listener.getDataLength());
1201 assertEquals(1, listener.getStartedCount());
1202 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1203 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1204 assertEquals(
1205 String.valueOf(file.length()),
1206 httpServer.getLogEntries().get(0).getRequestHeaders().get("Content-Length"));
1207 }
1208
1209 @Test
1210 protected void testPut_EmptyResource() throws Exception {
1211 RecordingTransportListener listener = new RecordingTransportListener();
1212 PutTask task = new PutTask(URI.create("repo/file.txt")).setListener(listener);
1213 transporter.put(task);
1214 assertEquals(0L, listener.getDataOffset());
1215 assertEquals(0L, listener.getDataLength());
1216
1217 assertTrue(
1218 listener.getStartedCount() <= 1,
1219 "The transport should be started at most once but was started " + listener.getStartedCount()
1220 + " times");
1221 assertEquals(0, listener.getProgressedCount());
1222 assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt")));
1223 }
1224
1225 @Test
1226 protected void testPut_EncodedResourcePath() throws Exception {
1227 RecordingTransportListener listener = new RecordingTransportListener();
1228 PutTask task = new PutTask(URI.create("repo/some%20space.txt"))
1229 .setListener(listener)
1230 .setDataString("OK");
1231 transporter.put(task);
1232 assertEquals(0L, listener.getDataOffset());
1233 assertEquals(2L, listener.getDataLength());
1234 assertEquals(1, listener.getStartedCount());
1235 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1236 assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt")));
1237 }
1238
1239 @Test
1240 protected void testPut_Authenticated_ExpectContinue() throws Exception {
1241 httpServer.setAuthentication("testuser", "testpass");
1242 auth = new AuthenticationBuilder()
1243 .addUsername("testuser")
1244 .addPassword("testpass")
1245 .build();
1246 newTransporter(httpServer.getHttpUrl());
1247 RecordingTransportListener listener = new RecordingTransportListener();
1248 PutTask task =
1249 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1250 transporter.put(task);
1251 assertEquals(0L, listener.getDataOffset());
1252 assertEquals(6L, listener.getDataLength());
1253 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1254 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1255 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1256 }
1257
1258 @Test
1259 protected void testPut_Authenticated_ExpectContinueBroken() throws Exception {
1260
1261 session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true);
1262 httpServer.setAuthentication("testuser", "testpass");
1263 httpServer.setExpectSupport(HttpServer.ExpectContinue.BROKEN);
1264 auth = new AuthenticationBuilder()
1265 .addUsername("testuser")
1266 .addPassword("testpass")
1267 .build();
1268 newTransporter(httpServer.getHttpUrl());
1269 RecordingTransportListener listener = new RecordingTransportListener();
1270 PutTask task =
1271 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1272 transporter.put(task);
1273 assertEquals(0L, listener.getDataOffset());
1274 assertEquals(6L, listener.getDataLength());
1275 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1276 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1277 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1278 }
1279
1280 @Test
1281 protected void testPut_Authenticated_ExpectContinueRejected() throws Exception {
1282 httpServer.setAuthentication("testuser", "testpass");
1283 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
1284 auth = new AuthenticationBuilder()
1285 .addUsername("testuser")
1286 .addPassword("testpass")
1287 .build();
1288 newTransporter(httpServer.getHttpUrl());
1289 RecordingTransportListener listener = new RecordingTransportListener();
1290 PutTask task =
1291 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1292 transporter.put(task);
1293 assertEquals(0L, listener.getDataOffset());
1294 assertEquals(6L, listener.getDataLength());
1295 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1296 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1297 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1298 }
1299
1300 @Test
1301 protected void testPut_Authenticated_ExpectContinueDisabled() throws Exception {
1302 session.setConfigProperty(ConfigurationProperties.HTTP_EXPECT_CONTINUE, false);
1303 httpServer.setAuthentication("testuser", "testpass");
1304 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
1305 auth = new AuthenticationBuilder()
1306 .addUsername("testuser")
1307 .addPassword("testpass")
1308 .build();
1309 newTransporter(httpServer.getHttpUrl());
1310 RecordingTransportListener listener = new RecordingTransportListener();
1311 PutTask task =
1312 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1313 transporter.put(task);
1314 assertEquals(0L, listener.getDataOffset());
1315 assertEquals(6L, listener.getDataLength());
1316 assertEquals(
1317 supportsPreemptiveAuth() ? 1 : 2,
1318 listener.getStartedCount());
1319 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1320 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1321 }
1322
1323 @Test
1324 protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() throws Exception {
1325 Map<String, String> headers = new HashMap<>();
1326 headers.put("Expect", "100-continue");
1327 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1328 httpServer.setAuthentication("testuser", "testpass");
1329 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
1330 auth = new AuthenticationBuilder()
1331 .addUsername("testuser")
1332 .addPassword("testpass")
1333 .build();
1334 newTransporter(httpServer.getHttpUrl());
1335 RecordingTransportListener listener = new RecordingTransportListener();
1336 PutTask task =
1337 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1338 transporter.put(task);
1339 assertEquals(0L, listener.getDataOffset());
1340 assertEquals(6L, listener.getDataLength());
1341 assertEquals(1, listener.getStartedCount());
1342 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1343 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1344 }
1345
1346 @Test
1347 protected void testPut_Unauthenticated() throws Exception {
1348 httpServer.setAuthentication("testuser", "testpass");
1349 RecordingTransportListener listener = new RecordingTransportListener();
1350 PutTask task =
1351 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1352 try {
1353 transporter.put(task);
1354 fail("Expected error");
1355 } catch (HttpTransporterException e) {
1356 assertEquals(401, e.getStatusCode());
1357 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1358 }
1359 assertEquals(0, listener.getStartedCount());
1360 assertEquals(0, listener.getProgressedCount());
1361 }
1362
1363 @Test
1364 protected void testPut_ProxyAuthenticated() throws Exception {
1365 httpServer.setProxyAuthentication("testuser", "testpass");
1366 Authentication auth = new AuthenticationBuilder()
1367 .addUsername("testuser")
1368 .addPassword("testpass")
1369 .build();
1370 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1371 newTransporter("http://bad.localhost:1/");
1372 RecordingTransportListener listener = new RecordingTransportListener();
1373 PutTask task =
1374 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1375 transporter.put(task);
1376 assertEquals(0L, listener.getDataOffset());
1377 assertEquals(6L, listener.getDataLength());
1378 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1379 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1380 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1381 }
1382
1383 @Test
1384 protected void testPut_ProxyUnauthenticated() throws Exception {
1385 httpServer.setProxyAuthentication("testuser", "testpass");
1386 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1387 newTransporter("http://bad.localhost:1/");
1388 RecordingTransportListener listener = new RecordingTransportListener();
1389 PutTask task =
1390 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1391 try {
1392 transporter.put(task);
1393 fail("Expected error");
1394 } catch (HttpTransporterException e) {
1395 assertEquals(407, e.getStatusCode());
1396 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1397 }
1398 assertEquals(0, listener.getStartedCount());
1399 assertEquals(0, listener.getProgressedCount());
1400 }
1401
1402 @Test
1403 protected void testPut_SSL() throws Exception {
1404 httpServer.addHttp2ConnectorWithMutualTLS();
1405 httpServer.setAuthentication("testuser", "testpass");
1406 auth = new AuthenticationBuilder()
1407 .addUsername("testuser")
1408 .addPassword("testpass")
1409 .build();
1410 newTransporter(httpServer.getHttpsUrl());
1411 RecordingTransportListener listener = new RecordingTransportListener();
1412 PutTask task =
1413 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1414 transporter.put(task);
1415 assertEquals(0L, listener.getDataOffset());
1416 assertEquals(6L, listener.getDataLength());
1417 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1418 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1419 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1420 assertEquals(
1421 HttpTransportProperty.SslProtocol.TLS_1_3,
1422 listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_PROTOCOL));
1423 }
1424
1425 @Test
1426 protected void testPut_FileHandleLeak() throws Exception {
1427 for (int i = 0; i < 100; i++) {
1428 File src = TestFileUtils.createTempFile("upload");
1429 File dst = new File(repoDir, "file.txt");
1430 transporter.put(new PutTask(URI.create("repo/file.txt")).setDataPath(src.toPath()));
1431 assertTrue(src.delete(), i + ", " + src.getAbsolutePath());
1432 assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath());
1433 }
1434 }
1435
1436 @Test
1437 protected void testPut_Closed() throws Exception {
1438 transporter.close();
1439 try {
1440 transporter.put(new PutTask(URI.create("repo/missing.txt")));
1441 fail("Expected error");
1442 } catch (IllegalStateException e) {
1443 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1444 }
1445 }
1446
1447 @Test
1448 protected void testPut_StartCancelled() throws Exception {
1449 RecordingTransportListener listener = new RecordingTransportListener();
1450 listener.cancelStart();
1451 PutTask task =
1452 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1453 try {
1454 transporter.put(task);
1455 fail("Expected error");
1456 } catch (TransferCancelledException e) {
1457 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1458 }
1459 assertEquals(0L, listener.getDataOffset());
1460 assertEquals(6L, listener.getDataLength());
1461 assertEquals(1, listener.getStartedCount());
1462 assertEquals(0, listener.getProgressedCount());
1463 }
1464
1465 @Test
1466 protected void testPut_ProgressCancelled() throws Exception {
1467 RecordingTransportListener listener = new RecordingTransportListener();
1468 listener.cancelProgress();
1469 PutTask task =
1470 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1471 try {
1472 transporter.put(task);
1473 fail("Expected error");
1474 } catch (TransferCancelledException e) {
1475 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1476 }
1477 assertEquals(0L, listener.getDataOffset());
1478 assertEquals(6L, listener.getDataLength());
1479 assertEquals(1, listener.getStartedCount());
1480 assertEquals(1, listener.getProgressedCount());
1481 }
1482
1483 @Test
1484 protected void testGetPut_AuthCache() throws Exception {
1485 httpServer.setAuthentication("testuser", "testpass");
1486 auth = new AuthenticationBuilder()
1487 .addUsername("testuser")
1488 .addPassword("testpass")
1489 .build();
1490 newTransporter(httpServer.getHttpUrl());
1491 GetTask get = new GetTask(URI.create("repo/file.txt"));
1492 transporter.get(get);
1493 RecordingTransportListener listener = new RecordingTransportListener();
1494 PutTask task =
1495 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1496 transporter.put(task);
1497 assertEquals(1, listener.getStartedCount());
1498 }
1499
1500 @Test
1501 protected void testPut_PreemptiveIsDefault() throws Exception {
1502 httpServer.setAuthentication("testuser", "testpass");
1503 auth = new AuthenticationBuilder()
1504 .addUsername("testuser")
1505 .addPassword("testpass")
1506 .build();
1507 newTransporter(httpServer.getHttpUrl());
1508 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1509 transporter.put(task);
1510 assertEquals(
1511 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1512 }
1513
1514 @Test
1515 protected void testPut_AuthCache() throws Exception {
1516 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH, false);
1517 httpServer.setAuthentication("testuser", "testpass");
1518 auth = new AuthenticationBuilder()
1519 .addUsername("testuser")
1520 .addPassword("testpass")
1521 .build();
1522 newTransporter(httpServer.getHttpUrl());
1523 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1524 transporter.put(task);
1525 assertEquals(2, httpServer.getLogEntries().size());
1526 httpServer.getLogEntries().clear();
1527 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1528 transporter.put(task);
1529 assertEquals(1, httpServer.getLogEntries().size());
1530 }
1531
1532 @Test
1533 protected void testPut_AuthCache_Preemptive() throws Exception {
1534 httpServer.setAuthentication("testuser", "testpass");
1535 auth = new AuthenticationBuilder()
1536 .addUsername("testuser")
1537 .addPassword("testpass")
1538 .build();
1539 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1540 newTransporter(httpServer.getHttpUrl());
1541 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1542 transporter.put(task);
1543 assertEquals(
1544 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1545 httpServer.getLogEntries().clear();
1546 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1547 transporter.put(task);
1548 assertEquals(1, httpServer.getLogEntries().size());
1549 }
1550
1551 @Test
1552 protected void testPut_WithResponseBody() throws Exception {
1553 httpServer.setAuthentication("testuser", "testpass");
1554 httpServer.setResponseBodyForPut("Some dummy response body");
1555 auth = new AuthenticationBuilder()
1556 .addUsername("testuser")
1557 .addPassword("testpass")
1558 .build();
1559 newTransporter(httpServer.getHttpUrl());
1560 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1561 transporter.put(task);
1562
1563 }
1564
1565 @Test
1566 @Timeout(20)
1567 protected void testConcurrency() throws Exception {
1568 httpServer.setAuthentication("testuser", "testpass");
1569 auth = new AuthenticationBuilder()
1570 .addUsername("testuser")
1571 .addPassword("testpass")
1572 .build();
1573 newTransporter(httpServer.getHttpUrl());
1574 final AtomicReference<Throwable> error = new AtomicReference<>();
1575 Thread[] threads = new Thread[20];
1576 for (int i = 0; i < threads.length; i++) {
1577 final String path = "repo/file.txt?i=" + i;
1578 threads[i] = new Thread(() -> {
1579 try {
1580 for (int j = 0; j < 100; j++) {
1581 GetTask task = new GetTask(URI.create(path));
1582 transporter.get(task);
1583 assertEquals("test", task.getDataString());
1584 }
1585 } catch (Throwable t) {
1586 error.compareAndSet(null, t);
1587 System.err.println(path);
1588 t.printStackTrace();
1589 }
1590 });
1591 threads[i].setName("Task-" + i);
1592 }
1593 for (Thread thread : threads) {
1594 thread.start();
1595 }
1596 for (Thread thread : threads) {
1597 thread.join();
1598 }
1599 assertNull(error.get(), String.valueOf(error.get()));
1600 }
1601
1602 @Test
1603 @Timeout(10)
1604 protected void testConnectTimeout() throws Exception {
1605 session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, 100);
1606 int port = 1;
1607 newTransporter("http://localhost:" + port);
1608 try {
1609 transporter.get(new GetTask(URI.create("repo/file.txt")));
1610 fail("Expected error");
1611 } catch (Exception e) {
1612
1613 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1614 }
1615 }
1616
1617 @Test
1618 @Timeout(10)
1619 protected void testRequestTimeout() throws Exception {
1620 session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, 100);
1621 ServerSocket server = new ServerSocket(0);
1622 try (server) {
1623 newTransporter("http://localhost:" + server.getLocalPort());
1624 try {
1625 transporter.get(new GetTask(URI.create("repo/file.txt")));
1626 fail("Expected error");
1627 } catch (Exception e) {
1628 assertTrue(e.getClass().getSimpleName().contains("Timeout"));
1629 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1630 }
1631 }
1632 }
1633
1634 @Test
1635 protected void testUserAgent() throws Exception {
1636 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1637 newTransporter(httpServer.getHttpUrl());
1638 transporter.get(new GetTask(URI.create("repo/file.txt")));
1639 assertEquals(1, httpServer.getLogEntries().size());
1640 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1641 assertEquals("SomeTest/1.0", log.getRequestHeaders().get("User-Agent"));
1642 }
1643 }
1644
1645 @Test
1646 protected void testCustomHeaders() throws Exception {
1647 Map<String, String> headers = new HashMap<>();
1648 headers.put("User-Agent", "Custom/1.0");
1649 headers.put("X-CustomHeader", "Custom-Value");
1650 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1651 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1652 newTransporter(httpServer.getHttpUrl());
1653 transporter.get(new GetTask(URI.create("repo/file.txt")));
1654 assertEquals(1, httpServer.getLogEntries().size());
1655 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1656 for (Map.Entry<String, String> entry : headers.entrySet()) {
1657 assertEquals(entry.getValue(), log.getRequestHeaders().get(entry.getKey()), entry.getKey());
1658 }
1659 }
1660 }
1661
1662 @Test
1663 protected void testServerAuthScope_NotUsedForProxy() throws Exception {
1664 String username = "testuser", password = "testpass";
1665 httpServer.setProxyAuthentication(username, password);
1666 auth = new AuthenticationBuilder()
1667 .addUsername(username)
1668 .addPassword(password)
1669 .build();
1670 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1671 newTransporter("http://" + httpServer.getHost() + ":12/");
1672 try {
1673 transporter.get(new GetTask(URI.create("repo/file.txt")));
1674 fail("Server auth must not be used as proxy auth");
1675 } catch (HttpTransporterException e) {
1676 assertEquals(407, e.getStatusCode());
1677 } catch (IOException e) {
1678
1679 }
1680 }
1681
1682 @Test
1683 protected void testProxyAuthScope_NotUsedForServer() throws Exception {
1684 String username = "testuser", password = "testpass";
1685 httpServer.setAuthentication(username, password);
1686 Authentication auth = new AuthenticationBuilder()
1687 .addUsername(username)
1688 .addPassword(password)
1689 .build();
1690 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1691 newTransporter("http://" + httpServer.getHost() + ":12/");
1692 try {
1693 transporter.get(new GetTask(URI.create("repo/file.txt")));
1694 fail("Proxy auth must not be used as server auth");
1695 } catch (HttpTransporterException e) {
1696 assertEquals(401, e.getStatusCode());
1697 } catch (IOException e) {
1698
1699 }
1700 }
1701
1702 @Test
1703 protected void testAuthSchemeReuse() throws Exception {
1704 httpServer.setAuthentication("testuser", "testpass");
1705 httpServer.setProxyAuthentication("proxyuser", "proxypass");
1706 session.setCache(new DefaultRepositoryCache());
1707 auth = new AuthenticationBuilder()
1708 .addUsername("testuser")
1709 .addPassword("testpass")
1710 .build();
1711 Authentication auth = new AuthenticationBuilder()
1712 .addUsername("proxyuser")
1713 .addPassword("proxypass")
1714 .build();
1715 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1716 newTransporter("http://bad.localhost:1/");
1717 GetTask task = new GetTask(URI.create("repo/file.txt"));
1718 transporter.get(task);
1719 assertEquals("test", task.getDataString());
1720 assertEquals(3, httpServer.getLogEntries().size());
1721 httpServer.getLogEntries().clear();
1722 newTransporter("http://bad.localhost:1/");
1723 task = new GetTask(URI.create("repo/file.txt"));
1724 transporter.get(task);
1725 assertEquals("test", task.getDataString());
1726 assertEquals(1, httpServer.getLogEntries().size());
1727 assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Authorization"));
1728 assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Proxy-Authorization"));
1729 }
1730
1731 @Test
1732 protected void testAuthSchemePreemptive() throws Exception {
1733 httpServer.setAuthentication("testuser", "testpass");
1734 session.setCache(new DefaultRepositoryCache());
1735 auth = new AuthenticationBuilder()
1736 .addUsername("testuser")
1737 .addPassword("testpass")
1738 .build();
1739
1740 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, false);
1741 newTransporter(httpServer.getHttpUrl());
1742 GetTask task = new GetTask(URI.create("repo/file.txt"));
1743 transporter.get(task);
1744 assertEquals("test", task.getDataString());
1745
1746 assertEquals(2, httpServer.getLogEntries().size());
1747
1748 httpServer.getLogEntries().clear();
1749
1750 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1751 newTransporter(httpServer.getHttpUrl());
1752 task = new GetTask(URI.create("repo/file.txt"));
1753 transporter.get(task);
1754 assertEquals("test", task.getDataString());
1755
1756 assertEquals(
1757 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1758 }
1759
1760 @Test
1761 void testInit_BadProtocol() {
1762 assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void"));
1763 }
1764
1765 @Test
1766 void testInit_BadUrl() {
1767 assertThrows(NoTransporterException.class, () -> newTransporter("http://localhost:NaN"));
1768 }
1769
1770 @Test
1771 void testInit_CaseInsensitiveProtocol() throws Exception {
1772 newTransporter("http://localhost");
1773 newTransporter("HTTP://localhost");
1774 newTransporter("Http://localhost");
1775 newTransporter("https://localhost");
1776 newTransporter("HTTPS://localhost");
1777 newTransporter("HttpS://localhost");
1778 }
1779 }