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