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 PutTask task =
861 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
862 transporter.put(task);
863 assertEquals(0L, listener.getDataOffset());
864 assertEquals(6L, listener.getDataLength());
865 assertEquals(1, listener.getStartedCount());
866 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
867 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
868 }
869
870 @Test
871 protected void testPut_FromFile() throws Exception {
872 File file = TestFileUtils.createTempFile("upload");
873 RecordingTransportListener listener = new RecordingTransportListener();
874 PutTask task =
875 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataPath(file.toPath());
876 transporter.put(task);
877 assertEquals(0L, listener.getDataOffset());
878 assertEquals(6L, listener.getDataLength());
879 assertEquals(1, listener.getStartedCount());
880 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
881 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
882 }
883
884 @Test
885 protected void testPut_EmptyResource() throws Exception {
886 RecordingTransportListener listener = new RecordingTransportListener();
887 PutTask task = new PutTask(URI.create("repo/file.txt")).setListener(listener);
888 transporter.put(task);
889 assertEquals(0L, listener.getDataOffset());
890 assertEquals(0L, listener.getDataLength());
891
892 assertTrue(
893 listener.getStartedCount() <= 1,
894 "The transport should be started at most once but was started " + listener.getStartedCount()
895 + " times");
896 assertEquals(0, listener.getProgressedCount());
897 assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt")));
898 }
899
900 @Test
901 protected void testPut_EncodedResourcePath() throws Exception {
902 RecordingTransportListener listener = new RecordingTransportListener();
903 PutTask task = new PutTask(URI.create("repo/some%20space.txt"))
904 .setListener(listener)
905 .setDataString("OK");
906 transporter.put(task);
907 assertEquals(0L, listener.getDataOffset());
908 assertEquals(2L, listener.getDataLength());
909 assertEquals(1, listener.getStartedCount());
910 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
911 assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt")));
912 }
913
914 @Test
915 protected void testPut_Authenticated_ExpectContinue() throws Exception {
916 httpServer.setAuthentication("testuser", "testpass");
917 auth = new AuthenticationBuilder()
918 .addUsername("testuser")
919 .addPassword("testpass")
920 .build();
921 newTransporter(httpServer.getHttpUrl());
922 RecordingTransportListener listener = new RecordingTransportListener();
923 PutTask task =
924 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
925 transporter.put(task);
926 assertEquals(0L, listener.getDataOffset());
927 assertEquals(6L, listener.getDataLength());
928 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
929 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
930 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
931 }
932
933 @Test
934 protected void testPut_Authenticated_ExpectContinueBroken() throws Exception {
935
936 session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true);
937 httpServer.setAuthentication("testuser", "testpass");
938 httpServer.setExpectSupport(HttpServer.ExpectContinue.BROKEN);
939 auth = new AuthenticationBuilder()
940 .addUsername("testuser")
941 .addPassword("testpass")
942 .build();
943 newTransporter(httpServer.getHttpUrl());
944 RecordingTransportListener listener = new RecordingTransportListener();
945 PutTask task =
946 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
947 transporter.put(task);
948 assertEquals(0L, listener.getDataOffset());
949 assertEquals(6L, listener.getDataLength());
950 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
951 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
952 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
953 }
954
955 @Test
956 protected void testPut_Authenticated_ExpectContinueRejected() throws Exception {
957 httpServer.setAuthentication("testuser", "testpass");
958 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
959 auth = new AuthenticationBuilder()
960 .addUsername("testuser")
961 .addPassword("testpass")
962 .build();
963 newTransporter(httpServer.getHttpUrl());
964 RecordingTransportListener listener = new RecordingTransportListener();
965 PutTask task =
966 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
967 transporter.put(task);
968 assertEquals(0L, listener.getDataOffset());
969 assertEquals(6L, listener.getDataLength());
970 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
971 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
972 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
973 }
974
975 @Test
976 protected void testPut_Authenticated_ExpectContinueDisabled() throws Exception {
977 session.setConfigProperty(ConfigurationProperties.HTTP_EXPECT_CONTINUE, false);
978 httpServer.setAuthentication("testuser", "testpass");
979 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
980 auth = new AuthenticationBuilder()
981 .addUsername("testuser")
982 .addPassword("testpass")
983 .build();
984 newTransporter(httpServer.getHttpUrl());
985 RecordingTransportListener listener = new RecordingTransportListener();
986 PutTask task =
987 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
988 transporter.put(task);
989 assertEquals(0L, listener.getDataOffset());
990 assertEquals(6L, listener.getDataLength());
991 assertEquals(
992 supportsPreemptiveAuth() ? 1 : 2,
993 listener.getStartedCount());
994 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
995 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
996 }
997
998 @Test
999 protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() throws Exception {
1000 Map<String, String> headers = new HashMap<>();
1001 headers.put("Expect", "100-continue");
1002 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1003 httpServer.setAuthentication("testuser", "testpass");
1004 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
1005 auth = new AuthenticationBuilder()
1006 .addUsername("testuser")
1007 .addPassword("testpass")
1008 .build();
1009 newTransporter(httpServer.getHttpUrl());
1010 RecordingTransportListener listener = new RecordingTransportListener();
1011 PutTask task =
1012 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1013 transporter.put(task);
1014 assertEquals(0L, listener.getDataOffset());
1015 assertEquals(6L, listener.getDataLength());
1016 assertEquals(1, listener.getStartedCount());
1017 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1018 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1019 }
1020
1021 @Test
1022 protected void testPut_Unauthenticated() throws Exception {
1023 httpServer.setAuthentication("testuser", "testpass");
1024 RecordingTransportListener listener = new RecordingTransportListener();
1025 PutTask task =
1026 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1027 try {
1028 transporter.put(task);
1029 fail("Expected error");
1030 } catch (HttpTransporterException e) {
1031 assertEquals(401, e.getStatusCode());
1032 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1033 }
1034 assertEquals(0, listener.getStartedCount());
1035 assertEquals(0, listener.getProgressedCount());
1036 }
1037
1038 @Test
1039 protected void testPut_ProxyAuthenticated() throws Exception {
1040 httpServer.setProxyAuthentication("testuser", "testpass");
1041 Authentication auth = new AuthenticationBuilder()
1042 .addUsername("testuser")
1043 .addPassword("testpass")
1044 .build();
1045 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1046 newTransporter("http://bad.localhost:1/");
1047 RecordingTransportListener listener = new RecordingTransportListener();
1048 PutTask task =
1049 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1050 transporter.put(task);
1051 assertEquals(0L, listener.getDataOffset());
1052 assertEquals(6L, listener.getDataLength());
1053 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1054 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1055 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1056 }
1057
1058 @Test
1059 protected void testPut_ProxyUnauthenticated() throws Exception {
1060 httpServer.setProxyAuthentication("testuser", "testpass");
1061 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1062 newTransporter("http://bad.localhost:1/");
1063 RecordingTransportListener listener = new RecordingTransportListener();
1064 PutTask task =
1065 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1066 try {
1067 transporter.put(task);
1068 fail("Expected error");
1069 } catch (HttpTransporterException e) {
1070 assertEquals(407, e.getStatusCode());
1071 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1072 }
1073 assertEquals(0, listener.getStartedCount());
1074 assertEquals(0, listener.getProgressedCount());
1075 }
1076
1077 @Test
1078 protected void testPut_SSL() throws Exception {
1079 httpServer.addSslConnector();
1080 httpServer.setAuthentication("testuser", "testpass");
1081 auth = new AuthenticationBuilder()
1082 .addUsername("testuser")
1083 .addPassword("testpass")
1084 .build();
1085 newTransporter(httpServer.getHttpsUrl());
1086 RecordingTransportListener listener = new RecordingTransportListener();
1087 PutTask task =
1088 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1089 transporter.put(task);
1090 assertEquals(0L, listener.getDataOffset());
1091 assertEquals(6L, listener.getDataLength());
1092 assertEquals(supportsPreemptiveAuth() ? 1 : 2, listener.getStartedCount());
1093 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount());
1094 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
1095 }
1096
1097 @Test
1098 protected void testPut_FileHandleLeak() throws Exception {
1099 for (int i = 0; i < 100; i++) {
1100 File src = TestFileUtils.createTempFile("upload");
1101 File dst = new File(repoDir, "file.txt");
1102 transporter.put(new PutTask(URI.create("repo/file.txt")).setDataPath(src.toPath()));
1103 assertTrue(src.delete(), i + ", " + src.getAbsolutePath());
1104 assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath());
1105 }
1106 }
1107
1108 @Test
1109 protected void testPut_Closed() throws Exception {
1110 transporter.close();
1111 try {
1112 transporter.put(new PutTask(URI.create("repo/missing.txt")));
1113 fail("Expected error");
1114 } catch (IllegalStateException e) {
1115 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1116 }
1117 }
1118
1119 @Test
1120 protected void testPut_StartCancelled() throws Exception {
1121 RecordingTransportListener listener = new RecordingTransportListener();
1122 listener.cancelStart();
1123 PutTask task =
1124 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1125 try {
1126 transporter.put(task);
1127 fail("Expected error");
1128 } catch (TransferCancelledException e) {
1129 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1130 }
1131 assertEquals(0L, listener.getDataOffset());
1132 assertEquals(6L, listener.getDataLength());
1133 assertEquals(1, listener.getStartedCount());
1134 assertEquals(0, listener.getProgressedCount());
1135 }
1136
1137 @Test
1138 protected void testPut_ProgressCancelled() throws Exception {
1139 RecordingTransportListener listener = new RecordingTransportListener();
1140 listener.cancelProgress();
1141 PutTask task =
1142 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1143 try {
1144 transporter.put(task);
1145 fail("Expected error");
1146 } catch (TransferCancelledException e) {
1147 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1148 }
1149 assertEquals(0L, listener.getDataOffset());
1150 assertEquals(6L, listener.getDataLength());
1151 assertEquals(1, listener.getStartedCount());
1152 assertEquals(1, listener.getProgressedCount());
1153 }
1154
1155 @Test
1156 protected void testGetPut_AuthCache() throws Exception {
1157 httpServer.setAuthentication("testuser", "testpass");
1158 auth = new AuthenticationBuilder()
1159 .addUsername("testuser")
1160 .addPassword("testpass")
1161 .build();
1162 newTransporter(httpServer.getHttpUrl());
1163 GetTask get = new GetTask(URI.create("repo/file.txt"));
1164 transporter.get(get);
1165 RecordingTransportListener listener = new RecordingTransportListener();
1166 PutTask task =
1167 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
1168 transporter.put(task);
1169 assertEquals(1, listener.getStartedCount());
1170 }
1171
1172 @Test
1173 protected void testPut_PreemptiveIsDefault() throws Exception {
1174 httpServer.setAuthentication("testuser", "testpass");
1175 auth = new AuthenticationBuilder()
1176 .addUsername("testuser")
1177 .addPassword("testpass")
1178 .build();
1179 newTransporter(httpServer.getHttpUrl());
1180 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1181 transporter.put(task);
1182 assertEquals(
1183 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1184 }
1185
1186 @Test
1187 protected void testPut_AuthCache() throws Exception {
1188 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH, false);
1189 httpServer.setAuthentication("testuser", "testpass");
1190 auth = new AuthenticationBuilder()
1191 .addUsername("testuser")
1192 .addPassword("testpass")
1193 .build();
1194 newTransporter(httpServer.getHttpUrl());
1195 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1196 transporter.put(task);
1197 assertEquals(2, httpServer.getLogEntries().size());
1198 httpServer.getLogEntries().clear();
1199 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1200 transporter.put(task);
1201 assertEquals(1, httpServer.getLogEntries().size());
1202 }
1203
1204 @Test
1205 protected void testPut_AuthCache_Preemptive() throws Exception {
1206 httpServer.setAuthentication("testuser", "testpass");
1207 auth = new AuthenticationBuilder()
1208 .addUsername("testuser")
1209 .addPassword("testpass")
1210 .build();
1211 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1212 newTransporter(httpServer.getHttpUrl());
1213 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1214 transporter.put(task);
1215 assertEquals(
1216 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1217 httpServer.getLogEntries().clear();
1218 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
1219 transporter.put(task);
1220 assertEquals(1, httpServer.getLogEntries().size());
1221 }
1222
1223 @Test
1224 @Timeout(20)
1225 protected void testConcurrency() throws Exception {
1226 httpServer.setAuthentication("testuser", "testpass");
1227 auth = new AuthenticationBuilder()
1228 .addUsername("testuser")
1229 .addPassword("testpass")
1230 .build();
1231 newTransporter(httpServer.getHttpUrl());
1232 final AtomicReference<Throwable> error = new AtomicReference<>();
1233 Thread[] threads = new Thread[20];
1234 for (int i = 0; i < threads.length; i++) {
1235 final String path = "repo/file.txt?i=" + i;
1236 threads[i] = new Thread(() -> {
1237 try {
1238 for (int j = 0; j < 100; j++) {
1239 GetTask task = new GetTask(URI.create(path));
1240 transporter.get(task);
1241 assertEquals("test", task.getDataString());
1242 }
1243 } catch (Throwable t) {
1244 error.compareAndSet(null, t);
1245 System.err.println(path);
1246 t.printStackTrace();
1247 }
1248 });
1249 threads[i].setName("Task-" + i);
1250 }
1251 for (Thread thread : threads) {
1252 thread.start();
1253 }
1254 for (Thread thread : threads) {
1255 thread.join();
1256 }
1257 assertNull(error.get(), String.valueOf(error.get()));
1258 }
1259
1260 @Test
1261 @Timeout(10)
1262 protected void testConnectTimeout() throws Exception {
1263 session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, 100);
1264 int port = 1;
1265 newTransporter("http://localhost:" + port);
1266 try {
1267 transporter.get(new GetTask(URI.create("repo/file.txt")));
1268 fail("Expected error");
1269 } catch (Exception e) {
1270
1271 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1272 }
1273 }
1274
1275 @Test
1276 @Timeout(10)
1277 protected void testRequestTimeout() throws Exception {
1278 session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, 100);
1279 ServerSocket server = new ServerSocket(0);
1280 try (server) {
1281 newTransporter("http://localhost:" + server.getLocalPort());
1282 try {
1283 transporter.get(new GetTask(URI.create("repo/file.txt")));
1284 fail("Expected error");
1285 } catch (Exception e) {
1286 assertTrue(e.getClass().getSimpleName().contains("Timeout"));
1287 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1288 }
1289 }
1290 }
1291
1292 @Test
1293 protected void testUserAgent() throws Exception {
1294 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1295 newTransporter(httpServer.getHttpUrl());
1296 transporter.get(new GetTask(URI.create("repo/file.txt")));
1297 assertEquals(1, httpServer.getLogEntries().size());
1298 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1299 assertEquals("SomeTest/1.0", log.getRequestHeaders().get("User-Agent"));
1300 }
1301 }
1302
1303 @Test
1304 protected void testCustomHeaders() throws Exception {
1305 Map<String, String> headers = new HashMap<>();
1306 headers.put("User-Agent", "Custom/1.0");
1307 headers.put("X-CustomHeader", "Custom-Value");
1308 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1309 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1310 newTransporter(httpServer.getHttpUrl());
1311 transporter.get(new GetTask(URI.create("repo/file.txt")));
1312 assertEquals(1, httpServer.getLogEntries().size());
1313 for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1314 for (Map.Entry<String, String> entry : headers.entrySet()) {
1315 assertEquals(entry.getValue(), log.getRequestHeaders().get(entry.getKey()), entry.getKey());
1316 }
1317 }
1318 }
1319
1320 @Test
1321 protected void testServerAuthScope_NotUsedForProxy() throws Exception {
1322 String username = "testuser", password = "testpass";
1323 httpServer.setProxyAuthentication(username, password);
1324 auth = new AuthenticationBuilder()
1325 .addUsername(username)
1326 .addPassword(password)
1327 .build();
1328 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1329 newTransporter("http://" + httpServer.getHost() + ":12/");
1330 try {
1331 transporter.get(new GetTask(URI.create("repo/file.txt")));
1332 fail("Server auth must not be used as proxy auth");
1333 } catch (HttpTransporterException e) {
1334 assertEquals(407, e.getStatusCode());
1335 } catch (IOException e) {
1336
1337 }
1338 }
1339
1340 @Test
1341 protected void testProxyAuthScope_NotUsedForServer() throws Exception {
1342 String username = "testuser", password = "testpass";
1343 httpServer.setAuthentication(username, password);
1344 Authentication auth = new AuthenticationBuilder()
1345 .addUsername(username)
1346 .addPassword(password)
1347 .build();
1348 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1349 newTransporter("http://" + httpServer.getHost() + ":12/");
1350 try {
1351 transporter.get(new GetTask(URI.create("repo/file.txt")));
1352 fail("Proxy auth must not be used as server auth");
1353 } catch (HttpTransporterException e) {
1354 assertEquals(401, e.getStatusCode());
1355 } catch (IOException e) {
1356
1357 }
1358 }
1359
1360 @Test
1361 protected void testAuthSchemeReuse() throws Exception {
1362 httpServer.setAuthentication("testuser", "testpass");
1363 httpServer.setProxyAuthentication("proxyuser", "proxypass");
1364 session.setCache(new DefaultRepositoryCache());
1365 auth = new AuthenticationBuilder()
1366 .addUsername("testuser")
1367 .addPassword("testpass")
1368 .build();
1369 Authentication auth = new AuthenticationBuilder()
1370 .addUsername("proxyuser")
1371 .addPassword("proxypass")
1372 .build();
1373 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1374 newTransporter("http://bad.localhost:1/");
1375 GetTask task = new GetTask(URI.create("repo/file.txt"));
1376 transporter.get(task);
1377 assertEquals("test", task.getDataString());
1378 assertEquals(3, httpServer.getLogEntries().size());
1379 httpServer.getLogEntries().clear();
1380 newTransporter("http://bad.localhost:1/");
1381 task = new GetTask(URI.create("repo/file.txt"));
1382 transporter.get(task);
1383 assertEquals("test", task.getDataString());
1384 assertEquals(1, httpServer.getLogEntries().size());
1385 assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Authorization"));
1386 assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Proxy-Authorization"));
1387 }
1388
1389 @Test
1390 protected void testAuthSchemePreemptive() throws Exception {
1391 httpServer.setAuthentication("testuser", "testpass");
1392 session.setCache(new DefaultRepositoryCache());
1393 auth = new AuthenticationBuilder()
1394 .addUsername("testuser")
1395 .addPassword("testpass")
1396 .build();
1397
1398 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, false);
1399 newTransporter(httpServer.getHttpUrl());
1400 GetTask task = new GetTask(URI.create("repo/file.txt"));
1401 transporter.get(task);
1402 assertEquals("test", task.getDataString());
1403
1404 assertEquals(2, httpServer.getLogEntries().size());
1405
1406 httpServer.getLogEntries().clear();
1407
1408 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1409 newTransporter(httpServer.getHttpUrl());
1410 task = new GetTask(URI.create("repo/file.txt"));
1411 transporter.get(task);
1412 assertEquals("test", task.getDataString());
1413
1414 assertEquals(
1415 supportsPreemptiveAuth() ? 1 : 2, httpServer.getLogEntries().size());
1416 }
1417
1418 @Test
1419 void testInit_BadProtocol() {
1420 assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void"));
1421 }
1422
1423 @Test
1424 void testInit_BadUrl() {
1425 assertThrows(NoTransporterException.class, () -> newTransporter("http://localhost:NaN"));
1426 }
1427
1428 @Test
1429 void testInit_CaseInsensitiveProtocol() throws Exception {
1430 newTransporter("http://localhost");
1431 newTransporter("HTTP://localhost");
1432 newTransporter("Http://localhost");
1433 newTransporter("https://localhost");
1434 newTransporter("HTTPS://localhost");
1435 newTransporter("HttpS://localhost");
1436 }
1437 }