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