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.TestUtils; 044import org.eclipse.aether.repository.Authentication; 045import org.eclipse.aether.repository.Proxy; 046import org.eclipse.aether.repository.RemoteRepository; 047import org.eclipse.aether.spi.connector.transport.GetTask; 048import org.eclipse.aether.spi.connector.transport.PeekTask; 049import org.eclipse.aether.spi.connector.transport.PutTask; 050import org.eclipse.aether.spi.connector.transport.Transporter; 051import org.eclipse.aether.spi.connector.transport.TransporterFactory; 052import org.eclipse.aether.transfer.NoTransporterException; 053import org.eclipse.aether.transfer.TransferCancelledException; 054import org.eclipse.aether.util.repository.AuthenticationBuilder; 055import org.junit.After; 056import org.junit.Before; 057import org.junit.Rule; 058import org.junit.Test; 059import org.junit.rules.TestName; 060 061/** 062 */ 063public class HttpTransporterTest 064{ 065 066 static 067 { 068 System.setProperty( "javax.net.ssl.trustStore", 069 new File( "src/test/resources/ssl/server-store" ).getAbsolutePath() ); 070 System.setProperty( "javax.net.ssl.trustStorePassword", "server-pwd" ); 071 System.setProperty( "javax.net.ssl.keyStore", 072 new File( "src/test/resources/ssl/client-store" ).getAbsolutePath() ); 073 System.setProperty( "javax.net.ssl.keyStorePassword", "client-pwd" ); 074 } 075 076 @Rule 077 public TestName testName = new TestName(); 078 079 private DefaultRepositorySystemSession session; 080 081 private TransporterFactory factory; 082 083 private Transporter transporter; 084 085 private File repoDir; 086 087 private HttpServer httpServer; 088 089 private Authentication auth; 090 091 private Proxy proxy; 092 093 private RemoteRepository newRepo( String url ) 094 { 095 return new RemoteRepository.Builder( "test", "default", url ).setAuthentication( auth ).setProxy( proxy ).build(); 096 } 097 098 private void newTransporter( String url ) 099 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}