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