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