001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.internal.test.util.http; 020 021import java.io.File; 022import java.io.FileNotFoundException; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.UncheckedIOException; 026import java.net.ServerSocket; 027import java.net.URI; 028import java.net.URL; 029import java.nio.charset.StandardCharsets; 030import java.nio.file.Files; 031import java.nio.file.Path; 032import java.nio.file.Paths; 033import java.nio.file.StandardCopyOption; 034import java.util.HashMap; 035import java.util.Map; 036import java.util.concurrent.atomic.AtomicReference; 037import java.util.function.Supplier; 038 039import org.eclipse.aether.ConfigurationProperties; 040import org.eclipse.aether.DefaultRepositoryCache; 041import org.eclipse.aether.DefaultRepositorySystemSession; 042import org.eclipse.aether.DefaultSessionData; 043import org.eclipse.aether.internal.impl.transport.http.DefaultChecksumExtractor; 044import org.eclipse.aether.internal.impl.transport.http.Nx2ChecksumExtractor; 045import org.eclipse.aether.internal.impl.transport.http.XChecksumExtractor; 046import org.eclipse.aether.internal.test.util.TestFileUtils; 047import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager; 048import org.eclipse.aether.repository.Authentication; 049import org.eclipse.aether.repository.Proxy; 050import org.eclipse.aether.repository.RemoteRepository; 051import org.eclipse.aether.spi.connector.transport.GetTask; 052import org.eclipse.aether.spi.connector.transport.PeekTask; 053import org.eclipse.aether.spi.connector.transport.PutTask; 054import org.eclipse.aether.spi.connector.transport.Transporter; 055import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; 056import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy; 057import org.eclipse.aether.spi.connector.transport.http.HttpTransporter; 058import org.eclipse.aether.spi.connector.transport.http.HttpTransporterException; 059import org.eclipse.aether.spi.connector.transport.http.HttpTransporterFactory; 060import org.eclipse.aether.transfer.NoTransporterException; 061import org.eclipse.aether.transfer.TransferCancelledException; 062import org.eclipse.aether.util.repository.AuthenticationBuilder; 063import org.junit.jupiter.api.AfterEach; 064import org.junit.jupiter.api.BeforeEach; 065import org.junit.jupiter.api.Test; 066import org.junit.jupiter.api.TestInfo; 067import org.junit.jupiter.api.Timeout; 068 069import static java.util.Objects.requireNonNull; 070import static org.junit.jupiter.api.Assertions.assertEquals; 071import static org.junit.jupiter.api.Assertions.assertNotNull; 072import static org.junit.jupiter.api.Assertions.assertNull; 073import static org.junit.jupiter.api.Assertions.assertThrows; 074import static org.junit.jupiter.api.Assertions.assertTrue; 075import static org.junit.jupiter.api.Assertions.fail; 076 077/** 078 * Common set of tests against Http transporter. 079 */ 080@SuppressWarnings({"checkstyle:MagicNumber", "checkstyle:MethodName"}) 081public class HttpTransporterTest { 082 083 protected static final Path KEY_STORE_PATH = Paths.get("target/keystore"); 084 085 protected static final Path KEY_STORE_SELF_SIGNED_PATH = Paths.get("target/keystore-self-signed"); 086 087 protected static final Path TRUST_STORE_PATH = Paths.get("target/trustStore"); 088 089 static { 090 // Warning: "cross connected" with HttpServer! 091 System.setProperty( 092 "javax.net.ssl.trustStore", KEY_STORE_PATH.toAbsolutePath().toString()); 093 System.setProperty("javax.net.ssl.trustStorePassword", "server-pwd"); 094 System.setProperty( 095 "javax.net.ssl.keyStore", TRUST_STORE_PATH.toAbsolutePath().toString()); 096 System.setProperty("javax.net.ssl.keyStorePassword", "client-pwd"); 097 098 System.setProperty("javax.net.ssl.trustStoreType", "jks"); 099 System.setProperty("javax.net.ssl.keyStoreType", "jks"); 100 // System.setProperty("javax.net.debug", "all"); 101 } 102 103 private final Supplier<HttpTransporterFactory> transporterFactorySupplier; 104 105 protected DefaultRepositorySystemSession session; 106 107 protected HttpTransporterFactory factory; 108 109 protected HttpTransporter transporter; 110 111 protected Runnable closer; 112 113 protected File repoDir; 114 115 protected HttpServer httpServer; 116 117 protected Authentication auth; 118 119 protected Proxy proxy; 120 121 protected HttpTransporterTest(Supplier<HttpTransporterFactory> transporterFactorySupplier) { 122 this.transporterFactorySupplier = requireNonNull(transporterFactorySupplier); 123 124 if (!Files.isRegularFile(KEY_STORE_PATH)) { 125 URL keyStoreUrl = HttpTransporterTest.class.getClassLoader().getResource("ssl/server-store"); 126 URL keyStoreSelfSignedUrl = 127 HttpTransporterTest.class.getClassLoader().getResource("ssl/server-store-selfsigned"); 128 URL trustStoreUrl = HttpTransporterTest.class.getClassLoader().getResource("ssl/client-store"); 129 130 try { 131 try (InputStream keyStoreStream = keyStoreUrl.openStream(); 132 InputStream keyStoreSelfSignedStream = keyStoreSelfSignedUrl.openStream(); 133 InputStream trustStoreStream = trustStoreUrl.openStream()) { 134 Files.copy(keyStoreStream, KEY_STORE_PATH, StandardCopyOption.REPLACE_EXISTING); 135 Files.copy( 136 keyStoreSelfSignedStream, KEY_STORE_SELF_SIGNED_PATH, StandardCopyOption.REPLACE_EXISTING); 137 Files.copy(trustStoreStream, TRUST_STORE_PATH, StandardCopyOption.REPLACE_EXISTING); 138 } 139 } catch (IOException e) { 140 throw new UncheckedIOException(e); 141 } 142 } 143 } 144 145 protected static ChecksumExtractor standardChecksumExtractor() { 146 HashMap<String, ChecksumExtractorStrategy> strategies = new HashMap<>(); 147 strategies.put("1", new Nx2ChecksumExtractor()); 148 strategies.put("2", new XChecksumExtractor()); 149 return new DefaultChecksumExtractor(strategies); 150 } 151 152 protected RemoteRepository newRepo(String url) { 153 return new RemoteRepository.Builder("test", "default", url) 154 .setAuthentication(auth) 155 .setProxy(proxy) 156 .build(); 157 } 158 159 protected void newTransporter(String url) throws Exception { 160 if (transporter != null) { 161 transporter.close(); 162 transporter = null; 163 } 164 if (closer != null) { 165 closer.run(); 166 closer = null; 167 } 168 session = new DefaultRepositorySystemSession(session); 169 session.setData(new DefaultSessionData()); 170 transporter = factory.newInstance(session, newRepo(url)); 171 } 172 173 protected static final long OLD_FILE_TIMESTAMP = 160660800000L; 174 175 @BeforeEach 176 protected void setUp(TestInfo testInfo) throws Exception { 177 System.out.println("=== " + testInfo.getDisplayName() + " ==="); 178 session = new DefaultRepositorySystemSession(h -> { 179 this.closer = h; 180 return true; 181 }); 182 session.setLocalRepositoryManager(new TestLocalRepositoryManager()); 183 factory = transporterFactorySupplier.get(); 184 repoDir = TestFileUtils.createTempDir(); 185 TestFileUtils.writeString(new File(repoDir, "file.txt"), "test"); 186 TestFileUtils.writeString(new File(repoDir, "dir/file.txt"), "test"); 187 TestFileUtils.writeString(new File(repoDir, "dir/oldFile.txt"), "oldTest", OLD_FILE_TIMESTAMP); 188 TestFileUtils.writeString(new File(repoDir, "empty.txt"), ""); 189 TestFileUtils.writeString(new File(repoDir, "some space.txt"), "space"); 190 File resumable = new File(repoDir, "resume.txt"); 191 TestFileUtils.writeString(resumable, "resumable"); 192 resumable.setLastModified(System.currentTimeMillis() - 90 * 1000); 193 httpServer = new HttpServer().setRepoDir(repoDir).start(); 194 newTransporter(httpServer.getHttpUrl()); 195 } 196 197 @AfterEach 198 protected void tearDown() throws Exception { 199 if (transporter != null) { 200 transporter.close(); 201 transporter = null; 202 } 203 if (closer != null) { 204 closer.run(); 205 closer = null; 206 } 207 if (httpServer != null) { 208 httpServer.stop(); 209 httpServer = null; 210 } 211 factory = null; 212 session = null; 213 } 214 215 @Test 216 protected void testClassify() { 217 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException())); 218 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new HttpTransporterException(403))); 219 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new HttpTransporterException(404))); 220 } 221 222 @Test 223 protected void testPeek() throws Exception { 224 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 225 } 226 227 @Test 228 protected void testRetryHandler_defaultCount_positive() throws Exception { 229 httpServer.setConnectionsToClose(3); 230 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 231 } 232 233 @Test 234 protected void testRetryHandler_defaultCount_negative() throws Exception { 235 httpServer.setConnectionsToClose(4); 236 try { 237 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 238 fail("Expected error"); 239 } catch (Exception expected) { 240 } 241 } 242 243 @Test 244 protected void testRetryHandler_explicitCount_positive() throws Exception { 245 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 10); 246 newTransporter(httpServer.getHttpUrl()); 247 httpServer.setConnectionsToClose(10); 248 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 249 } 250 251 @Test 252 protected void testRetryHandler_disabled() throws Exception { 253 session.setConfigProperty(ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT, 0); 254 newTransporter(httpServer.getHttpUrl()); 255 httpServer.setConnectionsToClose(1); 256 try { 257 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 258 } catch (Exception expected) { 259 } 260 } 261 262 @Test 263 protected void testPeek_NotFound() throws Exception { 264 try { 265 transporter.peek(new PeekTask(URI.create("repo/missing.txt"))); 266 fail("Expected error"); 267 } catch (HttpTransporterException e) { 268 assertEquals(404, e.getStatusCode()); 269 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e)); 270 } 271 } 272 273 @Test 274 protected void testPeek_Closed() throws Exception { 275 transporter.close(); 276 try { 277 transporter.peek(new PeekTask(URI.create("repo/missing.txt"))); 278 fail("Expected error"); 279 } catch (IllegalStateException e) { 280 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 281 } 282 } 283 284 @Test 285 protected void testPeek_Authenticated() throws Exception { 286 httpServer.setAuthentication("testuser", "testpass"); 287 auth = new AuthenticationBuilder() 288 .addUsername("testuser") 289 .addPassword("testpass") 290 .build(); 291 newTransporter(httpServer.getHttpUrl()); 292 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 293 } 294 295 @Test 296 protected void testPeek_Unauthenticated() throws Exception { 297 httpServer.setAuthentication("testuser", "testpass"); 298 try { 299 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 300 fail("Expected error"); 301 } catch (HttpTransporterException e) { 302 assertEquals(401, e.getStatusCode()); 303 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 304 } 305 } 306 307 @Test 308 protected void testPeek_ProxyAuthenticated() throws Exception { 309 httpServer.setProxyAuthentication("testuser", "testpass"); 310 auth = new AuthenticationBuilder() 311 .addUsername("testuser") 312 .addPassword("testpass") 313 .build(); 314 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 315 newTransporter("http://bad.localhost:1/"); 316 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 317 } 318 319 @Test 320 protected void testPeek_ProxyUnauthenticated() throws Exception { 321 httpServer.setProxyAuthentication("testuser", "testpass"); 322 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort()); 323 newTransporter("http://bad.localhost:1/"); 324 try { 325 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 326 fail("Expected error"); 327 } catch (HttpTransporterException e) { 328 assertEquals(407, e.getStatusCode()); 329 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 330 } 331 } 332 333 @Test 334 protected void testPeek_SSL() throws Exception { 335 httpServer.addSslConnector(); 336 newTransporter(httpServer.getHttpsUrl()); 337 transporter.peek(new PeekTask(URI.create("repo/file.txt"))); 338 } 339 340 @Test 341 protected void testPeek_Redirect() throws Exception { 342 httpServer.addSslConnector(); 343 transporter.peek(new PeekTask(URI.create("redirect/file.txt"))); 344 transporter.peek(new PeekTask(URI.create("redirect/file.txt?scheme=https"))); 345 } 346 347 @Test 348 protected void testGet_ToMemory() throws Exception { 349 RecordingTransportListener listener = new RecordingTransportListener(); 350 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 351 transporter.get(task); 352 assertEquals("test", task.getDataString()); 353 assertEquals(0L, listener.getDataOffset()); 354 assertEquals(4L, listener.getDataLength()); 355 assertEquals(1, listener.getStartedCount()); 356 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 357 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 358 } 359 360 @Test 361 protected void testGet_ToFile() throws Exception { 362 File file = TestFileUtils.createTempFile("failure"); 363 RecordingTransportListener listener = new RecordingTransportListener(); 364 GetTask task = new GetTask(URI.create("repo/file.txt")) 365 .setDataPath(file.toPath()) 366 .setListener(listener); 367 transporter.get(task); 368 assertEquals("test", TestFileUtils.readString(file)); 369 assertEquals(0L, listener.getDataOffset()); 370 assertEquals(4L, listener.getDataLength()); 371 assertEquals(1, listener.getStartedCount()); 372 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 373 assertEquals("test", listener.getBaos().toString(StandardCharsets.UTF_8)); 374 } 375 376 @Test 377 protected void testGet_ToFileTimestamp() throws Exception { 378 File file = TestFileUtils.createTempFile("failure"); 379 RecordingTransportListener listener = new RecordingTransportListener(); 380 GetTask task = new GetTask(URI.create("repo/dir/oldFile.txt")) 381 .setDataPath(file.toPath()) 382 .setListener(listener); 383 transporter.get(task); 384 assertEquals("oldTest", TestFileUtils.readString(file)); 385 assertEquals(0L, listener.getDataOffset()); 386 assertEquals(7L, listener.getDataLength()); 387 assertEquals(1, listener.getStartedCount()); 388 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 389 assertEquals("oldTest", listener.getBaos().toString(StandardCharsets.UTF_8)); 390 assertEquals(file.lastModified(), OLD_FILE_TIMESTAMP); 391 } 392 393 @Test 394 protected void testGet_EmptyResource() throws Exception { 395 File file = TestFileUtils.createTempFile("failure"); 396 RecordingTransportListener listener = new RecordingTransportListener(); 397 GetTask task = new GetTask(URI.create("repo/empty.txt")) 398 .setDataPath(file.toPath()) 399 .setListener(listener); 400 transporter.get(task); 401 assertEquals("", TestFileUtils.readString(file)); 402 assertEquals(0L, listener.getDataOffset()); 403 assertEquals(0L, listener.getDataLength()); 404 assertEquals(1, listener.getStartedCount()); 405 assertEquals(0, listener.getProgressedCount()); 406 assertEquals("", listener.getBaos().toString(StandardCharsets.UTF_8)); 407 } 408 409 @Test 410 protected void testGet_EncodedResourcePath() throws Exception { 411 GetTask task = new GetTask(URI.create("repo/some%20space.txt")); 412 transporter.get(task); 413 assertEquals("space", task.getDataString()); 414 } 415 416 @Test 417 protected void testGet_Authenticated() throws Exception { 418 httpServer.setAuthentication("testuser", "testpass"); 419 auth = new AuthenticationBuilder() 420 .addUsername("testuser") 421 .addPassword("testpass") 422 .build(); 423 newTransporter(httpServer.getHttpUrl()); 424 RecordingTransportListener listener = new RecordingTransportListener(); 425 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 426 transporter.get(task); 427 assertEquals("test", task.getDataString()); 428 assertEquals(0L, listener.getDataOffset()); 429 assertEquals(4L, listener.getDataLength()); 430 assertEquals(1, listener.getStartedCount()); 431 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 432 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 433 } 434 435 @Test 436 protected void testGet_Unauthenticated() throws Exception { 437 httpServer.setAuthentication("testuser", "testpass"); 438 try { 439 transporter.get(new GetTask(URI.create("repo/file.txt"))); 440 fail("Expected error"); 441 } catch (HttpTransporterException e) { 442 assertEquals(401, e.getStatusCode()); 443 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 444 } 445 } 446 447 @Test 448 protected void testGet_ProxyAuthenticated() throws Exception { 449 httpServer.setProxyAuthentication("testuser", "testpass"); 450 Authentication auth = new AuthenticationBuilder() 451 .addUsername("testuser") 452 .addPassword("testpass") 453 .build(); 454 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 455 newTransporter("http://bad.localhost:1/"); 456 RecordingTransportListener listener = new RecordingTransportListener(); 457 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 458 transporter.get(task); 459 assertEquals("test", task.getDataString()); 460 assertEquals(0L, listener.getDataOffset()); 461 assertEquals(4L, listener.getDataLength()); 462 assertEquals(1, listener.getStartedCount()); 463 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 464 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 465 } 466 467 @Test 468 protected void testGet_ProxyUnauthenticated() throws Exception { 469 httpServer.setProxyAuthentication("testuser", "testpass"); 470 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort()); 471 newTransporter("http://bad.localhost:1/"); 472 try { 473 transporter.get(new GetTask(URI.create("repo/file.txt"))); 474 fail("Expected error"); 475 } catch (HttpTransporterException e) { 476 assertEquals(407, e.getStatusCode()); 477 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 478 } 479 } 480 481 @Test 482 protected void testGet_SSL() throws Exception { 483 httpServer.addSslConnector(); 484 newTransporter(httpServer.getHttpsUrl()); 485 RecordingTransportListener listener = new RecordingTransportListener(); 486 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 487 transporter.get(task); 488 assertEquals("test", task.getDataString()); 489 assertEquals(0L, listener.getDataOffset()); 490 assertEquals(4L, listener.getDataLength()); 491 assertEquals(1, listener.getStartedCount()); 492 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 493 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 494 } 495 496 @Test 497 protected void testGet_SSL_WithServerErrors() throws Exception { 498 httpServer.setServerErrorsBeforeWorks(1); 499 httpServer.addSslConnector(); 500 newTransporter(httpServer.getHttpsUrl()); 501 for (int i = 1; i < 3; i++) { 502 try { 503 RecordingTransportListener listener = new RecordingTransportListener(); 504 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 505 transporter.get(task); 506 assertEquals("test", task.getDataString()); 507 assertEquals(0L, listener.getDataOffset()); 508 assertEquals(4L, listener.getDataLength()); 509 assertEquals(1, listener.getStartedCount()); 510 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 511 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 512 } catch (HttpTransporterException e) { 513 assertEquals(500, e.getStatusCode()); 514 } 515 } 516 } 517 518 @Test 519 protected void testGet_HTTPS_Unknown_SecurityMode() throws Exception { 520 session.setConfigProperty(ConfigurationProperties.HTTPS_SECURITY_MODE, "unknown"); 521 httpServer.addSelfSignedSslConnector(); 522 try { 523 newTransporter(httpServer.getHttpsUrl()); 524 fail("Unsupported security mode"); 525 } catch (IllegalArgumentException a) { 526 // good 527 } 528 } 529 530 @Test 531 protected void testGet_HTTPS_Insecure_SecurityMode() throws Exception { 532 // here we use alternate server-store-selfigned key (as the key set it static initializer is probably already 533 // used to init SSLContext/SSLSocketFactory/etc 534 session.setConfigProperty( 535 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE); 536 httpServer.addSelfSignedSslConnector(); 537 newTransporter(httpServer.getHttpsUrl()); 538 RecordingTransportListener listener = new RecordingTransportListener(); 539 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 540 transporter.get(task); 541 assertEquals("test", task.getDataString()); 542 assertEquals(0L, listener.getDataOffset()); 543 assertEquals(4L, listener.getDataLength()); 544 assertEquals(1, listener.getStartedCount()); 545 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 546 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 547 } 548 549 @Test 550 protected void testGet_HTTPS_HTTP2Only_Insecure_SecurityMode() throws Exception { 551 // here we use alternate server-store-selfigned key (as the key set it static initializer is probably already 552 // used to init SSLContext/SSLSocketFactory/etc 553 session.setConfigProperty( 554 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE); 555 httpServer.addSelfSignedSslConnectorHttp2Only(); 556 newTransporter(httpServer.getHttpsUrl()); 557 RecordingTransportListener listener = new RecordingTransportListener(); 558 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 559 transporter.get(task); 560 assertEquals("test", task.getDataString()); 561 assertEquals(0L, listener.getDataOffset()); 562 assertEquals(4L, listener.getDataLength()); 563 assertEquals(1, listener.getStartedCount()); 564 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 565 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 566 } 567 568 @Test 569 protected void testGet_Redirect() throws Exception { 570 httpServer.addSslConnector(); 571 RecordingTransportListener listener = new RecordingTransportListener(); 572 GetTask task = new GetTask(URI.create("redirect/file.txt?scheme=https")).setListener(listener); 573 transporter.get(task); 574 assertEquals("test", task.getDataString()); 575 assertEquals(0L, listener.getDataOffset()); 576 assertEquals(4L, listener.getDataLength()); 577 assertEquals(1, listener.getStartedCount()); 578 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 579 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 580 } 581 582 @Test 583 protected void testGet_Resume() throws Exception { 584 File file = TestFileUtils.createTempFile("re"); 585 RecordingTransportListener listener = new RecordingTransportListener(); 586 GetTask task = new GetTask(URI.create("repo/resume.txt")) 587 .setDataPath(file.toPath(), true) 588 .setListener(listener); 589 transporter.get(task); 590 assertEquals("resumable", TestFileUtils.readString(file)); 591 assertEquals(1L, listener.getStartedCount()); 592 assertEquals(2L, listener.getDataOffset()); 593 assertEquals(9, listener.getDataLength()); 594 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 595 assertEquals("sumable", listener.getBaos().toString(StandardCharsets.UTF_8)); 596 } 597 598 @Test 599 protected void testGet_ResumeLocalContentsOutdated() throws Exception { 600 File file = TestFileUtils.createTempFile("re"); 601 file.setLastModified(System.currentTimeMillis() - 5 * 60 * 1000); 602 RecordingTransportListener listener = new RecordingTransportListener(); 603 GetTask task = new GetTask(URI.create("repo/resume.txt")) 604 .setDataPath(file.toPath(), true) 605 .setListener(listener); 606 transporter.get(task); 607 assertEquals("resumable", TestFileUtils.readString(file)); 608 assertEquals(1L, listener.getStartedCount()); 609 assertEquals(0L, listener.getDataOffset()); 610 assertEquals(9, listener.getDataLength()); 611 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 612 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8)); 613 } 614 615 @Test 616 protected void testGet_ResumeRangesNotSupportedByServer() throws Exception { 617 httpServer.setRangeSupport(false); 618 File file = TestFileUtils.createTempFile("re"); 619 RecordingTransportListener listener = new RecordingTransportListener(); 620 GetTask task = new GetTask(URI.create("repo/resume.txt")) 621 .setDataPath(file.toPath(), true) 622 .setListener(listener); 623 transporter.get(task); 624 assertEquals("resumable", TestFileUtils.readString(file)); 625 assertEquals(1L, listener.getStartedCount()); 626 assertEquals(0L, listener.getDataOffset()); 627 assertEquals(9, listener.getDataLength()); 628 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 629 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8)); 630 } 631 632 @Test 633 protected void testGet_Checksums_Nexus() throws Exception { 634 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.NEXUS); 635 GetTask task = new GetTask(URI.create("repo/file.txt")); 636 transporter.get(task); 637 assertEquals("test", task.getDataString()); 638 assertEquals( 639 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1")); 640 } 641 642 @Test 643 protected void testGet_Checksums_XChecksum() throws Exception { 644 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM); 645 GetTask task = new GetTask(URI.create("repo/file.txt")); 646 transporter.get(task); 647 assertEquals("test", task.getDataString()); 648 assertEquals( 649 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1")); 650 } 651 652 @Test 653 protected void testGet_FileHandleLeak() throws Exception { 654 for (int i = 0; i < 100; i++) { 655 File file = TestFileUtils.createTempFile("failure"); 656 transporter.get(new GetTask(URI.create("repo/file.txt")).setDataPath(file.toPath())); 657 assertTrue(file.delete(), i + ", " + file.getAbsolutePath()); 658 } 659 } 660 661 @Test 662 protected void testGet_NotFound() throws Exception { 663 try { 664 transporter.get(new GetTask(URI.create("repo/missing.txt"))); 665 fail("Expected error"); 666 } catch (HttpTransporterException e) { 667 assertEquals(404, e.getStatusCode()); 668 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e)); 669 } 670 } 671 672 @Test 673 protected void testGet_Closed() throws Exception { 674 transporter.close(); 675 try { 676 transporter.get(new GetTask(URI.create("repo/file.txt"))); 677 fail("Expected error"); 678 } catch (IllegalStateException e) { 679 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 680 } 681 } 682 683 @Test 684 protected void testGet_StartCancelled() throws Exception { 685 RecordingTransportListener listener = new RecordingTransportListener(); 686 listener.cancelStart(); 687 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 688 try { 689 transporter.get(task); 690 fail("Expected error"); 691 } catch (TransferCancelledException e) { 692 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 693 } 694 assertEquals(0L, listener.getDataOffset()); 695 assertEquals(4L, listener.getDataLength()); 696 assertEquals(1, listener.getStartedCount()); 697 assertEquals(0, listener.getProgressedCount()); 698 } 699 700 @Test 701 protected void testGet_ProgressCancelled() throws Exception { 702 RecordingTransportListener listener = new RecordingTransportListener(); 703 listener.cancelProgress(); 704 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 705 try { 706 transporter.get(task); 707 fail("Expected error"); 708 } catch (TransferCancelledException e) { 709 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 710 } 711 assertEquals(0L, listener.getDataOffset()); 712 assertEquals(4L, listener.getDataLength()); 713 assertEquals(1, listener.getStartedCount()); 714 assertEquals(1, listener.getProgressedCount()); 715 } 716 717 @Test 718 protected void testPut_FromMemory() throws Exception { 719 RecordingTransportListener listener = new RecordingTransportListener(); 720 PutTask task = 721 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 722 transporter.put(task); 723 assertEquals(0L, listener.getDataOffset()); 724 assertEquals(6L, listener.getDataLength()); 725 assertEquals(1, listener.getStartedCount()); 726 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 727 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 728 } 729 730 @Test 731 protected void testPut_FromFile() throws Exception { 732 File file = TestFileUtils.createTempFile("upload"); 733 RecordingTransportListener listener = new RecordingTransportListener(); 734 PutTask task = 735 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataPath(file.toPath()); 736 transporter.put(task); 737 assertEquals(0L, listener.getDataOffset()); 738 assertEquals(6L, listener.getDataLength()); 739 assertEquals(1, listener.getStartedCount()); 740 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 741 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 742 } 743 744 @Test 745 protected void testPut_EmptyResource() throws Exception { 746 RecordingTransportListener listener = new RecordingTransportListener(); 747 PutTask task = new PutTask(URI.create("repo/file.txt")).setListener(listener); 748 transporter.put(task); 749 assertEquals(0L, listener.getDataOffset()); 750 assertEquals(0L, listener.getDataLength()); 751 assertEquals(1, listener.getStartedCount()); 752 assertEquals(0, listener.getProgressedCount()); 753 assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt"))); 754 } 755 756 @Test 757 protected void testPut_EncodedResourcePath() throws Exception { 758 RecordingTransportListener listener = new RecordingTransportListener(); 759 PutTask task = new PutTask(URI.create("repo/some%20space.txt")) 760 .setListener(listener) 761 .setDataString("OK"); 762 transporter.put(task); 763 assertEquals(0L, listener.getDataOffset()); 764 assertEquals(2L, listener.getDataLength()); 765 assertEquals(1, listener.getStartedCount()); 766 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 767 assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt"))); 768 } 769 770 @Test 771 protected void testPut_Authenticated_ExpectContinue() throws Exception { 772 httpServer.setAuthentication("testuser", "testpass"); 773 auth = new AuthenticationBuilder() 774 .addUsername("testuser") 775 .addPassword("testpass") 776 .build(); 777 newTransporter(httpServer.getHttpUrl()); 778 RecordingTransportListener listener = new RecordingTransportListener(); 779 PutTask task = 780 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 781 transporter.put(task); 782 assertEquals(0L, listener.getDataOffset()); 783 assertEquals(6L, listener.getDataLength()); 784 assertEquals(1, listener.getStartedCount()); 785 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 786 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 787 } 788 789 @Test 790 protected void testPut_Authenticated_ExpectContinueBroken() throws Exception { 791 // this makes OPTIONS recover, and have only 1 PUT (startedCount=1 as OPTIONS is not counted) 792 session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true); 793 httpServer.setAuthentication("testuser", "testpass"); 794 httpServer.setExpectSupport(HttpServer.ExpectContinue.BROKEN); 795 auth = new AuthenticationBuilder() 796 .addUsername("testuser") 797 .addPassword("testpass") 798 .build(); 799 newTransporter(httpServer.getHttpUrl()); 800 RecordingTransportListener listener = new RecordingTransportListener(); 801 PutTask task = 802 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 803 transporter.put(task); 804 assertEquals(0L, listener.getDataOffset()); 805 assertEquals(6L, listener.getDataLength()); 806 assertEquals(1, listener.getStartedCount()); 807 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 808 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 809 } 810 811 @Test 812 protected void testPut_Authenticated_ExpectContinueRejected() throws Exception { 813 httpServer.setAuthentication("testuser", "testpass"); 814 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); 815 auth = new AuthenticationBuilder() 816 .addUsername("testuser") 817 .addPassword("testpass") 818 .build(); 819 newTransporter(httpServer.getHttpUrl()); 820 RecordingTransportListener listener = new RecordingTransportListener(); 821 PutTask task = 822 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 823 transporter.put(task); 824 assertEquals(0L, listener.getDataOffset()); 825 assertEquals(6L, listener.getDataLength()); 826 assertEquals(1, listener.getStartedCount()); 827 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 828 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 829 } 830 831 @Test 832 protected void testPut_Authenticated_ExpectContinueDisabled() throws Exception { 833 session.setConfigProperty(ConfigurationProperties.HTTP_EXPECT_CONTINUE, false); 834 httpServer.setAuthentication("testuser", "testpass"); 835 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); // if transport tries Expect/Continue explode 836 auth = new AuthenticationBuilder() 837 .addUsername("testuser") 838 .addPassword("testpass") 839 .build(); 840 newTransporter(httpServer.getHttpUrl()); 841 RecordingTransportListener listener = new RecordingTransportListener(); 842 PutTask task = 843 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 844 transporter.put(task); 845 assertEquals(0L, listener.getDataOffset()); 846 assertEquals(6L, listener.getDataLength()); 847 assertEquals(1, listener.getStartedCount()); // w/ expectContinue enabled would have here 2 848 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 849 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 850 } 851 852 @Test 853 protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() throws Exception { 854 Map<String, String> headers = new HashMap<>(); 855 headers.put("Expect", "100-continue"); 856 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers); 857 httpServer.setAuthentication("testuser", "testpass"); 858 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); 859 auth = new AuthenticationBuilder() 860 .addUsername("testuser") 861 .addPassword("testpass") 862 .build(); 863 newTransporter(httpServer.getHttpUrl()); 864 RecordingTransportListener listener = new RecordingTransportListener(); 865 PutTask task = 866 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 867 transporter.put(task); 868 assertEquals(0L, listener.getDataOffset()); 869 assertEquals(6L, listener.getDataLength()); 870 assertEquals(1, listener.getStartedCount()); 871 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 872 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 873 } 874 875 @Test 876 protected void testPut_Unauthenticated() throws Exception { 877 httpServer.setAuthentication("testuser", "testpass"); 878 RecordingTransportListener listener = new RecordingTransportListener(); 879 PutTask task = 880 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 881 try { 882 transporter.put(task); 883 fail("Expected error"); 884 } catch (HttpTransporterException e) { 885 assertEquals(401, e.getStatusCode()); 886 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 887 } 888 assertEquals(0, listener.getStartedCount()); 889 assertEquals(0, listener.getProgressedCount()); 890 } 891 892 @Test 893 protected void testPut_ProxyAuthenticated() throws Exception { 894 httpServer.setProxyAuthentication("testuser", "testpass"); 895 Authentication auth = new AuthenticationBuilder() 896 .addUsername("testuser") 897 .addPassword("testpass") 898 .build(); 899 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 900 newTransporter("http://bad.localhost:1/"); 901 RecordingTransportListener listener = new RecordingTransportListener(); 902 PutTask task = 903 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 904 transporter.put(task); 905 assertEquals(0L, listener.getDataOffset()); 906 assertEquals(6L, listener.getDataLength()); 907 assertEquals(1, listener.getStartedCount()); 908 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 909 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 910 } 911 912 @Test 913 protected void testPut_ProxyUnauthenticated() throws Exception { 914 httpServer.setProxyAuthentication("testuser", "testpass"); 915 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort()); 916 newTransporter("http://bad.localhost:1/"); 917 RecordingTransportListener listener = new RecordingTransportListener(); 918 PutTask task = 919 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 920 try { 921 transporter.put(task); 922 fail("Expected error"); 923 } catch (HttpTransporterException e) { 924 assertEquals(407, e.getStatusCode()); 925 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 926 } 927 assertEquals(0, listener.getStartedCount()); 928 assertEquals(0, listener.getProgressedCount()); 929 } 930 931 @Test 932 protected void testPut_SSL() throws Exception { 933 httpServer.addSslConnector(); 934 httpServer.setAuthentication("testuser", "testpass"); 935 auth = new AuthenticationBuilder() 936 .addUsername("testuser") 937 .addPassword("testpass") 938 .build(); 939 newTransporter(httpServer.getHttpsUrl()); 940 RecordingTransportListener listener = new RecordingTransportListener(); 941 PutTask task = 942 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 943 transporter.put(task); 944 assertEquals(0L, listener.getDataOffset()); 945 assertEquals(6L, listener.getDataLength()); 946 assertEquals(1, listener.getStartedCount()); 947 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 948 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 949 } 950 951 @Test 952 protected void testPut_FileHandleLeak() throws Exception { 953 for (int i = 0; i < 100; i++) { 954 File src = TestFileUtils.createTempFile("upload"); 955 File dst = new File(repoDir, "file.txt"); 956 transporter.put(new PutTask(URI.create("repo/file.txt")).setDataPath(src.toPath())); 957 assertTrue(src.delete(), i + ", " + src.getAbsolutePath()); 958 assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath()); 959 } 960 } 961 962 @Test 963 protected void testPut_Closed() throws Exception { 964 transporter.close(); 965 try { 966 transporter.put(new PutTask(URI.create("repo/missing.txt"))); 967 fail("Expected error"); 968 } catch (IllegalStateException e) { 969 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 970 } 971 } 972 973 @Test 974 protected void testPut_StartCancelled() throws Exception { 975 RecordingTransportListener listener = new RecordingTransportListener(); 976 listener.cancelStart(); 977 PutTask task = 978 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 979 try { 980 transporter.put(task); 981 fail("Expected error"); 982 } catch (TransferCancelledException e) { 983 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 984 } 985 assertEquals(0L, listener.getDataOffset()); 986 assertEquals(6L, listener.getDataLength()); 987 assertEquals(1, listener.getStartedCount()); 988 assertEquals(0, listener.getProgressedCount()); 989 } 990 991 @Test 992 protected void testPut_ProgressCancelled() throws Exception { 993 RecordingTransportListener listener = new RecordingTransportListener(); 994 listener.cancelProgress(); 995 PutTask task = 996 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 997 try { 998 transporter.put(task); 999 fail("Expected error"); 1000 } catch (TransferCancelledException e) { 1001 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1002 } 1003 assertEquals(0L, listener.getDataOffset()); 1004 assertEquals(6L, listener.getDataLength()); 1005 assertEquals(1, listener.getStartedCount()); 1006 assertEquals(1, listener.getProgressedCount()); 1007 } 1008 1009 @Test 1010 protected void testGetPut_AuthCache() throws Exception { 1011 httpServer.setAuthentication("testuser", "testpass"); 1012 auth = new AuthenticationBuilder() 1013 .addUsername("testuser") 1014 .addPassword("testpass") 1015 .build(); 1016 newTransporter(httpServer.getHttpUrl()); 1017 GetTask get = new GetTask(URI.create("repo/file.txt")); 1018 transporter.get(get); 1019 RecordingTransportListener listener = new RecordingTransportListener(); 1020 PutTask task = 1021 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 1022 transporter.put(task); 1023 assertEquals(1, listener.getStartedCount()); 1024 } 1025 1026 @Test 1027 protected void testPut_PreemptiveIsDefault() throws Exception { 1028 httpServer.setAuthentication("testuser", "testpass"); 1029 auth = new AuthenticationBuilder() 1030 .addUsername("testuser") 1031 .addPassword("testpass") 1032 .build(); 1033 newTransporter(httpServer.getHttpUrl()); 1034 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1035 transporter.put(task); 1036 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1037 } 1038 1039 @Test 1040 protected void testPut_AuthCache() throws Exception { 1041 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH, false); 1042 httpServer.setAuthentication("testuser", "testpass"); 1043 auth = new AuthenticationBuilder() 1044 .addUsername("testuser") 1045 .addPassword("testpass") 1046 .build(); 1047 newTransporter(httpServer.getHttpUrl()); 1048 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1049 transporter.put(task); 1050 assertEquals(2, httpServer.getLogEntries().size()); // put (challenged) + put w/ auth 1051 httpServer.getLogEntries().clear(); 1052 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1053 transporter.put(task); 1054 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1055 } 1056 1057 @Test 1058 protected void testPut_AuthCache_Preemptive() throws Exception { 1059 httpServer.setAuthentication("testuser", "testpass"); 1060 auth = new AuthenticationBuilder() 1061 .addUsername("testuser") 1062 .addPassword("testpass") 1063 .build(); 1064 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true); 1065 newTransporter(httpServer.getHttpUrl()); 1066 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1067 transporter.put(task); 1068 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1069 httpServer.getLogEntries().clear(); 1070 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1071 transporter.put(task); 1072 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1073 } 1074 1075 @Test 1076 @Timeout(20) 1077 protected void testConcurrency() throws Exception { 1078 httpServer.setAuthentication("testuser", "testpass"); 1079 auth = new AuthenticationBuilder() 1080 .addUsername("testuser") 1081 .addPassword("testpass") 1082 .build(); 1083 newTransporter(httpServer.getHttpUrl()); 1084 final AtomicReference<Throwable> error = new AtomicReference<>(); 1085 Thread[] threads = new Thread[20]; 1086 for (int i = 0; i < threads.length; i++) { 1087 final String path = "repo/file.txt?i=" + i; 1088 threads[i] = new Thread(() -> { 1089 try { 1090 for (int j = 0; j < 100; j++) { 1091 GetTask task = new GetTask(URI.create(path)); 1092 transporter.get(task); 1093 assertEquals("test", task.getDataString()); 1094 } 1095 } catch (Throwable t) { 1096 error.compareAndSet(null, t); 1097 System.err.println(path); 1098 t.printStackTrace(); 1099 } 1100 }); 1101 threads[i].setName("Task-" + i); 1102 } 1103 for (Thread thread : threads) { 1104 thread.start(); 1105 } 1106 for (Thread thread : threads) { 1107 thread.join(); 1108 } 1109 assertNull(error.get(), String.valueOf(error.get())); 1110 } 1111 1112 @Test 1113 @Timeout(10) 1114 protected void testConnectTimeout() throws Exception { 1115 session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, 100); 1116 int port = 1; 1117 newTransporter("http://localhost:" + port); 1118 try { 1119 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1120 fail("Expected error"); 1121 } catch (Exception e) { 1122 // impl specific "timeout" exception 1123 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1124 } 1125 } 1126 1127 @Test 1128 @Timeout(10) 1129 protected void testRequestTimeout() throws Exception { 1130 session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, 100); 1131 ServerSocket server = new ServerSocket(0); 1132 try (server) { 1133 newTransporter("http://localhost:" + server.getLocalPort()); 1134 try { 1135 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1136 fail("Expected error"); 1137 } catch (Exception e) { 1138 assertTrue(e.getClass().getSimpleName().contains("Timeout")); 1139 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1140 } 1141 } 1142 } 1143 1144 @Test 1145 protected void testUserAgent() throws Exception { 1146 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0"); 1147 newTransporter(httpServer.getHttpUrl()); 1148 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1149 assertEquals(1, httpServer.getLogEntries().size()); 1150 for (HttpServer.LogEntry log : httpServer.getLogEntries()) { 1151 assertEquals("SomeTest/1.0", log.getHeaders().get("User-Agent")); 1152 } 1153 } 1154 1155 @Test 1156 protected void testCustomHeaders() throws Exception { 1157 Map<String, String> headers = new HashMap<>(); 1158 headers.put("User-Agent", "Custom/1.0"); 1159 headers.put("X-CustomHeader", "Custom-Value"); 1160 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0"); 1161 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers); 1162 newTransporter(httpServer.getHttpUrl()); 1163 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1164 assertEquals(1, httpServer.getLogEntries().size()); 1165 for (HttpServer.LogEntry log : httpServer.getLogEntries()) { 1166 for (Map.Entry<String, String> entry : headers.entrySet()) { 1167 assertEquals(entry.getValue(), log.getHeaders().get(entry.getKey()), entry.getKey()); 1168 } 1169 } 1170 } 1171 1172 @Test 1173 protected void testServerAuthScope_NotUsedForProxy() throws Exception { 1174 String username = "testuser", password = "testpass"; 1175 httpServer.setProxyAuthentication(username, password); 1176 auth = new AuthenticationBuilder() 1177 .addUsername(username) 1178 .addPassword(password) 1179 .build(); 1180 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort()); 1181 newTransporter("http://" + httpServer.getHost() + ":12/"); 1182 try { 1183 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1184 fail("Server auth must not be used as proxy auth"); 1185 } catch (HttpTransporterException e) { 1186 assertEquals(407, e.getStatusCode()); 1187 } catch (IOException e) { 1188 // accepted as well: point is to fail 1189 } 1190 } 1191 1192 @Test 1193 protected void testProxyAuthScope_NotUsedForServer() throws Exception { 1194 String username = "testuser", password = "testpass"; 1195 httpServer.setAuthentication(username, password); 1196 Authentication auth = new AuthenticationBuilder() 1197 .addUsername(username) 1198 .addPassword(password) 1199 .build(); 1200 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 1201 newTransporter("http://" + httpServer.getHost() + ":12/"); 1202 try { 1203 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1204 fail("Proxy auth must not be used as server auth"); 1205 } catch (HttpTransporterException e) { 1206 assertEquals(401, e.getStatusCode()); 1207 } catch (IOException e) { 1208 // accepted as well: point is to fail 1209 } 1210 } 1211 1212 @Test 1213 protected void testAuthSchemeReuse() throws Exception { 1214 httpServer.setAuthentication("testuser", "testpass"); 1215 httpServer.setProxyAuthentication("proxyuser", "proxypass"); 1216 session.setCache(new DefaultRepositoryCache()); 1217 auth = new AuthenticationBuilder() 1218 .addUsername("testuser") 1219 .addPassword("testpass") 1220 .build(); 1221 Authentication auth = new AuthenticationBuilder() 1222 .addUsername("proxyuser") 1223 .addPassword("proxypass") 1224 .build(); 1225 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 1226 newTransporter("http://bad.localhost:1/"); 1227 GetTask task = new GetTask(URI.create("repo/file.txt")); 1228 transporter.get(task); 1229 assertEquals("test", task.getDataString()); 1230 assertEquals(3, httpServer.getLogEntries().size()); 1231 httpServer.getLogEntries().clear(); 1232 newTransporter("http://bad.localhost:1/"); 1233 task = new GetTask(URI.create("repo/file.txt")); 1234 transporter.get(task); 1235 assertEquals("test", task.getDataString()); 1236 assertEquals(1, httpServer.getLogEntries().size()); 1237 assertNotNull(httpServer.getLogEntries().get(0).getHeaders().get("Authorization")); 1238 assertNotNull(httpServer.getLogEntries().get(0).getHeaders().get("Proxy-Authorization")); 1239 } 1240 1241 @Test 1242 protected void testAuthSchemePreemptive() throws Exception { 1243 httpServer.setAuthentication("testuser", "testpass"); 1244 session.setCache(new DefaultRepositoryCache()); 1245 auth = new AuthenticationBuilder() 1246 .addUsername("testuser") 1247 .addPassword("testpass") 1248 .build(); 1249 1250 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, false); 1251 newTransporter(httpServer.getHttpUrl()); 1252 GetTask task = new GetTask(URI.create("repo/file.txt")); 1253 transporter.get(task); 1254 assertEquals("test", task.getDataString()); 1255 // there ARE challenge round-trips 1256 assertEquals(2, httpServer.getLogEntries().size()); 1257 1258 httpServer.getLogEntries().clear(); 1259 1260 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true); 1261 newTransporter(httpServer.getHttpUrl()); 1262 task = new GetTask(URI.create("repo/file.txt")); 1263 transporter.get(task); 1264 assertEquals("test", task.getDataString()); 1265 // there are NO challenge round-trips, all goes through at first 1266 assertEquals(1, httpServer.getLogEntries().size()); 1267 } 1268 1269 @Test 1270 void testInit_BadProtocol() { 1271 assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void")); 1272 } 1273 1274 @Test 1275 void testInit_BadUrl() { 1276 assertThrows(NoTransporterException.class, () -> newTransporter("http://localhost:NaN")); 1277 } 1278 1279 @Test 1280 void testInit_CaseInsensitiveProtocol() throws Exception { 1281 newTransporter("http://localhost"); 1282 newTransporter("HTTP://localhost"); 1283 newTransporter("Http://localhost"); 1284 newTransporter("https://localhost"); 1285 newTransporter("HTTPS://localhost"); 1286 newTransporter("HttpS://localhost"); 1287 } 1288}