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