001package org.eclipse.aether.transport.http; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import static org.junit.Assert.*; 023 024import java.io.File; 025import java.io.FileNotFoundException; 026import java.net.ConnectException; 027import java.net.ServerSocket; 028import java.net.SocketTimeoutException; 029import java.net.URI; 030import java.nio.charset.StandardCharsets; 031import java.util.HashMap; 032import java.util.Map; 033import java.util.concurrent.atomic.AtomicReference; 034 035import org.apache.http.client.HttpResponseException; 036import org.apache.http.conn.ConnectTimeoutException; 037import org.apache.http.pool.ConnPoolControl; 038import org.apache.http.pool.PoolStats; 039import org.eclipse.aether.ConfigurationProperties; 040import org.eclipse.aether.DefaultRepositoryCache; 041import org.eclipse.aether.DefaultRepositorySystemSession; 042import org.eclipse.aether.internal.test.util.TestFileUtils; 043import org.eclipse.aether.internal.test.util.TestLoggerFactory; 044import org.eclipse.aether.internal.test.util.TestUtils; 045import org.eclipse.aether.repository.Authentication; 046import org.eclipse.aether.repository.Proxy; 047import org.eclipse.aether.repository.RemoteRepository; 048import org.eclipse.aether.spi.connector.transport.GetTask; 049import org.eclipse.aether.spi.connector.transport.PeekTask; 050import org.eclipse.aether.spi.connector.transport.PutTask; 051import org.eclipse.aether.spi.connector.transport.Transporter; 052import org.eclipse.aether.spi.connector.transport.TransporterFactory; 053import org.eclipse.aether.transfer.NoTransporterException; 054import org.eclipse.aether.transfer.TransferCancelledException; 055import org.eclipse.aether.util.repository.AuthenticationBuilder; 056import org.junit.After; 057import org.junit.Before; 058import org.junit.Rule; 059import org.junit.Test; 060import org.junit.rules.TestName; 061 062/** 063 */ 064public class HttpTransporterTest 065{ 066 067 static 068 { 069 System.setProperty( "javax.net.ssl.trustStore", 070 new File( "src/test/resources/ssl/server-store" ).getAbsolutePath() ); 071 System.setProperty( "javax.net.ssl.trustStorePassword", "server-pwd" ); 072 System.setProperty( "javax.net.ssl.keyStore", 073 new File( "src/test/resources/ssl/client-store" ).getAbsolutePath() ); 074 System.setProperty( "javax.net.ssl.keyStorePassword", "client-pwd" ); 075 } 076 077 @Rule 078 public TestName testName = new TestName(); 079 080 private DefaultRepositorySystemSession session; 081 082 private TransporterFactory factory; 083 084 private Transporter transporter; 085 086 private File repoDir; 087 088 private HttpServer httpServer; 089 090 private Authentication auth; 091 092 private Proxy proxy; 093 094 private RemoteRepository newRepo( String url ) 095 { 096 return new RemoteRepository.Builder( "test", "default", url ).setAuthentication( auth ).setProxy( proxy ).build(); 097 } 098 099 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}