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