View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.transport.http;
20  
21  import java.io.File;
22  import java.io.FileNotFoundException;
23  import java.net.ConnectException;
24  import java.net.ServerSocket;
25  import java.net.SocketTimeoutException;
26  import java.net.URI;
27  import java.nio.charset.StandardCharsets;
28  import java.util.HashMap;
29  import java.util.Map;
30  import java.util.concurrent.atomic.AtomicReference;
31  
32  import org.apache.http.NoHttpResponseException;
33  import org.apache.http.client.HttpResponseException;
34  import org.apache.http.conn.ConnectTimeoutException;
35  import org.apache.http.pool.ConnPoolControl;
36  import org.apache.http.pool.PoolStats;
37  import org.eclipse.aether.ConfigurationProperties;
38  import org.eclipse.aether.DefaultRepositoryCache;
39  import org.eclipse.aether.DefaultRepositorySystemSession;
40  import org.eclipse.aether.internal.test.util.TestFileUtils;
41  import org.eclipse.aether.internal.test.util.TestUtils;
42  import org.eclipse.aether.repository.Authentication;
43  import org.eclipse.aether.repository.Proxy;
44  import org.eclipse.aether.repository.RemoteRepository;
45  import org.eclipse.aether.spi.connector.transport.GetTask;
46  import org.eclipse.aether.spi.connector.transport.PeekTask;
47  import org.eclipse.aether.spi.connector.transport.PutTask;
48  import org.eclipse.aether.spi.connector.transport.Transporter;
49  import org.eclipse.aether.spi.connector.transport.TransporterFactory;
50  import org.eclipse.aether.transfer.NoTransporterException;
51  import org.eclipse.aether.transfer.TransferCancelledException;
52  import org.eclipse.aether.util.repository.AuthenticationBuilder;
53  import org.junit.jupiter.api.AfterEach;
54  import org.junit.jupiter.api.BeforeEach;
55  import org.junit.jupiter.api.Test;
56  import org.junit.jupiter.api.TestInfo;
57  import org.junit.jupiter.api.Timeout;
58  
59  import static org.junit.jupiter.api.Assertions.*;
60  
61  /**
62   */
63  public class HttpTransporterTest {
64  
65      static {
66          System.setProperty(
67                  "javax.net.ssl.trustStore", new File("src/test/resources/ssl/server-store").getAbsolutePath());
68          System.setProperty("javax.net.ssl.trustStorePassword", "server-pwd");
69          System.setProperty("javax.net.ssl.keyStore", new File("src/test/resources/ssl/client-store").getAbsolutePath());
70          System.setProperty("javax.net.ssl.keyStorePassword", "client-pwd");
71      }
72  
73      private DefaultRepositorySystemSession session;
74  
75      private TransporterFactory factory;
76  
77      private Transporter transporter;
78  
79      private File repoDir;
80  
81      private HttpServer httpServer;
82  
83      private Authentication auth;
84  
85      private Proxy proxy;
86  
87      private RemoteRepository newRepo(String url) {
88          return new RemoteRepository.Builder("test", "default", url)
89                  .setAuthentication(auth)
90                  .setProxy(proxy)
91                  .build();
92      }
93  
94      private void newTransporter(String url) throws Exception {
95          if (transporter != null) {
96              transporter.close();
97              transporter = null;
98          }
99          transporter = factory.newInstance(session, newRepo(url));
100     }
101 
102     private static final long OLD_FILE_TIMESTAMP = 160660800000L;
103 
104     @BeforeEach
105     void setUp(TestInfo testInfo) throws Exception {
106         System.out.println("=== " + testInfo.getDisplayName() + " ===");
107         session = TestUtils.newSession();
108         HashMap<String, ChecksumExtractor> extractors = new HashMap<>();
109         extractors.put(XChecksumChecksumExtractor.NAME, new XChecksumChecksumExtractor());
110         extractors.put(Nexus2ChecksumExtractor.NAME, new Nexus2ChecksumExtractor());
111         factory = new HttpTransporterFactory(extractors);
112         repoDir = TestFileUtils.createTempDir();
113         TestFileUtils.writeString(new File(repoDir, "file.txt"), "test");
114         TestFileUtils.writeString(new File(repoDir, "dir/file.txt"), "test");
115         TestFileUtils.writeString(new File(repoDir, "dir/oldFile.txt"), "oldTest", OLD_FILE_TIMESTAMP);
116         TestFileUtils.writeString(new File(repoDir, "empty.txt"), "");
117         TestFileUtils.writeString(new File(repoDir, "some space.txt"), "space");
118         File resumable = new File(repoDir, "resume.txt");
119         TestFileUtils.writeString(resumable, "resumable");
120         resumable.setLastModified(System.currentTimeMillis() - 90 * 1000);
121         httpServer = new HttpServer().setRepoDir(repoDir).start();
122         newTransporter(httpServer.getHttpUrl());
123     }
124 
125     @AfterEach
126     void tearDown() throws Exception {
127         if (transporter != null) {
128             transporter.close();
129             transporter = null;
130         }
131         if (httpServer != null) {
132             httpServer.stop();
133             httpServer = null;
134         }
135         factory = null;
136         session = null;
137     }
138 
139     @Test
140     void testClassify() {
141         assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException()));
142         assertEquals(Transporter.ERROR_OTHER, transporter.classify(new HttpResponseException(403, "Forbidden")));
143         assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new HttpResponseException(404, "Not Found")));
144     }
145 
146     @Test
147     void testPeek() throws Exception {
148         transporter.peek(new PeekTask(URI.create("repo/file.txt")));
149     }
150 
151     @Test
152     void testRetryHandler_defaultCount_positive() throws Exception {
153         httpServer.setConnectionsToClose(3);
154         transporter.peek(new PeekTask(URI.create("repo/file.txt")));
155     }
156 
157     @Test
158     void testRetryHandler_defaultCount_negative() throws Exception {
159         httpServer.setConnectionsToClose(4);
160         try {
161             transporter.peek(new PeekTask(URI.create("repo/file.txt")));
162             fail("Expected error");
163         } catch (NoHttpResponseException expected) {
164         }
165     }
166 
167     @Test
168     void testRetryHandler_explicitCount_positive() throws Exception {
169         session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 10);
170         newTransporter(httpServer.getHttpUrl());
171         httpServer.setConnectionsToClose(10);
172         transporter.peek(new PeekTask(URI.create("repo/file.txt")));
173     }
174 
175     @Test
176     void testRetryHandler_disabled() throws Exception {
177         session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 0);
178         newTransporter(httpServer.getHttpUrl());
179         httpServer.setConnectionsToClose(1);
180         try {
181             transporter.peek(new PeekTask(URI.create("repo/file.txt")));
182         } catch (NoHttpResponseException expected) {
183         }
184     }
185 
186     @Test
187     void testPeek_NotFound() throws Exception {
188         try {
189             transporter.peek(new PeekTask(URI.create("repo/missing.txt")));
190             fail("Expected error");
191         } catch (HttpResponseException e) {
192             assertEquals(404, e.getStatusCode());
193             assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
194         }
195     }
196 
197     @Test
198     void testPeek_Closed() throws Exception {
199         transporter.close();
200         try {
201             transporter.peek(new PeekTask(URI.create("repo/missing.txt")));
202             fail("Expected error");
203         } catch (IllegalStateException e) {
204             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
205         }
206     }
207 
208     @Test
209     void testPeek_Authenticated() throws Exception {
210         httpServer.setAuthentication("testuser", "testpass");
211         auth = new AuthenticationBuilder()
212                 .addUsername("testuser")
213                 .addPassword("testpass")
214                 .build();
215         newTransporter(httpServer.getHttpUrl());
216         transporter.peek(new PeekTask(URI.create("repo/file.txt")));
217     }
218 
219     @Test
220     void testPeek_Unauthenticated() throws Exception {
221         httpServer.setAuthentication("testuser", "testpass");
222         try {
223             transporter.peek(new PeekTask(URI.create("repo/file.txt")));
224             fail("Expected error");
225         } catch (HttpResponseException e) {
226             assertEquals(401, e.getStatusCode());
227             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
228         }
229     }
230 
231     @Test
232     void testPeek_ProxyAuthenticated() throws Exception {
233         httpServer.setProxyAuthentication("testuser", "testpass");
234         auth = new AuthenticationBuilder()
235                 .addUsername("testuser")
236                 .addPassword("testpass")
237                 .build();
238         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
239         newTransporter("http://bad.localhost:1/");
240         transporter.peek(new PeekTask(URI.create("repo/file.txt")));
241     }
242 
243     @Test
244     void testPeek_ProxyUnauthenticated() throws Exception {
245         httpServer.setProxyAuthentication("testuser", "testpass");
246         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
247         newTransporter("http://bad.localhost:1/");
248         try {
249             transporter.peek(new PeekTask(URI.create("repo/file.txt")));
250             fail("Expected error");
251         } catch (HttpResponseException e) {
252             assertEquals(407, e.getStatusCode());
253             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
254         }
255     }
256 
257     @Test
258     void testPeek_SSL() throws Exception {
259         httpServer.addSslConnector();
260         newTransporter(httpServer.getHttpsUrl());
261         transporter.peek(new PeekTask(URI.create("repo/file.txt")));
262     }
263 
264     @Test
265     void testPeek_Redirect() throws Exception {
266         httpServer.addSslConnector();
267         transporter.peek(new PeekTask(URI.create("redirect/file.txt")));
268         transporter.peek(new PeekTask(URI.create("redirect/file.txt?scheme=https")));
269     }
270 
271     @Test
272     void testGet_ToMemory() throws Exception {
273         RecordingTransportListener listener = new RecordingTransportListener();
274         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
275         transporter.get(task);
276         assertEquals("test", task.getDataString());
277         assertEquals(0L, listener.dataOffset);
278         assertEquals(4L, listener.dataLength);
279         assertEquals(1, listener.startedCount);
280         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
281         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
282     }
283 
284     @Test
285     void testGet_ToFile() throws Exception {
286         File file = TestFileUtils.createTempFile("failure");
287         RecordingTransportListener listener = new RecordingTransportListener();
288         GetTask task =
289                 new GetTask(URI.create("repo/file.txt")).setDataFile(file).setListener(listener);
290         transporter.get(task);
291         assertEquals("test", TestFileUtils.readString(file));
292         assertEquals(0L, listener.dataOffset);
293         assertEquals(4L, listener.dataLength);
294         assertEquals(1, listener.startedCount);
295         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
296         assertEquals("test", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
297     }
298 
299     @Test
300     void testGet_ToFileTimestamp() throws Exception {
301         File file = TestFileUtils.createTempFile("failure");
302         RecordingTransportListener listener = new RecordingTransportListener();
303         GetTask task = new GetTask(URI.create("repo/dir/oldFile.txt"))
304                 .setDataFile(file)
305                 .setListener(listener);
306         transporter.get(task);
307         assertEquals("oldTest", TestFileUtils.readString(file));
308         assertEquals(0L, listener.dataOffset);
309         assertEquals(7L, listener.dataLength);
310         assertEquals(1, listener.startedCount);
311         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
312         assertEquals("oldTest", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
313         assertEquals(file.lastModified(), OLD_FILE_TIMESTAMP);
314     }
315 
316     @Test
317     void testGet_EmptyResource() throws Exception {
318         File file = TestFileUtils.createTempFile("failure");
319         RecordingTransportListener listener = new RecordingTransportListener();
320         GetTask task =
321                 new GetTask(URI.create("repo/empty.txt")).setDataFile(file).setListener(listener);
322         transporter.get(task);
323         assertEquals("", TestFileUtils.readString(file));
324         assertEquals(0L, listener.dataOffset);
325         assertEquals(0L, listener.dataLength);
326         assertEquals(1, listener.startedCount);
327         assertEquals(0, listener.progressedCount);
328         assertEquals("", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
329     }
330 
331     @Test
332     void testGet_EncodedResourcePath() throws Exception {
333         GetTask task = new GetTask(URI.create("repo/some%20space.txt"));
334         transporter.get(task);
335         assertEquals("space", task.getDataString());
336     }
337 
338     @Test
339     void testGet_Authenticated() throws Exception {
340         httpServer.setAuthentication("testuser", "testpass");
341         auth = new AuthenticationBuilder()
342                 .addUsername("testuser")
343                 .addPassword("testpass")
344                 .build();
345         newTransporter(httpServer.getHttpUrl());
346         RecordingTransportListener listener = new RecordingTransportListener();
347         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
348         transporter.get(task);
349         assertEquals("test", task.getDataString());
350         assertEquals(0L, listener.dataOffset);
351         assertEquals(4L, listener.dataLength);
352         assertEquals(1, listener.startedCount);
353         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
354         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
355     }
356 
357     @Test
358     void testGet_Unauthenticated() throws Exception {
359         httpServer.setAuthentication("testuser", "testpass");
360         try {
361             transporter.get(new GetTask(URI.create("repo/file.txt")));
362             fail("Expected error");
363         } catch (HttpResponseException e) {
364             assertEquals(401, e.getStatusCode());
365             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
366         }
367     }
368 
369     @Test
370     void testGet_ProxyAuthenticated() throws Exception {
371         httpServer.setProxyAuthentication("testuser", "testpass");
372         Authentication auth = new AuthenticationBuilder()
373                 .addUsername("testuser")
374                 .addPassword("testpass")
375                 .build();
376         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
377         newTransporter("http://bad.localhost:1/");
378         RecordingTransportListener listener = new RecordingTransportListener();
379         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
380         transporter.get(task);
381         assertEquals("test", task.getDataString());
382         assertEquals(0L, listener.dataOffset);
383         assertEquals(4L, listener.dataLength);
384         assertEquals(1, listener.startedCount);
385         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
386         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
387     }
388 
389     @Test
390     void testGet_ProxyUnauthenticated() throws Exception {
391         httpServer.setProxyAuthentication("testuser", "testpass");
392         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
393         newTransporter("http://bad.localhost:1/");
394         try {
395             transporter.get(new GetTask(URI.create("repo/file.txt")));
396             fail("Expected error");
397         } catch (HttpResponseException e) {
398             assertEquals(407, e.getStatusCode());
399             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
400         }
401     }
402 
403     @Test
404     void testGet_SSL() throws Exception {
405         httpServer.addSslConnector();
406         newTransporter(httpServer.getHttpsUrl());
407         RecordingTransportListener listener = new RecordingTransportListener();
408         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
409         transporter.get(task);
410         assertEquals("test", task.getDataString());
411         assertEquals(0L, listener.dataOffset);
412         assertEquals(4L, listener.dataLength);
413         assertEquals(1, listener.startedCount);
414         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
415         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
416     }
417 
418     @Test
419     void testGet_HTTPS_Unknown_SecurityMode() throws Exception {
420         session.setConfigProperty("aether.connector.https.securityMode", "unknown");
421         httpServer.addSelfSignedSslConnector();
422         try {
423             newTransporter(httpServer.getHttpsUrl());
424             fail("Unsupported security mode");
425         } catch (IllegalArgumentException a) {
426             // good
427         }
428     }
429 
430     @Test
431     void testGet_HTTPS_Insecure_SecurityMode() throws Exception {
432         // here we use alternate server-store-selfigned key (as the key set it static initalizer is probably already
433         // used to init SSLContext/SSLSocketFactory/etc
434         session.setConfigProperty(
435                 "aether.connector.https.securityMode", ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE);
436         httpServer.addSelfSignedSslConnector();
437         newTransporter(httpServer.getHttpsUrl());
438         RecordingTransportListener listener = new RecordingTransportListener();
439         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
440         transporter.get(task);
441         assertEquals("test", task.getDataString());
442         assertEquals(0L, listener.dataOffset);
443         assertEquals(4L, listener.dataLength);
444         assertEquals(1, listener.startedCount);
445         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
446         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
447     }
448 
449     @Test
450     void testGet_WebDav() throws Exception {
451         httpServer.setWebDav(true);
452         RecordingTransportListener listener = new RecordingTransportListener();
453         GetTask task = new GetTask(URI.create("repo/dir/file.txt")).setListener(listener);
454         ((HttpTransporter) transporter).getState().setWebDav(true);
455         transporter.get(task);
456         assertEquals("test", task.getDataString());
457         assertEquals(0L, listener.dataOffset);
458         assertEquals(4L, listener.dataLength);
459         assertEquals(1, listener.startedCount);
460         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
461         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
462         assertEquals(
463                 1, httpServer.getLogEntries().size(), httpServer.getLogEntries().toString());
464     }
465 
466     @Test
467     void testGet_Redirect() throws Exception {
468         httpServer.addSslConnector();
469         RecordingTransportListener listener = new RecordingTransportListener();
470         GetTask task = new GetTask(URI.create("redirect/file.txt?scheme=https")).setListener(listener);
471         transporter.get(task);
472         assertEquals("test", task.getDataString());
473         assertEquals(0L, listener.dataOffset);
474         assertEquals(4L, listener.dataLength);
475         assertEquals(1, listener.startedCount);
476         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
477         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
478     }
479 
480     @Test
481     void testGet_Resume() throws Exception {
482         File file = TestFileUtils.createTempFile("re");
483         RecordingTransportListener listener = new RecordingTransportListener();
484         GetTask task = new GetTask(URI.create("repo/resume.txt"))
485                 .setDataFile(file, true)
486                 .setListener(listener);
487         transporter.get(task);
488         assertEquals("resumable", TestFileUtils.readString(file));
489         assertEquals(1L, listener.startedCount);
490         assertEquals(2L, listener.dataOffset);
491         assertEquals(9, listener.dataLength);
492         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
493         assertEquals("sumable", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
494     }
495 
496     @Test
497     void testGet_ResumeLocalContentsOutdated() throws Exception {
498         File file = TestFileUtils.createTempFile("re");
499         file.setLastModified(System.currentTimeMillis() - 5 * 60 * 1000);
500         RecordingTransportListener listener = new RecordingTransportListener();
501         GetTask task = new GetTask(URI.create("repo/resume.txt"))
502                 .setDataFile(file, true)
503                 .setListener(listener);
504         transporter.get(task);
505         assertEquals("resumable", TestFileUtils.readString(file));
506         assertEquals(1L, listener.startedCount);
507         assertEquals(0L, listener.dataOffset);
508         assertEquals(9, listener.dataLength);
509         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
510         assertEquals("resumable", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
511     }
512 
513     @Test
514     void testGet_ResumeRangesNotSupportedByServer() throws Exception {
515         httpServer.setRangeSupport(false);
516         File file = TestFileUtils.createTempFile("re");
517         RecordingTransportListener listener = new RecordingTransportListener();
518         GetTask task = new GetTask(URI.create("repo/resume.txt"))
519                 .setDataFile(file, true)
520                 .setListener(listener);
521         transporter.get(task);
522         assertEquals("resumable", TestFileUtils.readString(file));
523         assertEquals(1L, listener.startedCount);
524         assertEquals(0L, listener.dataOffset);
525         assertEquals(9, listener.dataLength);
526         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
527         assertEquals("resumable", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
528     }
529 
530     @Test
531     void testGet_Checksums_Nexus() throws Exception {
532         httpServer.setChecksumHeader(HttpServer.ChecksumHeader.NEXUS);
533         GetTask task = new GetTask(URI.create("repo/file.txt"));
534         transporter.get(task);
535         assertEquals("test", task.getDataString());
536         assertEquals(
537                 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
538     }
539 
540     @Test
541     void testGet_Checksums_XChecksum() throws Exception {
542         httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM);
543         GetTask task = new GetTask(URI.create("repo/file.txt"));
544         transporter.get(task);
545         assertEquals("test", task.getDataString());
546         assertEquals(
547                 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1"));
548     }
549 
550     @Test
551     void testGet_FileHandleLeak() throws Exception {
552         for (int i = 0; i < 100; i++) {
553             File file = TestFileUtils.createTempFile("failure");
554             transporter.get(new GetTask(URI.create("repo/file.txt")).setDataFile(file));
555             assertTrue(file.delete(), i + ", " + file.getAbsolutePath());
556         }
557     }
558 
559     @Test
560     void testGet_NotFound() throws Exception {
561         try {
562             transporter.get(new GetTask(URI.create("repo/missing.txt")));
563             fail("Expected error");
564         } catch (HttpResponseException e) {
565             assertEquals(404, e.getStatusCode());
566             assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
567         }
568     }
569 
570     @Test
571     void testGet_Closed() throws Exception {
572         transporter.close();
573         try {
574             transporter.get(new GetTask(URI.create("repo/file.txt")));
575             fail("Expected error");
576         } catch (IllegalStateException e) {
577             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
578         }
579     }
580 
581     @Test
582     void testGet_StartCancelled() throws Exception {
583         RecordingTransportListener listener = new RecordingTransportListener();
584         listener.cancelStart = true;
585         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
586         try {
587             transporter.get(task);
588             fail("Expected error");
589         } catch (TransferCancelledException e) {
590             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
591         }
592         assertEquals(0L, listener.dataOffset);
593         assertEquals(4L, listener.dataLength);
594         assertEquals(1, listener.startedCount);
595         assertEquals(0, listener.progressedCount);
596     }
597 
598     @Test
599     void testGet_ProgressCancelled() throws Exception {
600         RecordingTransportListener listener = new RecordingTransportListener();
601         listener.cancelProgress = true;
602         GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
603         try {
604             transporter.get(task);
605             fail("Expected error");
606         } catch (TransferCancelledException e) {
607             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
608         }
609         assertEquals(0L, listener.dataOffset);
610         assertEquals(4L, listener.dataLength);
611         assertEquals(1, listener.startedCount);
612         assertEquals(1, listener.progressedCount);
613     }
614 
615     @Test
616     void testPut_FromMemory() throws Exception {
617         RecordingTransportListener listener = new RecordingTransportListener();
618         PutTask task =
619                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
620         transporter.put(task);
621         assertEquals(0L, listener.dataOffset);
622         assertEquals(6L, listener.dataLength);
623         assertEquals(1, listener.startedCount);
624         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
625         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
626     }
627 
628     @Test
629     void testPut_FromFile() throws Exception {
630         File file = TestFileUtils.createTempFile("upload");
631         RecordingTransportListener listener = new RecordingTransportListener();
632         PutTask task =
633                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataFile(file);
634         transporter.put(task);
635         assertEquals(0L, listener.dataOffset);
636         assertEquals(6L, listener.dataLength);
637         assertEquals(1, listener.startedCount);
638         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
639         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
640     }
641 
642     @Test
643     void testPut_EmptyResource() throws Exception {
644         RecordingTransportListener listener = new RecordingTransportListener();
645         PutTask task = new PutTask(URI.create("repo/file.txt")).setListener(listener);
646         transporter.put(task);
647         assertEquals(0L, listener.dataOffset);
648         assertEquals(0L, listener.dataLength);
649         assertEquals(1, listener.startedCount);
650         assertEquals(0, listener.progressedCount);
651         assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt")));
652     }
653 
654     @Test
655     void testPut_EncodedResourcePath() throws Exception {
656         RecordingTransportListener listener = new RecordingTransportListener();
657         PutTask task = new PutTask(URI.create("repo/some%20space.txt"))
658                 .setListener(listener)
659                 .setDataString("OK");
660         transporter.put(task);
661         assertEquals(0L, listener.dataOffset);
662         assertEquals(2L, listener.dataLength);
663         assertEquals(1, listener.startedCount);
664         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
665         assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt")));
666     }
667 
668     @Test
669     void testPut_Authenticated_ExpectContinue() throws Exception {
670         httpServer.setAuthentication("testuser", "testpass");
671         auth = new AuthenticationBuilder()
672                 .addUsername("testuser")
673                 .addPassword("testpass")
674                 .build();
675         newTransporter(httpServer.getHttpUrl());
676         RecordingTransportListener listener = new RecordingTransportListener();
677         PutTask task =
678                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
679         transporter.put(task);
680         assertEquals(0L, listener.dataOffset);
681         assertEquals(6L, listener.dataLength);
682         assertEquals(1, listener.startedCount);
683         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
684         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
685     }
686 
687     @Test
688     void testPut_Authenticated_ExpectContinueBroken() throws Exception {
689         // this makes OPTIONS recover, and have only 1 PUT (startedCount=1 as OPTIONS is not counted)
690         session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true);
691         httpServer.setAuthentication("testuser", "testpass");
692         httpServer.setExpectSupport(HttpServer.ExpectContinue.BROKEN);
693         auth = new AuthenticationBuilder()
694                 .addUsername("testuser")
695                 .addPassword("testpass")
696                 .build();
697         newTransporter(httpServer.getHttpUrl());
698         RecordingTransportListener listener = new RecordingTransportListener();
699         PutTask task =
700                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
701         transporter.put(task);
702         assertEquals(0L, listener.dataOffset);
703         assertEquals(6L, listener.dataLength);
704         assertEquals(1, listener.startedCount);
705         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
706         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
707     }
708 
709     @Test
710     void testPut_Authenticated_ExpectContinueRejected() throws Exception {
711         httpServer.setAuthentication("testuser", "testpass");
712         httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
713         auth = new AuthenticationBuilder()
714                 .addUsername("testuser")
715                 .addPassword("testpass")
716                 .build();
717         newTransporter(httpServer.getHttpUrl());
718         RecordingTransportListener listener = new RecordingTransportListener();
719         PutTask task =
720                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
721         transporter.put(task);
722         assertEquals(0L, listener.dataOffset);
723         assertEquals(6L, listener.dataLength);
724         assertEquals(1, listener.startedCount);
725         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
726         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
727     }
728 
729     @Test
730     void testPut_Authenticated_ExpectContinueDisabled() throws Exception {
731         session.setConfigProperty(ConfigurationProperties.HTTP_EXPECT_CONTINUE, false);
732         httpServer.setAuthentication("testuser", "testpass");
733         httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); // if transport tries Expect/Continue explode
734         auth = new AuthenticationBuilder()
735                 .addUsername("testuser")
736                 .addPassword("testpass")
737                 .build();
738         newTransporter(httpServer.getHttpUrl());
739         RecordingTransportListener listener = new RecordingTransportListener();
740         PutTask task =
741                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
742         transporter.put(task);
743         assertEquals(0L, listener.dataOffset);
744         assertEquals(6L, listener.dataLength);
745         assertEquals(1, listener.startedCount); // w/ expectContinue enabled would have here 2
746         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
747         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
748     }
749 
750     @Test
751     void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() throws Exception {
752         Map<String, String> headers = new HashMap<>();
753         headers.put("Expect", "100-continue");
754         session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
755         httpServer.setAuthentication("testuser", "testpass");
756         httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL);
757         auth = new AuthenticationBuilder()
758                 .addUsername("testuser")
759                 .addPassword("testpass")
760                 .build();
761         newTransporter(httpServer.getHttpUrl());
762         RecordingTransportListener listener = new RecordingTransportListener();
763         PutTask task =
764                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
765         transporter.put(task);
766         assertEquals(0L, listener.dataOffset);
767         assertEquals(6L, listener.dataLength);
768         assertEquals(1, listener.startedCount);
769         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
770         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
771     }
772 
773     @Test
774     void testPut_Unauthenticated() throws Exception {
775         httpServer.setAuthentication("testuser", "testpass");
776         RecordingTransportListener listener = new RecordingTransportListener();
777         PutTask task =
778                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
779         try {
780             transporter.put(task);
781             fail("Expected error");
782         } catch (HttpResponseException e) {
783             assertEquals(401, e.getStatusCode());
784             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
785         }
786         assertEquals(0, listener.startedCount);
787         assertEquals(0, listener.progressedCount);
788     }
789 
790     @Test
791     void testPut_ProxyAuthenticated() throws Exception {
792         httpServer.setProxyAuthentication("testuser", "testpass");
793         Authentication auth = new AuthenticationBuilder()
794                 .addUsername("testuser")
795                 .addPassword("testpass")
796                 .build();
797         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
798         newTransporter("http://bad.localhost:1/");
799         RecordingTransportListener listener = new RecordingTransportListener();
800         PutTask task =
801                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
802         transporter.put(task);
803         assertEquals(0L, listener.dataOffset);
804         assertEquals(6L, listener.dataLength);
805         assertEquals(1, listener.startedCount);
806         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
807         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
808     }
809 
810     @Test
811     void testPut_ProxyUnauthenticated() throws Exception {
812         httpServer.setProxyAuthentication("testuser", "testpass");
813         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
814         newTransporter("http://bad.localhost:1/");
815         RecordingTransportListener listener = new RecordingTransportListener();
816         PutTask task =
817                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
818         try {
819             transporter.put(task);
820             fail("Expected error");
821         } catch (HttpResponseException e) {
822             assertEquals(407, e.getStatusCode());
823             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
824         }
825         assertEquals(0, listener.startedCount);
826         assertEquals(0, listener.progressedCount);
827     }
828 
829     @Test
830     void testPut_SSL() throws Exception {
831         httpServer.addSslConnector();
832         httpServer.setAuthentication("testuser", "testpass");
833         auth = new AuthenticationBuilder()
834                 .addUsername("testuser")
835                 .addPassword("testpass")
836                 .build();
837         newTransporter(httpServer.getHttpsUrl());
838         RecordingTransportListener listener = new RecordingTransportListener();
839         PutTask task =
840                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
841         transporter.put(task);
842         assertEquals(0L, listener.dataOffset);
843         assertEquals(6L, listener.dataLength);
844         assertEquals(1, listener.startedCount);
845         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
846         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt")));
847     }
848 
849     @Test
850     void testPut_WebDav() throws Exception {
851         httpServer.setWebDav(true);
852         session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true);
853         newTransporter(httpServer.getHttpUrl());
854 
855         RecordingTransportListener listener = new RecordingTransportListener();
856         PutTask task = new PutTask(URI.create("repo/dir1/dir2/file.txt"))
857                 .setListener(listener)
858                 .setDataString("upload");
859         transporter.put(task);
860         assertEquals(0L, listener.dataOffset);
861         assertEquals(6L, listener.dataLength);
862         assertEquals(1, listener.startedCount);
863         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
864         assertEquals("upload", TestFileUtils.readString(new File(repoDir, "dir1/dir2/file.txt")));
865 
866         assertEquals(5, httpServer.getLogEntries().size());
867         assertEquals("OPTIONS", httpServer.getLogEntries().get(0).method);
868         assertEquals("MKCOL", httpServer.getLogEntries().get(1).method);
869         assertEquals("/repo/dir1/dir2/", httpServer.getLogEntries().get(1).path);
870         assertEquals("MKCOL", httpServer.getLogEntries().get(2).method);
871         assertEquals("/repo/dir1/", httpServer.getLogEntries().get(2).path);
872         assertEquals("MKCOL", httpServer.getLogEntries().get(3).method);
873         assertEquals("/repo/dir1/dir2/", httpServer.getLogEntries().get(3).path);
874         assertEquals("PUT", httpServer.getLogEntries().get(4).method);
875     }
876 
877     @Test
878     void testPut_FileHandleLeak() throws Exception {
879         for (int i = 0; i < 100; i++) {
880             File src = TestFileUtils.createTempFile("upload");
881             File dst = new File(repoDir, "file.txt");
882             transporter.put(new PutTask(URI.create("repo/file.txt")).setDataFile(src));
883             assertTrue(src.delete(), i + ", " + src.getAbsolutePath());
884             assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath());
885         }
886     }
887 
888     @Test
889     void testPut_Closed() throws Exception {
890         transporter.close();
891         try {
892             transporter.put(new PutTask(URI.create("repo/missing.txt")));
893             fail("Expected error");
894         } catch (IllegalStateException e) {
895             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
896         }
897     }
898 
899     @Test
900     void testPut_StartCancelled() throws Exception {
901         RecordingTransportListener listener = new RecordingTransportListener();
902         listener.cancelStart = true;
903         PutTask task =
904                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
905         try {
906             transporter.put(task);
907             fail("Expected error");
908         } catch (TransferCancelledException e) {
909             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
910         }
911         assertEquals(0L, listener.dataOffset);
912         assertEquals(6L, listener.dataLength);
913         assertEquals(1, listener.startedCount);
914         assertEquals(0, listener.progressedCount);
915     }
916 
917     @Test
918     void testPut_ProgressCancelled() throws Exception {
919         RecordingTransportListener listener = new RecordingTransportListener();
920         listener.cancelProgress = true;
921         PutTask task =
922                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
923         try {
924             transporter.put(task);
925             fail("Expected error");
926         } catch (TransferCancelledException e) {
927             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
928         }
929         assertEquals(0L, listener.dataOffset);
930         assertEquals(6L, listener.dataLength);
931         assertEquals(1, listener.startedCount);
932         assertEquals(1, listener.progressedCount);
933     }
934 
935     @Test
936     void testGetPut_AuthCache() throws Exception {
937         httpServer.setAuthentication("testuser", "testpass");
938         auth = new AuthenticationBuilder()
939                 .addUsername("testuser")
940                 .addPassword("testpass")
941                 .build();
942         newTransporter(httpServer.getHttpUrl());
943         GetTask get = new GetTask(URI.create("repo/file.txt"));
944         transporter.get(get);
945         RecordingTransportListener listener = new RecordingTransportListener();
946         PutTask task =
947                 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload");
948         transporter.put(task);
949         assertEquals(1, listener.startedCount);
950     }
951 
952     @Test
953     void testPut_PreemptiveIsDefault() throws Exception {
954         httpServer.setAuthentication("testuser", "testpass");
955         auth = new AuthenticationBuilder()
956                 .addUsername("testuser")
957                 .addPassword("testpass")
958                 .build();
959         newTransporter(httpServer.getHttpUrl());
960         PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
961         transporter.put(task);
962         assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth
963     }
964 
965     @Test
966     void testPut_AuthCache() throws Exception {
967         session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH, false);
968         httpServer.setAuthentication("testuser", "testpass");
969         auth = new AuthenticationBuilder()
970                 .addUsername("testuser")
971                 .addPassword("testpass")
972                 .build();
973         newTransporter(httpServer.getHttpUrl());
974         PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
975         transporter.put(task);
976         assertEquals(2, httpServer.getLogEntries().size()); // put (challenged) + put w/ auth
977         httpServer.getLogEntries().clear();
978         task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
979         transporter.put(task);
980         assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth
981     }
982 
983     @Test
984     void testPut_AuthCache_Preemptive() throws Exception {
985         httpServer.setAuthentication("testuser", "testpass");
986         auth = new AuthenticationBuilder()
987                 .addUsername("testuser")
988                 .addPassword("testpass")
989                 .build();
990         session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
991         newTransporter(httpServer.getHttpUrl());
992         PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
993         transporter.put(task);
994         assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth
995         httpServer.getLogEntries().clear();
996         task = new PutTask(URI.create("repo/file.txt")).setDataString("upload");
997         transporter.put(task);
998         assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth
999     }
1000 
1001     @Test
1002     @Timeout(20)
1003     public void testConcurrency() throws Exception {
1004         httpServer.setAuthentication("testuser", "testpass");
1005         auth = new AuthenticationBuilder()
1006                 .addUsername("testuser")
1007                 .addPassword("testpass")
1008                 .build();
1009         newTransporter(httpServer.getHttpUrl());
1010         final AtomicReference<Throwable> error = new AtomicReference<>();
1011         Thread[] threads = new Thread[20];
1012         for (int i = 0; i < threads.length; i++) {
1013             final String path = "repo/file.txt?i=" + i;
1014             threads[i] = new Thread() {
1015                 @Override
1016                 public void run() {
1017                     try {
1018                         for (int j = 0; j < 100; j++) {
1019                             GetTask task = new GetTask(URI.create(path));
1020                             transporter.get(task);
1021                             assertEquals("test", task.getDataString());
1022                         }
1023                     } catch (Throwable t) {
1024                         error.compareAndSet(null, t);
1025                         System.err.println(path);
1026                         t.printStackTrace();
1027                     }
1028                 }
1029             };
1030             threads[i].setName("Task-" + i);
1031         }
1032         for (Thread thread : threads) {
1033             thread.start();
1034         }
1035         for (Thread thread : threads) {
1036             thread.join();
1037         }
1038         assertNull(error.get(), String.valueOf(error.get()));
1039     }
1040 
1041     @Test
1042     @Timeout(10)
1043     public void testConnectTimeout() throws Exception {
1044         session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, 100);
1045         int port = 1;
1046         newTransporter("http://localhost:" + port);
1047         try {
1048             transporter.get(new GetTask(URI.create("repo/file.txt")));
1049             fail("Expected error");
1050         } catch (ConnectTimeoutException | ConnectException e) {
1051             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1052         }
1053     }
1054 
1055     @Test
1056     @Timeout(10)
1057     public void testRequestTimeout() throws Exception {
1058         session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, 100);
1059         ServerSocket server = new ServerSocket(0);
1060         newTransporter("http://localhost:" + server.getLocalPort());
1061         try {
1062             try {
1063                 transporter.get(new GetTask(URI.create("repo/file.txt")));
1064                 fail("Expected error");
1065             } catch (SocketTimeoutException e) {
1066                 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
1067             }
1068         } finally {
1069             server.close();
1070         }
1071     }
1072 
1073     @Test
1074     void testUserAgent() throws Exception {
1075         session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1076         newTransporter(httpServer.getHttpUrl());
1077         transporter.get(new GetTask(URI.create("repo/file.txt")));
1078         assertEquals(1, httpServer.getLogEntries().size());
1079         for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1080             assertEquals("SomeTest/1.0", log.headers.get("User-Agent"));
1081         }
1082     }
1083 
1084     @Test
1085     void testCustomHeaders() throws Exception {
1086         Map<String, String> headers = new HashMap<>();
1087         headers.put("User-Agent", "Custom/1.0");
1088         headers.put("X-CustomHeader", "Custom-Value");
1089         session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0");
1090         session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers);
1091         newTransporter(httpServer.getHttpUrl());
1092         transporter.get(new GetTask(URI.create("repo/file.txt")));
1093         assertEquals(1, httpServer.getLogEntries().size());
1094         for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
1095             for (Map.Entry<String, String> entry : headers.entrySet()) {
1096                 assertEquals(entry.getValue(), log.headers.get(entry.getKey()), entry.getKey());
1097             }
1098         }
1099     }
1100 
1101     @Test
1102     void testServerAuthScope_NotUsedForProxy() throws Exception {
1103         String username = "testuser", password = "testpass";
1104         httpServer.setProxyAuthentication(username, password);
1105         auth = new AuthenticationBuilder()
1106                 .addUsername(username)
1107                 .addPassword(password)
1108                 .build();
1109         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort());
1110         newTransporter("http://" + httpServer.getHost() + ":12/");
1111         try {
1112             transporter.get(new GetTask(URI.create("repo/file.txt")));
1113             fail("Server auth must not be used as proxy auth");
1114         } catch (HttpResponseException e) {
1115             assertEquals(407, e.getStatusCode());
1116         }
1117     }
1118 
1119     @Test
1120     void testProxyAuthScope_NotUsedForServer() throws Exception {
1121         String username = "testuser", password = "testpass";
1122         httpServer.setAuthentication(username, password);
1123         Authentication auth = new AuthenticationBuilder()
1124                 .addUsername(username)
1125                 .addPassword(password)
1126                 .build();
1127         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1128         newTransporter("http://" + httpServer.getHost() + ":12/");
1129         try {
1130             transporter.get(new GetTask(URI.create("repo/file.txt")));
1131             fail("Proxy auth must not be used as server auth");
1132         } catch (HttpResponseException e) {
1133             assertEquals(401, e.getStatusCode());
1134         }
1135     }
1136 
1137     @Test
1138     void testAuthSchemeReuse() throws Exception {
1139         httpServer.setAuthentication("testuser", "testpass");
1140         httpServer.setProxyAuthentication("proxyuser", "proxypass");
1141         session.setCache(new DefaultRepositoryCache());
1142         auth = new AuthenticationBuilder()
1143                 .addUsername("testuser")
1144                 .addPassword("testpass")
1145                 .build();
1146         Authentication auth = new AuthenticationBuilder()
1147                 .addUsername("proxyuser")
1148                 .addPassword("proxypass")
1149                 .build();
1150         proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth);
1151         newTransporter("http://bad.localhost:1/");
1152         GetTask task = new GetTask(URI.create("repo/file.txt"));
1153         transporter.get(task);
1154         assertEquals("test", task.getDataString());
1155         assertEquals(3, httpServer.getLogEntries().size());
1156         httpServer.getLogEntries().clear();
1157         newTransporter("http://bad.localhost:1/");
1158         task = new GetTask(URI.create("repo/file.txt"));
1159         transporter.get(task);
1160         assertEquals("test", task.getDataString());
1161         assertEquals(1, httpServer.getLogEntries().size());
1162         assertNotNull(httpServer.getLogEntries().get(0).headers.get("Authorization"));
1163         assertNotNull(httpServer.getLogEntries().get(0).headers.get("Proxy-Authorization"));
1164     }
1165 
1166     @Test
1167     void testAuthSchemePreemptive() throws Exception {
1168         httpServer.setAuthentication("testuser", "testpass");
1169         session.setCache(new DefaultRepositoryCache());
1170         auth = new AuthenticationBuilder()
1171                 .addUsername("testuser")
1172                 .addPassword("testpass")
1173                 .build();
1174 
1175         session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, false);
1176         newTransporter(httpServer.getHttpUrl());
1177         GetTask task = new GetTask(URI.create("repo/file.txt"));
1178         transporter.get(task);
1179         assertEquals("test", task.getDataString());
1180         // there ARE challenge round-trips
1181         assertEquals(2, httpServer.getLogEntries().size());
1182 
1183         httpServer.getLogEntries().clear();
1184 
1185         session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true);
1186         newTransporter(httpServer.getHttpUrl());
1187         task = new GetTask(URI.create("repo/file.txt"));
1188         transporter.get(task);
1189         assertEquals("test", task.getDataString());
1190         // there are NO challenge round-trips, all goes through at first
1191         assertEquals(1, httpServer.getLogEntries().size());
1192     }
1193 
1194     @Test
1195     void testConnectionReuse() throws Exception {
1196         httpServer.addSslConnector();
1197         session.setCache(new DefaultRepositoryCache());
1198         for (int i = 0; i < 3; i++) {
1199             newTransporter(httpServer.getHttpsUrl());
1200             GetTask task = new GetTask(URI.create("repo/file.txt"));
1201             transporter.get(task);
1202             assertEquals("test", task.getDataString());
1203         }
1204         PoolStats stats = ((ConnPoolControl<?>)
1205                         ((HttpTransporter) transporter).getState().getConnectionManager())
1206                 .getTotalStats();
1207         assertEquals(1, stats.getAvailable(), stats.toString());
1208     }
1209 
1210     @Test
1211     void testConnectionNoReuse() throws Exception {
1212         httpServer.addSslConnector();
1213         session.setCache(new DefaultRepositoryCache());
1214         session.setConfigProperty(ConfigurationProperties.HTTP_REUSE_CONNECTIONS, false);
1215         for (int i = 0; i < 3; i++) {
1216             newTransporter(httpServer.getHttpsUrl());
1217             GetTask task = new GetTask(URI.create("repo/file.txt"));
1218             transporter.get(task);
1219             assertEquals("test", task.getDataString());
1220         }
1221         PoolStats stats = ((ConnPoolControl<?>)
1222                         ((HttpTransporter) transporter).getState().getConnectionManager())
1223                 .getTotalStats();
1224         assertEquals(0, stats.getAvailable(), stats.toString());
1225     }
1226 
1227     @Test
1228     void testInit_BadProtocol() {
1229         assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void"));
1230     }
1231 
1232     @Test
1233     void testInit_BadUrl() {
1234         assertThrows(NoTransporterException.class, () -> newTransporter("http://localhost:NaN"));
1235     }
1236 
1237     @Test
1238     void testInit_CaseInsensitiveProtocol() throws Exception {
1239         newTransporter("http://localhost");
1240         newTransporter("HTTP://localhost");
1241         newTransporter("Http://localhost");
1242         newTransporter("https://localhost");
1243         newTransporter("HTTPS://localhost");
1244         newTransporter("HttpS://localhost");
1245     }
1246 }