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 java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.UncheckedIOException;
26 import java.net.ServerSocket;
27 import java.net.URI;
28 import java.net.URL;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.nio.file.StandardCopyOption;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.concurrent.atomic.AtomicReference;
37 import java.util.function.Supplier;
38 import java.util.stream.Stream;
39
40 import org.eclipse.aether.ConfigurationProperties;
41 import org.eclipse.aether.DefaultRepositoryCache;
42 import org.eclipse.aether.DefaultRepositorySystemSession;
43 import org.eclipse.aether.DefaultSessionData;
44 import org.eclipse.aether.internal.impl.transport.http.DefaultChecksumExtractor;
45 import org.eclipse.aether.internal.impl.transport.http.Nx2ChecksumExtractor;
46 import org.eclipse.aether.internal.impl.transport.http.XChecksumExtractor;
47 import org.eclipse.aether.internal.test.util.TestFileUtils;
48 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
49 import org.eclipse.aether.repository.Authentication;
50 import org.eclipse.aether.repository.Proxy;
51 import org.eclipse.aether.repository.RemoteRepository;
52 import org.eclipse.aether.spi.connector.transport.GetTask;
53 import org.eclipse.aether.spi.connector.transport.PeekTask;
54 import org.eclipse.aether.spi.connector.transport.PutTask;
55 import org.eclipse.aether.spi.connector.transport.Transporter;
56 import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
57 import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy;
58 import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
59 import org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
60 import org.eclipse.aether.spi.connector.transport.http.HttpTransporterFactory;
61 import org.eclipse.aether.spi.connector.transport.http.RFC9457.HttpRFC9457Exception;
62 import org.eclipse.aether.transfer.NoTransporterException;
63 import org.eclipse.aether.transfer.TransferCancelledException;
64 import org.eclipse.aether.util.repository.AuthenticationBuilder;
65 import org.junit.jupiter.api.AfterEach;
66 import org.junit.jupiter.api.BeforeEach;
67 import org.junit.jupiter.api.Test;
68 import org.junit.jupiter.api.TestInfo;
69 import org.junit.jupiter.api.Timeout;
70 import org.junit.jupiter.params.ParameterizedTest;
71 import org.junit.jupiter.params.provider.ValueSource;
72
73 import static java.util.Objects.requireNonNull;
74 import static org.junit.jupiter.api.Assertions.assertEquals;
75 import static org.junit.jupiter.api.Assertions.assertNotNull;
76 import static org.junit.jupiter.api.Assertions.assertNull;
77 import static org.junit.jupiter.api.Assertions.assertThrows;
78 import static org.junit.jupiter.api.Assertions.assertTrue;
79 import static org.junit.jupiter.api.Assertions.fail;
80 import static org.junit.jupiter.api.Assumptions.assumeTrue;
81
82
83
84
85 @SuppressWarnings({"checkstyle:MethodName"})
86 public abstract class HttpTransporterTest {
87
88 protected static final Path KEY_STORE_PATH = Paths.get("target/keystore");
89
90 protected static final Path KEY_STORE_SELF_SIGNED_PATH = Paths.get("target/keystore-self-signed");
91
92 protected static final Path TRUST_STORE_PATH = Paths.get("target/trustStore");
93
94 static {
95
96 System.setProperty(
97 "javax.net.ssl.trustStore", KEY_STORE_PATH.toAbsolutePath().toString());
98 System.setProperty("javax.net.ssl.trustStorePassword", "server-pwd");
99 System.setProperty(
100 "javax.net.ssl.keyStore", TRUST_STORE_PATH.toAbsolutePath().toString());
101 System.setProperty("javax.net.ssl.keyStorePassword", "client-pwd");
102
103 System.setProperty("javax.net.ssl.trustStoreType", "jks");
104 System.setProperty("javax.net.ssl.keyStoreType", "jks");
105
106 }
107
108 private final Supplier<HttpTransporterFactory> transporterFactorySupplier;
109
110 protected DefaultRepositorySystemSession session;
111
112 protected HttpTransporterFactory factory;
113
114 protected HttpTransporter transporter;
115
116 protected Runnable closer;
117
118 protected File repoDir;
119
120 protected HttpServer httpServer;
121
122 protected Authentication auth;
123
124 protected Proxy proxy;
125
126 protected HttpTransporterTest(Supplier<HttpTransporterFactory> transporterFactorySupplier) {
127 this.transporterFactorySupplier = requireNonNull(transporterFactorySupplier);
128
129 if (!Files.isRegularFile(KEY_STORE_PATH)) {
130 URL keyStoreUrl = HttpTransporterTest.class.getClassLoader().getResource("ssl/server-store");
131 URL keyStoreSelfSignedUrl =
132 HttpTransporterTest.class.getClassLoader().getResource("ssl/server-store-selfsigned");
133 URL trustStoreUrl = HttpTransporterTest.class.getClassLoader().getResource("ssl/client-store");
134
135 try {
136 try (InputStream keyStoreStream = keyStoreUrl.openStream();
137 InputStream keyStoreSelfSignedStream = keyStoreSelfSignedUrl.openStream();
138 InputStream trustStoreStream = trustStoreUrl.openStream()) {
139 Files.copy(keyStoreStream, KEY_STORE_PATH, StandardCopyOption.REPLACE_EXISTING);
140 Files.copy(
141 keyStoreSelfSignedStream, KEY_STORE_SELF_SIGNED_PATH, StandardCopyOption.REPLACE_EXISTING);
142 Files.copy(trustStoreStream, TRUST_STORE_PATH, StandardCopyOption.REPLACE_EXISTING);
143 }
144 } catch (IOException e) {
145 throw new UncheckedIOException(e);
146 }
147 }
148 }
149
150 protected static ChecksumExtractor standardChecksumExtractor() {
151 HashMap<String, ChecksumExtractorStrategy> strategies = new HashMap<>();
152 strategies.put("1", new Nx2ChecksumExtractor());
153 strategies.put("2", new XChecksumExtractor());
154 return new DefaultChecksumExtractor(strategies);
155 }
156
157 protected RemoteRepository newRepo(String url) {
158 return new RemoteRepository.Builder("test", "default", url)
159 .setAuthentication(auth)
160 .setProxy(proxy)
161 .build();
162 }
163
164 protected void newTransporter(String url) throws Exception {
165 if (transporter != null) {
166 transporter.close();
167 transporter = null;
168 }
169 if (closer != null) {
170 closer.run();
171 closer = null;
172 }
173 session = new DefaultRepositorySystemSession(session);
174 session.setData(new DefaultSessionData());
175 transporter = factory.newInstance(session, newRepo(url));
176 }
177
178 protected static final long OLD_FILE_TIMESTAMP = 160660800000L;
179
180
181 private static final int SC_TOO_MANY_REQUESTS = 429;
182
183 @BeforeEach
184 protected void setUp(TestInfo testInfo) throws Exception {
185 System.out.println("=== " + testInfo.getDisplayName() + " ===");
186 session = new DefaultRepositorySystemSession(h -> {
187 this.closer = h;
188 return true;
189 });
190 session.setLocalRepositoryManager(new TestLocalRepositoryManager());
191 factory = transporterFactorySupplier.get();
192 repoDir = TestFileUtils.createTempDir();
193 TestFileUtils.writeString(new File(repoDir, "file.txt"), "test");
194 TestFileUtils.writeString(new File(repoDir, "artifact.pom"), "<xml>pom</xml>");
195 TestFileUtils.writeString(new File(repoDir, "dir/file.txt"), "test");
196 TestFileUtils.writeString(new File(repoDir, "dir/oldFile.txt"), "oldTest", OLD_FILE_TIMESTAMP);
197 TestFileUtils.writeString(new File(repoDir, "empty.txt"), "");
198 TestFileUtils.writeString(new File(repoDir, "some space.txt"), "space");
199 try (InputStream is = getCompressibleFileStream()) {
200 Files.copy(is, repoDir.toPath().resolve("compressible-file.xml"));
201 }
202 File resumable = new File(repoDir, "resume.txt");
203 TestFileUtils.writeString(resumable, "resumable");
204 resumable.setLastModified(System.currentTimeMillis() - 90 * 1000);
205 httpServer = new HttpServer().setRepoDir(repoDir).start();
206 newTransporter(httpServer.getHttpUrl());
207 }
208
209 private static InputStream getCompressibleFileStream() {
210 return HttpTransporterTest.class.getClassLoader().getResourceAsStream("compressible-file.xml");
211 }
212
213 @AfterEach
214 protected void tearDown() throws Exception {
215 if (transporter != null) {
216 transporter.close();
217 transporter = null;
218 }
219 if (closer != null) {
220 closer.run();
221 closer = null;
222 }
223 if (httpServer != null) {
224 httpServer.stop();
225 httpServer = null;
226 }
227 factory = null;
228 session = null;
229 }
230
231
232
233
234
235 protected boolean supportsPreemptiveAuth() {
236 return true;
237 }
238
239 @Test
240 protected void testClassify() {
241 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException()));
242 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new HttpTransporterException(403)));
243 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new HttpTransporterException(404)));
244 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new HttpTransporterException(410)));
245 }
246
247 @Test
248 protected void testPeek() throws Exception {
249 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
250 }
251
252 @Test
253 protected void testRetryHandler_defaultCount_positive() throws Exception {
254 httpServer.setConnectionsToClose(3);
255 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
256 }
257
258 @Test
259 protected void testRetryHandler_defaultCount_negative() throws Exception {
260 httpServer.setConnectionsToClose(4);
261 try {
262 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
263 fail("Expected error");
264 } catch (Exception expected) {
265 }
266 }
267
268 @Test
269 protected void testRetryHandler_tooManyRequests_explicitCount_positive() throws Exception {
270
271 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 1);
272 int retryIntervalMs = 500;
273 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL, retryIntervalMs);
274 newTransporter(httpServer.getHttpUrl());
275 httpServer.setServerErrorsBeforeWorks(1, SC_TOO_MANY_REQUESTS);
276 long startTime = System.currentTimeMillis();
277 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
278 assertTrue(
279 System.currentTimeMillis() - startTime >= retryIntervalMs,
280 "Expected back off delay of at least " + retryIntervalMs);
281 }
282
283 @Test
284 protected void testRetryHandler_tooManyRequests_explicitCount_negative() throws Exception {
285
286 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 3);
287 int retryIntervalMs = 100;
288 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL, retryIntervalMs);
289 newTransporter(httpServer.getHttpUrl());
290 httpServer.setServerErrorsBeforeWorks(4, SC_TOO_MANY_REQUESTS);
291 long startTime = System.currentTimeMillis();
292 try {
293 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
294 fail("Expected error");
295 } catch (Exception expected) {
296 }
297
298 long expectedMinimumDuration = retryIntervalMs * (1 + 2 + 3);
299 assertTrue(
300 System.currentTimeMillis() - startTime >= expectedMinimumDuration,
301 "Expected back off delay of at least " + expectedMinimumDuration);
302 }
303
304 @Test
305 protected void testRetryHandler_explicitCount_positive() throws Exception {
306 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 10);
307 newTransporter(httpServer.getHttpUrl());
308 httpServer.setConnectionsToClose(10);
309 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
310 }
311
312 @Test
313 protected void testRetryHandler_disabled() throws Exception {
314 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 0);
315 newTransporter(httpServer.getHttpUrl());
316 httpServer.setConnectionsToClose(1);
317 try {
318 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
319 } catch (Exception expected) {
320 }
321 }
322
323 @Test
324 protected void testPeek_NotFound() throws Exception {
325 try {
326 transporter.peek(new PeekTask(URI.create("repo/missing.txt")));
327 fail("Expected error");
328 } catch (HttpTransporterException e) {
329 assertEquals(404, e.getStatusCode());
330 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
331 }
332 }
333
334 @Test
335 protected void testPeek_Closed() throws Exception {
336 transporter.close();
337 try {
338 transporter.peek(new PeekTask(URI.create("repo/missing.txt")));
339 fail("Expected error");
340 } catch (IllegalStateException e) {
341 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
342 }
343 }
344
345 @Test
346 protected void testPeek_Authenticated() throws Exception {
347 httpServer.setAuthentication("testuser", "testpass");
348 auth = new AuthenticationBuilder()
349 .addUsername("testuser")
350 .addPassword("testpass")
351 .build();
352 newTransporter(httpServer.getHttpUrl());
353 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
354 }
355
356 @Test
357 protected void testPeek_Unauthenticated() throws Exception {
358 httpServer.setAuthentication("testuser", "testpass");
359 try {
360 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
361 fail("Expected error");
362 } catch (HttpTransporterException e) {
363 assertEquals(401, e.getStatusCode());
364 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
365 }
366 }
367
368 @Test
369 protected void testPeek_ProxyAuthenticated() throws Exception {
370 httpServer.setProxyAuthentication("testuser", "testpass");
371 auth = new AuthenticationBuilder()
372 .addUsername("testuser")
373 .addPassword("testpass")
374 .build();
375 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
376 newTransporter("http://bad.localhost:1/");
377 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
378 }
379
380 @Test
381 protected void testPeek_ProxyUnauthenticated() throws Exception {
382 httpServer.setProxyAuthentication("testuser", "testpass");
383 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
384 newTransporter("http://bad.localhost:1/");
385 try {
386 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
387 fail("Expected error");
388 } catch (HttpTransporterException e) {
389 assertEquals(407, e.getStatusCode());
390 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
391 }
392 }
393
394 @Test
395 protected void testPeek_SSL() throws Exception {
396 httpServer.addSslConnector();
397 newTransporter(httpServer.getHttpsUrl());
398 transporter.peek(new PeekTask(URI.create("repo/file.txt")));
399 }
400
401 @Test
402 protected void testPeek_Redirect() throws Exception {
403 httpServer.addSslConnector();
404 transporter.peek(new PeekTask(URI.create("redirect/file.txt")));
405 transporter.peek(new PeekTask(URI.create("redirect/file.txt?scheme=https")));
406 }
407
408 @Test
409 protected void testGet_ToMemory() throws Exception {
410 RecordingTransportListener listener = new RecordingTransportListener();
411 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
412 transporter.get(task);
413 assertEquals("test", task.getDataString());
414 assertEquals(0L, listener.getDataOffset());
415 assertEquals(4L, listener.getDataLength());
416 assertEquals(1, listener.getStartedCount());
417 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
418 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
419 }
420
421 @Test
422 protected void testGet_ToFile() throws Exception {
423 File file = TestFileUtils.createTempFile("failure");
424 RecordingTransportListener listener = new RecordingTransportListener();
425 GetTask task = new GetTask(URI.create("repo/file.txt"))
426 .setDataPath(file.toPath())
427 .setListener(listener);
428 transporter.get(task);
429 assertEquals("test", TestFileUtils.readString(file));
430 assertEquals(0L, listener.getDataOffset());
431 assertEquals(4L, listener.getDataLength());
432 assertEquals(1, listener.getStartedCount());
433 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
434 assertEquals("test", listener.getBaos().toString(StandardCharsets.UTF_8));
435 }
436
437 @Test
438 protected void testGet_ToFileTimestamp() throws Exception {
439 File file = TestFileUtils.createTempFile("failure");
440 RecordingTransportListener listener = new RecordingTransportListener();
441 GetTask task = new GetTask(URI.create("repo/dir/oldFile.txt"))
442 .setDataPath(file.toPath())
443 .setListener(listener);
444 transporter.get(task);
445 assertEquals("oldTest", TestFileUtils.readString(file));
446 assertEquals(0L, listener.getDataOffset());
447 assertEquals(7L, listener.getDataLength());
448 assertEquals(1, listener.getStartedCount());
449 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
450 assertEquals("oldTest", listener.getBaos().toString(StandardCharsets.UTF_8));
451 assertEquals(OLD_FILE_TIMESTAMP, file.lastModified());
452 }
453
454
455
456
457
458
459
460
461 protected abstract Stream<String> supportedCompressionAlgorithms();
462
463 @ParameterizedTest
464
465 @ValueSource(strings = {"br", "gzip", "zstd"})
466 protected void testGet_WithCompression(String encoding) throws Exception {
467 assumeTrue(
468 supportedCompressionAlgorithms().anyMatch(supported -> supported.equals(encoding)),
469 () -> "Transporter does not support compression algorithm: " + encoding);
470 RecordingTransportListener listener = new RecordingTransportListener();
471
472
473 GetTask task = new GetTask(URI.create(encoding + "/repo/compressible-file.xml")).setListener(listener);
474 transporter.get(task);
475 String acceptEncoding =
476 httpServer.getLogEntries().get(0).getRequestHeaders().get("Accept-Encoding");
477 assertNotNull(acceptEncoding, "Missing Accept-Encoding header when retrieving pom");
478 assertTrue(acceptEncoding.contains(encoding));
479
480
481
482 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
483 assertEquals(encoding, log.getResponseHeaders().get("Content-Encoding"));
484 }
485 String expectedResourceData;
486 try (InputStream is = getCompressibleFileStream()) {
487 expectedResourceData = new String(is.readAllBytes(), StandardCharsets.UTF_8);
488 }
489 assertEquals(expectedResourceData, task.getDataString());
490 assertEquals(0L, listener.getDataOffset());
491
492 assertEquals(-1, listener.getDataLength());
493 assertEquals(1, listener.getStartedCount());
494 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
495 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
496 }
497
498 @Test
499 protected void testGet_EmptyResource() throws Exception {
500 File file = TestFileUtils.createTempFile("failure");
501 RecordingTransportListener listener = new RecordingTransportListener();
502 GetTask task = new GetTask(URI.create("repo/empty.txt"))
503 .setDataPath(file.toPath())
504 .setListener(listener);
505 transporter.get(task);
506 assertEquals("", TestFileUtils.readString(file));
507 assertEquals(0L, listener.getDataOffset());
508 assertEquals(0L, listener.getDataLength());
509 assertEquals(1, listener.getStartedCount());
510 assertEquals(0, listener.getProgressedCount());
511 assertEquals("", listener.getBaos().toString(StandardCharsets.UTF_8));
512 }
513
514 @Test
515 protected void testGet_EncodedResourcePath() throws Exception {
516 GetTask task = new GetTask(URI.create("repo/some%20space.txt"));
517 transporter.get(task);
518 assertEquals("space", task.getDataString());
519 }
520
521 @Test
522 protected void testGet_Authenticated() throws Exception {
523 httpServer.setAuthentication("testuser", "testpass");
524 auth = new AuthenticationBuilder()
525 .addUsername("testuser")
526 .addPassword("testpass")
527 .build();
528 newTransporter(httpServer.getHttpUrl());
529 RecordingTransportListener listener = new RecordingTransportListener();
530 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
531 transporter.get(task);
532 assertEquals("test", task.getDataString());
533 assertEquals(0L, listener.getDataOffset());
534 assertEquals(4L, listener.getDataLength());
535 assertEquals(1, listener.getStartedCount());
536 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
537 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
538 }
539
540 @Test
541 protected void testGet_Unauthenticated() throws Exception {
542 httpServer.setAuthentication("testuser", "testpass");
543 try {
544 transporter.get(new GetTask(URI.create("repo/file.txt")));
545 fail("Expected error");
546 } catch (HttpTransporterException e) {
547 assertEquals(401, e.getStatusCode());
548 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
549 }
550 }
551
552 @Test
553 protected void testGet_ProxyAuthenticated() throws Exception {
554 httpServer.setProxyAuthentication("testuser", "testpass");
555 Authentication auth = new AuthenticationBuilder()
556 .addUsername("testuser")
557 .addPassword("testpass")
558 .build();
559 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
560 newTransporter("http://bad.localhost:1/");
561 RecordingTransportListener listener = new RecordingTransportListener();
562 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
563 transporter.get(task);
564 assertEquals("test", task.getDataString());
565 assertEquals(0L, listener.getDataOffset());
566 assertEquals(4L, listener.getDataLength());
567 assertEquals(1, listener.getStartedCount());
568 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
569 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
570 }
571
572 @Test
573 protected void testGet_ProxyUnauthenticated() throws Exception {
574 httpServer.setProxyAuthentication("testuser", "testpass");
575 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
576 newTransporter("http://bad.localhost:1/");
577 try {
578 transporter.get(new GetTask(URI.create("repo/file.txt")));
579 fail("Expected error");
580 } catch (HttpTransporterException e) {
581 assertEquals(407, e.getStatusCode());
582 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
583 }
584 }
585
586 @Test
587 protected void testGet_RFC9457Response() throws Exception {
588 try {
589 transporter.get(new GetTask(URI.create("rfc9457/file.txt")));
590 fail("Expected error");
591 } catch (HttpRFC9457Exception e) {
592 assertEquals(403, e.getStatusCode());
593 assertEquals(e.getPayload().getType(), URI.create("https://example.com/probs/out-of-credit"));
594 assertEquals(403, e.getPayload().getStatus());
595 assertEquals("You do not have enough credit.", e.getPayload().getTitle());
596 assertEquals(
597 "Your current balance is 30, but that costs 50.",
598 e.getPayload().getDetail());
599 assertEquals(URI.create("/account/12345/msgs/abc"), e.getPayload().getInstance());
600 }
601 }
602
603 @Test
604 protected void testGet_RFC9457Response_with_missing_fields() throws Exception {
605 try {
606 transporter.get(new GetTask(URI.create("rfc9457/missing_fields.txt")));
607 fail("Expected error");
608 } catch (HttpRFC9457Exception e) {
609 assertEquals(403, e.getStatusCode());
610 assertEquals(e.getPayload().getType(), URI.create("about:blank"));
611 assertNull(e.getPayload().getStatus());
612 assertNull(e.getPayload().getTitle());
613 assertNull(e.getPayload().getDetail());
614 assertNull(e.getPayload().getInstance());
615 }
616 }
617
618 @Test
619 protected void testGet_SSL() throws Exception {
620 httpServer.addSslConnector();
621 newTransporter(httpServer.getHttpsUrl());
622 RecordingTransportListener listener = new RecordingTransportListener();
623 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
624 transporter.get(task);
625 assertEquals("test", task.getDataString());
626 assertEquals(0L, listener.getDataOffset());
627 assertEquals(4L, listener.getDataLength());
628 assertEquals(1, listener.getStartedCount());
629 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
630 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
631 }
632
633 @Test
634 protected void testGet_SSL_WithServerErrors() throws Exception {
635 httpServer.setServerErrorsBeforeWorks(1);
636 httpServer.addSslConnector();
637 newTransporter(httpServer.getHttpsUrl());
638 for (int i = 1; i < 3; i++) {
639 try {
640 RecordingTransportListener listener = new RecordingTransportListener();
641 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
642 transporter.get(task);
643 assertEquals("test", task.getDataString());
644 assertEquals(0L, listener.getDataOffset());
645 assertEquals(4L, listener.getDataLength());
646 assertEquals(1, listener.getStartedCount());
647 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
648 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
649 } catch (HttpTransporterException e) {
650 assertEquals(500, e.getStatusCode());
651 }
652 }
653 }
654
655 @Test
656 protected void testGet_HTTPS_Unknown_SecurityMode() throws Exception {
657 session.setConfigProperty(ConfigurationProperties.HTTPS_SECURITY_MODE, "unknown");
658 httpServer.addSelfSignedSslConnector();
659 try {
660 newTransporter(httpServer.getHttpsUrl());
661 fail("Unsupported security mode");
662 } catch (IllegalArgumentException a) {
663
664 }
665 }
666
667 @Test
668 protected void testGet_HTTPS_Insecure_SecurityMode() throws Exception {
669
670
671 session.setConfigProperty(
672 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE);
673 httpServer.addSelfSignedSslConnector();
674 newTransporter(httpServer.getHttpsUrl());
675 RecordingTransportListener listener = new RecordingTransportListener();
676 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
677 transporter.get(task);
678 assertEquals("test", task.getDataString());
679 assertEquals(0L, listener.getDataOffset());
680 assertEquals(4L, listener.getDataLength());
681 assertEquals(1, listener.getStartedCount());
682 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
683 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
684 }
685
686 @Test
687 protected void testGet_HTTPS_HTTP2Only_Insecure_SecurityMode() throws Exception {
688
689
690 enableHttp2Protocol();
691 session.setConfigProperty(
692 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE);
693 httpServer.addSelfSignedSslConnectorHttp2Only();
694 newTransporter(httpServer.getHttpsUrl());
695 RecordingTransportListener listener = new RecordingTransportListener();
696 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
697 transporter.get(task);
698 assertEquals("test", task.getDataString());
699 assertEquals(0L, listener.getDataOffset());
700 assertEquals(4L, listener.getDataLength());
701 assertEquals(1, listener.getStartedCount());
702 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
703 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
704 }
705
706 protected void enableHttp2Protocol() {}
707
708 @Test
709 protected void testGet_Redirect() throws Exception {
710 httpServer.addSslConnector();
711 RecordingTransportListener listener = new RecordingTransportListener();
712 GetTask task = new GetTask(URI.create("redirect/file.txt?scheme=https")).setListener(listener);
713 transporter.get(task);
714 assertEquals("test", task.getDataString());
715 assertEquals(0L, listener.getDataOffset());
716 assertEquals(4L, listener.getDataLength());
717 assertEquals(1, listener.getStartedCount());
718 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
719 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8));
720 }
721
722 @Test
723 protected void testGet_Resume() throws Exception {
724 File file = TestFileUtils.createTempFile("re");
725 RecordingTransportListener listener = new RecordingTransportListener();
726 GetTask task = new GetTask(URI.create("repo/resume.txt"))
727 .setDataPath(file.toPath(), true)
728 .setListener(listener);
729 transporter.get(task);
730 assertEquals("resumable", TestFileUtils.readString(file));
731 assertEquals(1L, listener.getStartedCount());
732 assertEquals(2L, listener.getDataOffset());
733 assertEquals(9, listener.getDataLength());
734 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
735 assertEquals("sumable", listener.getBaos().toString(StandardCharsets.UTF_8));
736 }
737
738 @Test
739 protected void testGet_ResumeLocalContentsOutdated() throws Exception {
740 File file = TestFileUtils.createTempFile("re");
741 file.setLastModified(System.currentTimeMillis() - 5 * 60 * 1000);
742 RecordingTransportListener listener = new RecordingTransportListener();
743 GetTask task = new GetTask(URI.create("repo/resume.txt"))
744 .setDataPath(file.toPath(), true)
745 .setListener(listener);
746 transporter.get(task);
747 assertEquals("resumable", TestFileUtils.readString(file));
748 assertEquals(1L, listener.getStartedCount());
749 assertEquals(0L, listener.getDataOffset());
750 assertEquals(9, listener.getDataLength());
751 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
752 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8));
753 }
754
755 @Test
756 protected void testGet_ResumeRangesNotSupportedByServer() throws Exception {
757 httpServer.setRangeSupport(false);
758 File file = TestFileUtils.createTempFile("re");
759 RecordingTransportListener listener = new RecordingTransportListener();
760 GetTask task = new GetTask(URI.create("repo/resume.txt"))
761 .setDataPath(file.toPath(), true)
762 .setListener(listener);
763 transporter.get(task);
764 assertEquals("resumable", TestFileUtils.readString(file));
765 assertEquals(1L, listener.getStartedCount());
766 assertEquals(0L, listener.getDataOffset());
767 assertEquals(9, listener.getDataLength());
768 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
769 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8));
770 }
771
772 @Test
773 protected void testGet_Checksums_Nexus() throws Exception {
774 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.NEXUS);
775 GetTask task = new GetTask(URI.create("repo/file.txt"));
776 transporter.get(task);
777 assertEquals("test", task.getDataString());
778 assertEquals(
779 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
780 }
781
782 @Test
783 protected void testGet_Checksums_XChecksum() throws Exception {
784 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM);
785 GetTask task = new GetTask(URI.create("repo/file.txt"));
786 transporter.get(task);
787 assertEquals("test", task.getDataString());
788 assertEquals(
789 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
790 }
791
792 @Test
793 protected void testGet_FileHandleLeak() throws Exception {
794 for (int i = 0; i < 100; i++) {
795 File file = TestFileUtils.createTempFile("failure");
796 transporter.get(new GetTask(URI.create("repo/file.txt")).setDataPath(file.toPath()));
797 assertTrue(file.delete(), i + ", " + file.getAbsolutePath());
798 }
799 }
800
801 @Test
802 protected void testGet_NotFound() throws Exception {
803 try {
804 transporter.get(new GetTask(URI.create("repo/missing.txt")));
805 fail("Expected error");
806 } catch (HttpTransporterException e) {
807 assertEquals(404, e.getStatusCode());
808 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
809 }
810 }
811
812 @Test
813 protected void testGet_Closed() throws Exception {
814 transporter.close();
815 try {
816 transporter.get(new GetTask(URI.create("repo/file.txt")));
817 fail("Expected error");
818 } catch (IllegalStateException e) {
819 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
820 }
821 }
822
823 @Test
824 protected void testGet_StartCancelled() throws Exception {
825 RecordingTransportListener listener = new RecordingTransportListener();
826 listener.cancelStart();
827 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
828 try {
829 transporter.get(task);
830 fail("Expected error");
831 } catch (TransferCancelledException e) {
832 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
833 }
834 assertEquals(0L, listener.getDataOffset());
835 assertEquals(4L, listener.getDataLength());
836 assertEquals(1, listener.getStartedCount());
837 assertEquals(0, listener.getProgressedCount());
838 }
839
840 @Test
841 protected void testGet_ProgressCancelled() throws Exception {
842 RecordingTransportListener listener = new RecordingTransportListener();
843 listener.cancelProgress();
844 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
845 try {
846 transporter.get(task);
847 fail("Expected error");
848 } catch (TransferCancelledException e) {
849 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
850 }
851 assertEquals(0L, listener.getDataOffset());
852 assertEquals(4L, listener.getDataLength());
853 assertEquals(1, listener.getStartedCount());
854 assertEquals(1, listener.getProgressedCount());
855 }
856
857 @Test
858 protected void testPut_FromMemory() throws Exception {
859 RecordingTransportListener listener = new RecordingTransportListener();
860 String payload = "upload";
861 PutTask task =
862 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString(payload);
863 transporter.put(task);
864 assertEquals(0L, listener.getDataOffset());
865 assertEquals(6L, listener.getDataLength());
866 assertEquals(1, listener.getStartedCount());
867 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
868 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
869 assertEquals(
870 String.valueOf(payload.getBytes(StandardCharsets.UTF_8).length),
871 httpServer.getLogEntries().get(0).getRequestHeaders().get("Content-Length"));
872 }
873
874 @Test
875 protected void testPut_FromFile() throws Exception {
876 File file = TestFileUtils.createTempFile("upload");
877 RecordingTransportListener listener = new RecordingTransportListener();
878 PutTask task =
879 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataPath(file.toPath());
880 transporter.put(task);
881 assertEquals(0L, listener.getDataOffset());
882 assertEquals(6L, listener.getDataLength());
883 assertEquals(1, listener.getStartedCount());
884 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
885 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
886 assertEquals(
887 String.valueOf(file.length()),
888 httpServer.getLogEntries().get(0).getRequestHeaders().get("Content-Length"));
889 }
890
891 @Test
892 protected void testPut_EmptyResource() throws Exception {
893 RecordingTransportListener listener = new RecordingTransportListener();
894 PutTask task = new PutTask(URI.create("repo/file.txt")).setListener(listener);
895 transporter.put(task);
896 assertEquals(0L, listener.getDataOffset());
897 assertEquals(0L, listener.getDataLength());
898
899 assertTrue(
900 listener.getStartedCount() <= 1,
901 "The transport should be started at most once but was started " + listener.getStartedCount()
902 + " times");
903 assertEquals(0, listener.getProgressedCount());
904 assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt")));
905 }
906
907 @Test
908 protected void testPut_EncodedResourcePath() throws Exception {
909 RecordingTransportListener listener = new RecordingTransportListener();
910 PutTask task = new PutTask(URI.create("repo/some%20space.txt"))
911 .setListener(listener)
912 .setDataString("OK");
913 transporter.put(task);
914 assertEquals(0L, listener.getDataOffset());
915 assertEquals(2L, listener.getDataLength());
916 assertEquals(1, listener.getStartedCount());
917 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
918 assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt")));
919 }
920
921 @Test
922 protected void testPut_Authenticated_ExpectContinue() throws Exception {
923 httpServer.setAuthentication("testuser", "testpass");
924 auth = new AuthenticationBuilder()
925 .addUsername("testuser")
926 .addPassword("testpass")
927 .build();
928 newTransporter(httpServer.getHttpUrl());
929 RecordingTransportListener listener = new RecordingTransportListener();
930 PutTask task =
931 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
932 transporter.put(task);
933 assertEquals(0L, listener.getDataOffset());
934 assertEquals(6L, listener.getDataLength());
935 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
936 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
937 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
938 }
939
940 @Test
941 protected void testPut_Authenticated_ExpectContinueBroken() throws Exception {
942
943 session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true);
944 httpServer.setAuthentication("testuser", "testpass");
945 httpServer.setExpectSupport(HttpServer.ExpectContinue.BROKEN);
946 auth = new AuthenticationBuilder()
947 .addUsername("testuser")
948 .addPassword("testpass")
949 .build();
950 newTransporter(httpServer.getHttpUrl());
951 RecordingTransportListener listener = new RecordingTransportListener();
952 PutTask task =
953 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
954 transporter.put(task);
955 assertEquals(0L, listener.getDataOffset());
956 assertEquals(6L, listener.getDataLength());
957 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
958 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
959 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
960 }
961
962 @Test
963 protected void testPut_Authenticated_ExpectContinueRejected() throws Exception {
964 httpServer.setAuthentication("testuser", "testpass");
965 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
966 auth = new AuthenticationBuilder()
967 .addUsername("testuser")
968 .addPassword("testpass")
969 .build();
970 newTransporter(httpServer.getHttpUrl());
971 RecordingTransportListener listener = new RecordingTransportListener();
972 PutTask task =
973 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
974 transporter.put(task);
975 assertEquals(0L, listener.getDataOffset());
976 assertEquals(6L, listener.getDataLength());
977 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
978 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
979 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
980 }
981
982 @Test
983 protected void testPut_Authenticated_ExpectContinueDisabled() throws Exception {
984 session.setConfigProperty(ConfigurationProperties.HTTP_EXPECT_CONTINUE, false);
985 httpServer.setAuthentication("testuser", "testpass");
986 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
987 auth = new AuthenticationBuilder()
988 .addUsername("testuser")
989 .addPassword("testpass")
990 .build();
991 newTransporter(httpServer.getHttpUrl());
992 RecordingTransportListener listener = new RecordingTransportListener();
993 PutTask task =
994 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
995 transporter.put(task);
996 assertEquals(0L, listener.getDataOffset());
997 assertEquals(6L, listener.getDataLength());
998 assertEquals(
999 supportsPreemptiveAuth() ? 1 : 2,
1000 listener.getStartedCount());
1001 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1002 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1003 }
1004
1005 @Test
1006 protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() throws Exception {
1007 Map<String, String> headers = new HashMap<>();
1008 headers.put("Expect", "100-continue");
1009 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1010 httpServer.setAuthentication("testuser", "testpass");
1011 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
1012 auth = new AuthenticationBuilder()
1013 .addUsername("testuser")
1014 .addPassword("testpass")
1015 .build();
1016 newTransporter(httpServer.getHttpUrl());
1017 RecordingTransportListener listener = new RecordingTransportListener();
1018 PutTask task =
1019 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1020 transporter.put(task);
1021 assertEquals(0L, listener.getDataOffset());
1022 assertEquals(6L, listener.getDataLength());
1023 assertEquals(1, listener.getStartedCount());
1024 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1025 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1026 }
1027
1028 @Test
1029 protected void testPut_Unauthenticated() throws Exception {
1030 httpServer.setAuthentication("testuser", "testpass");
1031 RecordingTransportListener listener = new RecordingTransportListener();
1032 PutTask task =
1033 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1034 try {
1035 transporter.put(task);
1036 fail("Expected error");
1037 } catch (HttpTransporterException e) {
1038 assertEquals(401, e.getStatusCode());
1039 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1040 }
1041 assertEquals(0, listener.getStartedCount());
1042 assertEquals(0, listener.getProgressedCount());
1043 }
1044
1045 @Test
1046 protected void testPut_ProxyAuthenticated() throws Exception {
1047 httpServer.setProxyAuthentication("testuser", "testpass");
1048 Authentication auth = new AuthenticationBuilder()
1049 .addUsername("testuser")
1050 .addPassword("testpass")
1051 .build();
1052 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1053 newTransporter("http://bad.localhost:1/");
1054 RecordingTransportListener listener = new RecordingTransportListener();
1055 PutTask task =
1056 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1057 transporter.put(task);
1058 assertEquals(0L, listener.getDataOffset());
1059 assertEquals(6L, listener.getDataLength());
1060 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1061 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1062 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1063 }
1064
1065 @Test
1066 protected void testPut_ProxyUnauthenticated() throws Exception {
1067 httpServer.setProxyAuthentication("testuser", "testpass");
1068 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1069 newTransporter("http://bad.localhost:1/");
1070 RecordingTransportListener listener = new RecordingTransportListener();
1071 PutTask task =
1072 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1073 try {
1074 transporter.put(task);
1075 fail("Expected error");
1076 } catch (HttpTransporterException e) {
1077 assertEquals(407, e.getStatusCode());
1078 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1079 }
1080 assertEquals(0, listener.getStartedCount());
1081 assertEquals(0, listener.getProgressedCount());
1082 }
1083
1084 @Test
1085 protected void testPut_SSL() throws Exception {
1086 httpServer.addSslConnector();
1087 httpServer.setAuthentication("testuser", "testpass");
1088 auth = new AuthenticationBuilder()
1089 .addUsername("testuser")
1090 .addPassword("testpass")
1091 .build();
1092 newTransporter(httpServer.getHttpsUrl());
1093 RecordingTransportListener listener = new RecordingTransportListener();
1094 PutTask task =
1095 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1096 transporter.put(task);
1097 assertEquals(0L, listener.getDataOffset());
1098 assertEquals(6L, listener.getDataLength());
1099 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1100 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1101 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1102 }
1103
1104 @Test
1105 protected void testPut_FileHandleLeak() throws Exception {
1106 for (int i = 0; i < 100; i++) {
1107 File src = TestFileUtils.createTempFile("upload");
1108 File dst = new File(repoDir, "file.txt");
1109 transporter.put(new PutTask(URI.create("repo/file.txt")).setDataPath(src.toPath()));
1110 assertTrue(src.delete(), i + ", " + src.getAbsolutePath());
1111 assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath());
1112 }
1113 }
1114
1115 @Test
1116 protected void testPut_Closed() throws Exception {
1117 transporter.close();
1118 try {
1119 transporter.put(new PutTask(URI.create("repo/missing.txt")));
1120 fail("Expected error");
1121 } catch (IllegalStateException e) {
1122 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1123 }
1124 }
1125
1126 @Test
1127 protected void testPut_StartCancelled() throws Exception {
1128 RecordingTransportListener listener = new RecordingTransportListener();
1129 listener.cancelStart();
1130 PutTask task =
1131 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1132 try {
1133 transporter.put(task);
1134 fail("Expected error");
1135 } catch (TransferCancelledException e) {
1136 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1137 }
1138 assertEquals(0L, listener.getDataOffset());
1139 assertEquals(6L, listener.getDataLength());
1140 assertEquals(1, listener.getStartedCount());
1141 assertEquals(0, listener.getProgressedCount());
1142 }
1143
1144 @Test
1145 protected void testPut_ProgressCancelled() throws Exception {
1146 RecordingTransportListener listener = new RecordingTransportListener();
1147 listener.cancelProgress();
1148 PutTask task =
1149 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1150 try {
1151 transporter.put(task);
1152 fail("Expected error");
1153 } catch (TransferCancelledException e) {
1154 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1155 }
1156 assertEquals(0L, listener.getDataOffset());
1157 assertEquals(6L, listener.getDataLength());
1158 assertEquals(1, listener.getStartedCount());
1159 assertEquals(1, listener.getProgressedCount());
1160 }
1161
1162 @Test
1163 protected void testGetPut_AuthCache() throws Exception {
1164 httpServer.setAuthentication("testuser", "testpass");
1165 auth = new AuthenticationBuilder()
1166 .addUsername("testuser")
1167 .addPassword("testpass")
1168 .build();
1169 newTransporter(httpServer.getHttpUrl());
1170 GetTask get = new GetTask(URI.create("repo/file.txt"));
1171 transporter.get(get);
1172 RecordingTransportListener listener = new RecordingTransportListener();
1173 PutTask task =
1174 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1175 transporter.put(task);
1176 assertEquals(1, listener.getStartedCount());
1177 }
1178
1179 @Test
1180 protected void testPut_PreemptiveIsDefault() throws Exception {
1181 httpServer.setAuthentication("testuser", "testpass");
1182 auth = new AuthenticationBuilder()
1183 .addUsername("testuser")
1184 .addPassword("testpass")
1185 .build();
1186 newTransporter(httpServer.getHttpUrl());
1187 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1188 transporter.put(task);
1189 assertEquals(
1190 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1191 }
1192
1193 @Test
1194 protected void testPut_AuthCache() throws Exception {
1195 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH, false);
1196 httpServer.setAuthentication("testuser", "testpass");
1197 auth = new AuthenticationBuilder()
1198 .addUsername("testuser")
1199 .addPassword("testpass")
1200 .build();
1201 newTransporter(httpServer.getHttpUrl());
1202 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1203 transporter.put(task);
1204 assertEquals(2, httpServer.getLogEntries().size());
1205 httpServer.getLogEntries().clear();
1206 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1207 transporter.put(task);
1208 assertEquals(1, httpServer.getLogEntries().size());
1209 }
1210
1211 @Test
1212 protected void testPut_AuthCache_Preemptive() throws Exception {
1213 httpServer.setAuthentication("testuser", "testpass");
1214 auth = new AuthenticationBuilder()
1215 .addUsername("testuser")
1216 .addPassword("testpass")
1217 .build();
1218 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1219 newTransporter(httpServer.getHttpUrl());
1220 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1221 transporter.put(task);
1222 assertEquals(
1223 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1224 httpServer.getLogEntries().clear();
1225 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1226 transporter.put(task);
1227 assertEquals(1, httpServer.getLogEntries().size());
1228 }
1229
1230 @Test
1231 @Timeout(20)
1232 protected void testConcurrency() throws Exception {
1233 httpServer.setAuthentication("testuser", "testpass");
1234 auth = new AuthenticationBuilder()
1235 .addUsername("testuser")
1236 .addPassword("testpass")
1237 .build();
1238 newTransporter(httpServer.getHttpUrl());
1239 final AtomicReference<Throwable> error = new AtomicReference<>();
1240 Thread[] threads = new Thread[20];
1241 for (int i = 0; i < threads.length; i++) {
1242 final String path = "repo/file.txt?i=" + i;
1243 threads[i] = new Thread(() -> {
1244 try {
1245 for (int j = 0; j < 100; j++) {
1246 GetTask task = new GetTask(URI.create(path));
1247 transporter.get(task);
1248 assertEquals("test", task.getDataString());
1249 }
1250 } catch (Throwable t) {
1251 error.compareAndSet(null, t);
1252 System.err.println(path);
1253 t.printStackTrace();
1254 }
1255 });
1256 threads[i].setName("Task-" + i);
1257 }
1258 for (Thread thread : threads) {
1259 thread.start();
1260 }
1261 for (Thread thread : threads) {
1262 thread.join();
1263 }
1264 assertNull(error.get(), String.valueOf(error.get()));
1265 }
1266
1267 @Test
1268 @Timeout(10)
1269 protected void testConnectTimeout() throws Exception {
1270 session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, 100);
1271 int port = 1;
1272 newTransporter("http://localhost:" + port);
1273 try {
1274 transporter.get(new GetTask(URI.create("repo/file.txt")));
1275 fail("Expected error");
1276 } catch (Exception e) {
1277
1278 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1279 }
1280 }
1281
1282 @Test
1283 @Timeout(10)
1284 protected void testRequestTimeout() throws Exception {
1285 session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, 100);
1286 ServerSocket server = new ServerSocket(0);
1287 try (server) {
1288 newTransporter("http://localhost:" + server.getLocalPort());
1289 try {
1290 transporter.get(new GetTask(URI.create("repo/file.txt")));
1291 fail("Expected error");
1292 } catch (Exception e) {
1293 assertTrue(e.getClass().getSimpleName().contains("Timeout"));
1294 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1295 }
1296 }
1297 }
1298
1299 @Test
1300 protected void testUserAgent() throws Exception {
1301 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1302 newTransporter(httpServer.getHttpUrl());
1303 transporter.get(new GetTask(URI.create("repo/file.txt")));
1304 assertEquals(1, httpServer.getLogEntries().size());
1305 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1306 assertEquals("SomeTest/1.0", log.getRequestHeaders().get("User-Agent"));
1307 }
1308 }
1309
1310 @Test
1311 protected void testCustomHeaders() throws Exception {
1312 Map<String, String> headers = new HashMap<>();
1313 headers.put("User-Agent", "Custom/1.0");
1314 headers.put("X-CustomHeader", "Custom-Value");
1315 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1316 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1317 newTransporter(httpServer.getHttpUrl());
1318 transporter.get(new GetTask(URI.create("repo/file.txt")));
1319 assertEquals(1, httpServer.getLogEntries().size());
1320 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1321 for (Map.Entry<String, String> entry : headers.entrySet()) {
1322 assertEquals(entry.getValue(), log.getRequestHeaders().get(entry.getKey()), entry.getKey());
1323 }
1324 }
1325 }
1326
1327 @Test
1328 protected void testServerAuthScope_NotUsedForProxy() throws Exception {
1329 String username = "testuser", password = "testpass";
1330 httpServer.setProxyAuthentication(username, password);
1331 auth = new AuthenticationBuilder()
1332 .addUsername(username)
1333 .addPassword(password)
1334 .build();
1335 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1336 newTransporter("http://" + httpServer.getHost() + ":12/");
1337 try {
1338 transporter.get(new GetTask(URI.create("repo/file.txt")));
1339 fail("Server auth must not be used as proxy auth");
1340 } catch (HttpTransporterException e) {
1341 assertEquals(407, e.getStatusCode());
1342 } catch (IOException e) {
1343
1344 }
1345 }
1346
1347 @Test
1348 protected void testProxyAuthScope_NotUsedForServer() throws Exception {
1349 String username = "testuser", password = "testpass";
1350 httpServer.setAuthentication(username, password);
1351 Authentication auth = new AuthenticationBuilder()
1352 .addUsername(username)
1353 .addPassword(password)
1354 .build();
1355 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1356 newTransporter("http://" + httpServer.getHost() + ":12/");
1357 try {
1358 transporter.get(new GetTask(URI.create("repo/file.txt")));
1359 fail("Proxy auth must not be used as server auth");
1360 } catch (HttpTransporterException e) {
1361 assertEquals(401, e.getStatusCode());
1362 } catch (IOException e) {
1363
1364 }
1365 }
1366
1367 @Test
1368 protected void testAuthSchemeReuse() throws Exception {
1369 httpServer.setAuthentication("testuser", "testpass");
1370 httpServer.setProxyAuthentication("proxyuser", "proxypass");
1371 session.setCache(new DefaultRepositoryCache());
1372 auth = new AuthenticationBuilder()
1373 .addUsername("testuser")
1374 .addPassword("testpass")
1375 .build();
1376 Authentication auth = new AuthenticationBuilder()
1377 .addUsername("proxyuser")
1378 .addPassword("proxypass")
1379 .build();
1380 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1381 newTransporter("http://bad.localhost:1/");
1382 GetTask task = new GetTask(URI.create("repo/file.txt"));
1383 transporter.get(task);
1384 assertEquals("test", task.getDataString());
1385 assertEquals(3, httpServer.getLogEntries().size());
1386 httpServer.getLogEntries().clear();
1387 newTransporter("http://bad.localhost:1/");
1388 task = new GetTask(URI.create("repo/file.txt"));
1389 transporter.get(task);
1390 assertEquals("test", task.getDataString());
1391 assertEquals(1, httpServer.getLogEntries().size());
1392 assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Authorization"));
1393 assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Proxy-Authorization"));
1394 }
1395
1396 @Test
1397 protected void testAuthSchemePreemptive() throws Exception {
1398 httpServer.setAuthentication("testuser", "testpass");
1399 session.setCache(new DefaultRepositoryCache());
1400 auth = new AuthenticationBuilder()
1401 .addUsername("testuser")
1402 .addPassword("testpass")
1403 .build();
1404
1405 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, false);
1406 newTransporter(httpServer.getHttpUrl());
1407 GetTask task = new GetTask(URI.create("repo/file.txt"));
1408 transporter.get(task);
1409 assertEquals("test", task.getDataString());
1410
1411 assertEquals(2, httpServer.getLogEntries().size());
1412
1413 httpServer.getLogEntries().clear();
1414
1415 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1416 newTransporter(httpServer.getHttpUrl());
1417 task = new GetTask(URI.create("repo/file.txt"));
1418 transporter.get(task);
1419 assertEquals("test", task.getDataString());
1420
1421 assertEquals(
1422 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1423 }
1424
1425 @Test
1426 void testInit_BadProtocol() {
1427 assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void"));
1428 }
1429
1430 @Test
1431 void testInit_BadUrl() {
1432 assertThrows(NoTransporterException.class, () -> newTransporter("http://localhost:NaN"));
1433 }
1434
1435 @Test
1436 void testInit_CaseInsensitiveProtocol() throws Exception {
1437 newTransporter("http://localhost");
1438 newTransporter("HTTP://localhost");
1439 newTransporter("Http://localhost");
1440 newTransporter("https://localhost");
1441 newTransporter("HTTPS://localhost");
1442 newTransporter("HttpS://localhost");
1443 }
1444 }