1 package org.eclipse.aether.transport.http;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.nio.charset.StandardCharsets;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.concurrent.atomic.AtomicReference;
34
35 import org.apache.http.client.HttpResponseException;
36 import org.apache.http.conn.ConnectTimeoutException;
37 import org.apache.http.pool.ConnPoolControl;
38 import org.apache.http.pool.PoolStats;
39 import org.eclipse.aether.ConfigurationProperties;
40 import org.eclipse.aether.DefaultRepositoryCache;
41 import org.eclipse.aether.DefaultRepositorySystemSession;
42 import org.eclipse.aether.internal.test.util.TestFileUtils;
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( );
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( 0L, listener.dataOffset );
277 assertEquals( 4L, listener.dataLength );
278 assertEquals( 1, listener.startedCount );
279 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
280 assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
293 assertEquals( 4L, listener.dataLength );
294 assertEquals( 1, listener.startedCount );
295 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
296 assertEquals( "test", new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
309 assertEquals( 0L, listener.dataLength );
310 assertEquals( 1, listener.startedCount );
311 assertEquals( 0, listener.progressedCount );
312 assertEquals( "", new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
336 assertEquals( 4L, listener.dataLength );
337 assertEquals( 1, listener.startedCount );
338 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
339 assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
372 assertEquals( 4L, listener.dataLength );
373 assertEquals( 1, listener.startedCount );
374 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
375 assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
408 assertEquals( 4L, listener.dataLength );
409 assertEquals( 1, listener.startedCount );
410 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
411 assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
425 assertEquals( 4L, listener.dataLength );
426 assertEquals( 1, listener.startedCount );
427 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
428 assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
442 assertEquals( 4L, listener.dataLength );
443 assertEquals( 1, listener.startedCount );
444 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
445 assertEquals( task.getDataString(), new String( listener.baos.toByteArray(), StandardCharsets.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( 1L, listener.startedCount );
458 assertEquals( 2L, listener.dataOffset );
459 assertEquals( 9, listener.dataLength );
460 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
461 assertEquals( "sumable", new String( listener.baos.toByteArray(), StandardCharsets.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( 1L, listener.startedCount );
475 assertEquals( 0L, listener.dataOffset );
476 assertEquals( 9, listener.dataLength );
477 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
478 assertEquals( "resumable", new String( listener.baos.toByteArray(), StandardCharsets.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( 1L, listener.startedCount );
492 assertEquals( 0L, listener.dataOffset );
493 assertEquals( 9, listener.dataLength );
494 assertTrue( "Count: " + listener.progressedCount, listener.progressedCount > 0 );
495 assertEquals( "resumable", new String( listener.baos.toByteArray(), StandardCharsets.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( 0L, listener.dataOffset );
570 assertEquals( 4L, 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( 0L, listener.dataOffset );
592 assertEquals( 4L, 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( 0L, listener.dataOffset );
605 assertEquals( 6L, 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( 0L, listener.dataOffset );
620 assertEquals( 6L, 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( 0L, listener.dataOffset );
634 assertEquals( 0L, 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( 0L, listener.dataOffset );
649 assertEquals( 2L, 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( 0L, listener.dataOffset );
666 assertEquals( 6L, 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( 0L, listener.dataOffset );
684 assertEquals( 6L, 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( 0L, listener.dataOffset );
702 assertEquals( 6L, 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<>();
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( 0L, listener.dataOffset );
723 assertEquals( 6L, 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( 0L, listener.dataOffset );
762 assertEquals( 6L, 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( 0L, listener.dataOffset );
803 assertEquals( 6L, 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( 0L, listener.dataOffset );
819 assertEquals( 6L, 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( 0L, listener.dataOffset );
882 assertEquals( 6L, 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( 0L, listener.dataOffset );
904 assertEquals( 6L, 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 = 20000L )
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<>();
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 = 1000L )
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 | ConnectException e )
984 {
985 assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
986 }
987 }
988
989 @Test( timeout = 1000L )
990 public void testRequestTimeout()
991 throws Exception
992 {
993 session.setConfigProperty( ConfigurationProperties.REQUEST_TIMEOUT, 100 );
994 ServerSocket server = new ServerSocket( 0 );
995 newTransporter( "http://localhost:" + server.getLocalPort() );
996 try
997 {
998 try
999 {
1000 transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) );
1001 fail( "Expected error" );
1002 }
1003 catch ( SocketTimeoutException e )
1004 {
1005 assertEquals( Transporter.ERROR_OTHER, transporter.classify( e ) );
1006 }
1007 }
1008 finally
1009 {
1010 server.close();
1011 }
1012 }
1013
1014 @Test
1015 public void testUserAgent()
1016 throws Exception
1017 {
1018 session.setConfigProperty( ConfigurationProperties.USER_AGENT, "SomeTest/1.0" );
1019 newTransporter( httpServer.getHttpUrl() );
1020 transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) );
1021 assertEquals( 1, httpServer.getLogEntries().size() );
1022 for ( HttpServer.LogEntry log : httpServer.getLogEntries() )
1023 {
1024 assertEquals( "SomeTest/1.0", log.headers.get( "User-Agent" ) );
1025 }
1026 }
1027
1028 @Test
1029 public void testCustomHeaders()
1030 throws Exception
1031 {
1032 Map<String, String> headers = new HashMap<>();
1033 headers.put( "User-Agent", "Custom/1.0" );
1034 headers.put( "X-CustomHeader", "Custom-Value" );
1035 session.setConfigProperty( ConfigurationProperties.USER_AGENT, "SomeTest/1.0" );
1036 session.setConfigProperty( ConfigurationProperties.HTTP_HEADERS + ".test", headers );
1037 newTransporter( httpServer.getHttpUrl() );
1038 transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) );
1039 assertEquals( 1, httpServer.getLogEntries().size() );
1040 for ( HttpServer.LogEntry log : httpServer.getLogEntries() )
1041 {
1042 for ( Map.Entry<String, String> entry : headers.entrySet() )
1043 {
1044 assertEquals( entry.getKey(), entry.getValue(), log.headers.get( entry.getKey() ) );
1045 }
1046 }
1047 }
1048
1049 @Test
1050 public void testServerAuthScope_NotUsedForProxy()
1051 throws Exception
1052 {
1053 String username = "testuser", password = "testpass";
1054 httpServer.setProxyAuthentication( username, password );
1055 auth = new AuthenticationBuilder().addUsername( username ).addPassword( password ).build();
1056 proxy = new Proxy( Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort() );
1057 newTransporter( "http://" + httpServer.getHost() + ":12/" );
1058 try
1059 {
1060 transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) );
1061 fail( "Server auth must not be used as proxy auth" );
1062 }
1063 catch ( HttpResponseException e )
1064 {
1065 assertEquals( 407, e.getStatusCode() );
1066 }
1067 }
1068
1069 @Test
1070 public void testProxyAuthScope_NotUsedForServer()
1071 throws Exception
1072 {
1073 String username = "testuser", password = "testpass";
1074 httpServer.setAuthentication( username, password );
1075 Authentication auth = new AuthenticationBuilder().addUsername( username ).addPassword( password ).build();
1076 proxy = new Proxy( Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth );
1077 newTransporter( "http://" + httpServer.getHost() + ":12/" );
1078 try
1079 {
1080 transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) );
1081 fail( "Proxy auth must not be used as server auth" );
1082 }
1083 catch ( HttpResponseException e )
1084 {
1085 assertEquals( 401, e.getStatusCode() );
1086 }
1087 }
1088
1089 @Test
1090 public void testAuthSchemeReuse()
1091 throws Exception
1092 {
1093 httpServer.setAuthentication( "testuser", "testpass" );
1094 httpServer.setProxyAuthentication( "proxyuser", "proxypass" );
1095 session.setCache( new DefaultRepositoryCache() );
1096 auth = new AuthenticationBuilder().addUsername( "testuser" ).addPassword( "testpass" ).build();
1097 Authentication auth = new AuthenticationBuilder().addUsername( "proxyuser" ).addPassword( "proxypass" ).build();
1098 proxy = new Proxy( Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth );
1099 newTransporter( "http://bad.localhost:1/" );
1100 GetTask task = new GetTask( URI.create( "repo/file.txt" ) );
1101 transporter.get( task );
1102 assertEquals( "test", task.getDataString() );
1103 assertEquals( 3, httpServer.getLogEntries().size() );
1104 httpServer.getLogEntries().clear();
1105 newTransporter( "http://bad.localhost:1/" );
1106 task = new GetTask( URI.create( "repo/file.txt" ) );
1107 transporter.get( task );
1108 assertEquals( "test", task.getDataString() );
1109 assertEquals( 1, httpServer.getLogEntries().size() );
1110 assertNotNull( httpServer.getLogEntries().get( 0 ).headers.get( "Authorization" ) );
1111 assertNotNull( httpServer.getLogEntries().get( 0 ).headers.get( "Proxy-Authorization" ) );
1112 }
1113
1114 @Test
1115 public void testConnectionReuse()
1116 throws Exception
1117 {
1118 httpServer.addSslConnector();
1119 session.setCache( new DefaultRepositoryCache() );
1120 for ( int i = 0; i < 3; i++ )
1121 {
1122 newTransporter( httpServer.getHttpsUrl() );
1123 GetTask task = new GetTask( URI.create( "repo/file.txt" ) );
1124 transporter.get( task );
1125 assertEquals( "test", task.getDataString() );
1126 }
1127 PoolStats stats =
1128 ( (ConnPoolControl<?>) ( (HttpTransporter) transporter ).getState().getConnectionManager() ).getTotalStats();
1129 assertEquals( stats.toString(), 1, stats.getAvailable() );
1130 }
1131
1132 @Test( expected = NoTransporterException.class )
1133 public void testInit_BadProtocol()
1134 throws Exception
1135 {
1136 newTransporter( "bad:/void" );
1137 }
1138
1139 @Test( expected = NoTransporterException.class )
1140 public void testInit_BadUrl()
1141 throws Exception
1142 {
1143 newTransporter( "http://localhost:NaN" );
1144 }
1145
1146 @Test
1147 public void testInit_CaseInsensitiveProtocol()
1148 throws Exception
1149 {
1150 newTransporter( "http://localhost" );
1151 newTransporter( "HTTP://localhost" );
1152 newTransporter( "Http://localhost" );
1153 newTransporter( "https://localhost" );
1154 newTransporter( "HTTPS://localhost" );
1155 newTransporter( "HttpS://localhost" );
1156 }
1157
1158 }