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:MagicNumber", "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(file.lastModified(), OLD_FILE_TIMESTAMP); 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(e.getPayload().getStatus(), 403); 491 assertEquals(e.getPayload().getTitle(), "You do not have enough credit."); 492 assertEquals(e.getPayload().getDetail(), "Your current balance is 30, but that costs 50."); 493 assertEquals(e.getPayload().getInstance(), URI.create("/account/12345/msgs/abc")); 494 } 495 } 496 497 @Test 498 protected void testGet_RFC9457Response_with_missing_fields() throws Exception { 499 try { 500 transporter.get(new GetTask(URI.create("rfc9457/missing_fields.txt"))); 501 fail("Expected error"); 502 } catch (HttpRFC9457Exception e) { 503 assertEquals(403, e.getStatusCode()); 504 assertEquals(e.getPayload().getType(), URI.create("about:blank")); 505 assertNull(e.getPayload().getStatus()); 506 assertNull(e.getPayload().getTitle()); 507 assertNull(e.getPayload().getDetail()); 508 assertNull(e.getPayload().getInstance()); 509 } 510 } 511 512 @Test 513 protected void testGet_SSL() throws Exception { 514 httpServer.addSslConnector(); 515 newTransporter(httpServer.getHttpsUrl()); 516 RecordingTransportListener listener = new RecordingTransportListener(); 517 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 518 transporter.get(task); 519 assertEquals("test", task.getDataString()); 520 assertEquals(0L, listener.getDataOffset()); 521 assertEquals(4L, listener.getDataLength()); 522 assertEquals(1, listener.getStartedCount()); 523 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 524 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 525 } 526 527 @Test 528 protected void testGet_SSL_WithServerErrors() throws Exception { 529 httpServer.setServerErrorsBeforeWorks(1); 530 httpServer.addSslConnector(); 531 newTransporter(httpServer.getHttpsUrl()); 532 for (int i = 1; i < 3; i++) { 533 try { 534 RecordingTransportListener listener = new RecordingTransportListener(); 535 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 536 transporter.get(task); 537 assertEquals("test", task.getDataString()); 538 assertEquals(0L, listener.getDataOffset()); 539 assertEquals(4L, listener.getDataLength()); 540 assertEquals(1, listener.getStartedCount()); 541 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 542 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 543 } catch (HttpTransporterException e) { 544 assertEquals(500, e.getStatusCode()); 545 } 546 } 547 } 548 549 @Test 550 protected void testGet_HTTPS_Unknown_SecurityMode() throws Exception { 551 session.setConfigProperty(ConfigurationProperties.HTTPS_SECURITY_MODE, "unknown"); 552 httpServer.addSelfSignedSslConnector(); 553 try { 554 newTransporter(httpServer.getHttpsUrl()); 555 fail("Unsupported security mode"); 556 } catch (IllegalArgumentException a) { 557 // good 558 } 559 } 560 561 @Test 562 protected void testGet_HTTPS_Insecure_SecurityMode() throws Exception { 563 // here we use alternate server-store-selfigned key (as the key set it static initializer is probably already 564 // used to init SSLContext/SSLSocketFactory/etc 565 session.setConfigProperty( 566 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE); 567 httpServer.addSelfSignedSslConnector(); 568 newTransporter(httpServer.getHttpsUrl()); 569 RecordingTransportListener listener = new RecordingTransportListener(); 570 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 571 transporter.get(task); 572 assertEquals("test", task.getDataString()); 573 assertEquals(0L, listener.getDataOffset()); 574 assertEquals(4L, listener.getDataLength()); 575 assertEquals(1, listener.getStartedCount()); 576 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 577 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 578 } 579 580 @Test 581 protected void testGet_HTTPS_HTTP2Only_Insecure_SecurityMode() throws Exception { 582 // here we use alternate server-store-selfigned key (as the key set it static initializer is probably already 583 // used to init SSLContext/SSLSocketFactory/etc 584 enableHttp2Protocol(); 585 session.setConfigProperty( 586 ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE); 587 httpServer.addSelfSignedSslConnectorHttp2Only(); 588 newTransporter(httpServer.getHttpsUrl()); 589 RecordingTransportListener listener = new RecordingTransportListener(); 590 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 591 transporter.get(task); 592 assertEquals("test", task.getDataString()); 593 assertEquals(0L, listener.getDataOffset()); 594 assertEquals(4L, listener.getDataLength()); 595 assertEquals(1, listener.getStartedCount()); 596 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 597 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 598 } 599 600 protected void enableHttp2Protocol() {} 601 602 @Test 603 protected void testGet_Redirect() throws Exception { 604 httpServer.addSslConnector(); 605 RecordingTransportListener listener = new RecordingTransportListener(); 606 GetTask task = new GetTask(URI.create("redirect/file.txt?scheme=https")).setListener(listener); 607 transporter.get(task); 608 assertEquals("test", task.getDataString()); 609 assertEquals(0L, listener.getDataOffset()); 610 assertEquals(4L, listener.getDataLength()); 611 assertEquals(1, listener.getStartedCount()); 612 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 613 assertEquals(task.getDataString(), listener.getBaos().toString(StandardCharsets.UTF_8)); 614 } 615 616 @Test 617 protected void testGet_Resume() throws Exception { 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(2L, listener.getDataOffset()); 627 assertEquals(9, listener.getDataLength()); 628 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 629 assertEquals("sumable", listener.getBaos().toString(StandardCharsets.UTF_8)); 630 } 631 632 @Test 633 protected void testGet_ResumeLocalContentsOutdated() throws Exception { 634 File file = TestFileUtils.createTempFile("re"); 635 file.setLastModified(System.currentTimeMillis() - 5 * 60 * 1000); 636 RecordingTransportListener listener = new RecordingTransportListener(); 637 GetTask task = new GetTask(URI.create("repo/resume.txt")) 638 .setDataPath(file.toPath(), true) 639 .setListener(listener); 640 transporter.get(task); 641 assertEquals("resumable", TestFileUtils.readString(file)); 642 assertEquals(1L, listener.getStartedCount()); 643 assertEquals(0L, listener.getDataOffset()); 644 assertEquals(9, listener.getDataLength()); 645 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 646 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8)); 647 } 648 649 @Test 650 protected void testGet_ResumeRangesNotSupportedByServer() throws Exception { 651 httpServer.setRangeSupport(false); 652 File file = TestFileUtils.createTempFile("re"); 653 RecordingTransportListener listener = new RecordingTransportListener(); 654 GetTask task = new GetTask(URI.create("repo/resume.txt")) 655 .setDataPath(file.toPath(), true) 656 .setListener(listener); 657 transporter.get(task); 658 assertEquals("resumable", TestFileUtils.readString(file)); 659 assertEquals(1L, listener.getStartedCount()); 660 assertEquals(0L, listener.getDataOffset()); 661 assertEquals(9, listener.getDataLength()); 662 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 663 assertEquals("resumable", listener.getBaos().toString(StandardCharsets.UTF_8)); 664 } 665 666 @Test 667 protected void testGet_Checksums_Nexus() throws Exception { 668 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.NEXUS); 669 GetTask task = new GetTask(URI.create("repo/file.txt")); 670 transporter.get(task); 671 assertEquals("test", task.getDataString()); 672 assertEquals( 673 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1")); 674 } 675 676 @Test 677 protected void testGet_Checksums_XChecksum() throws Exception { 678 httpServer.setChecksumHeader(HttpServer.ChecksumHeader.XCHECKSUM); 679 GetTask task = new GetTask(URI.create("repo/file.txt")); 680 transporter.get(task); 681 assertEquals("test", task.getDataString()); 682 assertEquals( 683 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", task.getChecksums().get("SHA-1")); 684 } 685 686 @Test 687 protected void testGet_FileHandleLeak() throws Exception { 688 for (int i = 0; i < 100; i++) { 689 File file = TestFileUtils.createTempFile("failure"); 690 transporter.get(new GetTask(URI.create("repo/file.txt")).setDataPath(file.toPath())); 691 assertTrue(file.delete(), i + ", " + file.getAbsolutePath()); 692 } 693 } 694 695 @Test 696 protected void testGet_NotFound() throws Exception { 697 try { 698 transporter.get(new GetTask(URI.create("repo/missing.txt"))); 699 fail("Expected error"); 700 } catch (HttpTransporterException e) { 701 assertEquals(404, e.getStatusCode()); 702 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e)); 703 } 704 } 705 706 @Test 707 protected void testGet_Closed() throws Exception { 708 transporter.close(); 709 try { 710 transporter.get(new GetTask(URI.create("repo/file.txt"))); 711 fail("Expected error"); 712 } catch (IllegalStateException e) { 713 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 714 } 715 } 716 717 @Test 718 protected void testGet_StartCancelled() throws Exception { 719 RecordingTransportListener listener = new RecordingTransportListener(); 720 listener.cancelStart(); 721 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 722 try { 723 transporter.get(task); 724 fail("Expected error"); 725 } catch (TransferCancelledException e) { 726 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 727 } 728 assertEquals(0L, listener.getDataOffset()); 729 assertEquals(4L, listener.getDataLength()); 730 assertEquals(1, listener.getStartedCount()); 731 assertEquals(0, listener.getProgressedCount()); 732 } 733 734 @Test 735 protected void testGet_ProgressCancelled() throws Exception { 736 RecordingTransportListener listener = new RecordingTransportListener(); 737 listener.cancelProgress(); 738 GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener); 739 try { 740 transporter.get(task); 741 fail("Expected error"); 742 } catch (TransferCancelledException e) { 743 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 744 } 745 assertEquals(0L, listener.getDataOffset()); 746 assertEquals(4L, listener.getDataLength()); 747 assertEquals(1, listener.getStartedCount()); 748 assertEquals(1, listener.getProgressedCount()); 749 } 750 751 @Test 752 protected void testPut_FromMemory() throws Exception { 753 RecordingTransportListener listener = new RecordingTransportListener(); 754 PutTask task = 755 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 756 transporter.put(task); 757 assertEquals(0L, listener.getDataOffset()); 758 assertEquals(6L, listener.getDataLength()); 759 assertEquals(1, listener.getStartedCount()); 760 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 761 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 762 } 763 764 @Test 765 protected void testPut_FromFile() throws Exception { 766 File file = TestFileUtils.createTempFile("upload"); 767 RecordingTransportListener listener = new RecordingTransportListener(); 768 PutTask task = 769 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataPath(file.toPath()); 770 transporter.put(task); 771 assertEquals(0L, listener.getDataOffset()); 772 assertEquals(6L, listener.getDataLength()); 773 assertEquals(1, listener.getStartedCount()); 774 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 775 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 776 } 777 778 @Test 779 protected void testPut_EmptyResource() throws Exception { 780 RecordingTransportListener listener = new RecordingTransportListener(); 781 PutTask task = new PutTask(URI.create("repo/file.txt")).setListener(listener); 782 transporter.put(task); 783 assertEquals(0L, listener.getDataOffset()); 784 assertEquals(0L, listener.getDataLength()); 785 assertEquals(1, listener.getStartedCount()); 786 assertEquals(0, listener.getProgressedCount()); 787 assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt"))); 788 } 789 790 @Test 791 protected void testPut_EncodedResourcePath() throws Exception { 792 RecordingTransportListener listener = new RecordingTransportListener(); 793 PutTask task = new PutTask(URI.create("repo/some%20space.txt")) 794 .setListener(listener) 795 .setDataString("OK"); 796 transporter.put(task); 797 assertEquals(0L, listener.getDataOffset()); 798 assertEquals(2L, listener.getDataLength()); 799 assertEquals(1, listener.getStartedCount()); 800 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 801 assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt"))); 802 } 803 804 @Test 805 protected void testPut_Authenticated_ExpectContinue() throws Exception { 806 httpServer.setAuthentication("testuser", "testpass"); 807 auth = new AuthenticationBuilder() 808 .addUsername("testuser") 809 .addPassword("testpass") 810 .build(); 811 newTransporter(httpServer.getHttpUrl()); 812 RecordingTransportListener listener = new RecordingTransportListener(); 813 PutTask task = 814 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 815 transporter.put(task); 816 assertEquals(0L, listener.getDataOffset()); 817 assertEquals(6L, listener.getDataLength()); 818 assertEquals(1, listener.getStartedCount()); 819 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 820 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 821 } 822 823 @Test 824 protected void testPut_Authenticated_ExpectContinueBroken() throws Exception { 825 // this makes OPTIONS recover, and have only 1 PUT (startedCount=1 as OPTIONS is not counted) 826 session.setConfigProperty(ConfigurationProperties.HTTP_SUPPORT_WEBDAV, true); 827 httpServer.setAuthentication("testuser", "testpass"); 828 httpServer.setExpectSupport(HttpServer.ExpectContinue.BROKEN); 829 auth = new AuthenticationBuilder() 830 .addUsername("testuser") 831 .addPassword("testpass") 832 .build(); 833 newTransporter(httpServer.getHttpUrl()); 834 RecordingTransportListener listener = new RecordingTransportListener(); 835 PutTask task = 836 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 837 transporter.put(task); 838 assertEquals(0L, listener.getDataOffset()); 839 assertEquals(6L, listener.getDataLength()); 840 assertEquals(1, listener.getStartedCount()); 841 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 842 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 843 } 844 845 @Test 846 protected void testPut_Authenticated_ExpectContinueRejected() throws Exception { 847 httpServer.setAuthentication("testuser", "testpass"); 848 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); 849 auth = new AuthenticationBuilder() 850 .addUsername("testuser") 851 .addPassword("testpass") 852 .build(); 853 newTransporter(httpServer.getHttpUrl()); 854 RecordingTransportListener listener = new RecordingTransportListener(); 855 PutTask task = 856 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 857 transporter.put(task); 858 assertEquals(0L, listener.getDataOffset()); 859 assertEquals(6L, listener.getDataLength()); 860 assertEquals(1, listener.getStartedCount()); 861 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 862 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 863 } 864 865 @Test 866 protected void testPut_Authenticated_ExpectContinueDisabled() throws Exception { 867 session.setConfigProperty(ConfigurationProperties.HTTP_EXPECT_CONTINUE, false); 868 httpServer.setAuthentication("testuser", "testpass"); 869 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); // if transport tries Expect/Continue explode 870 auth = new AuthenticationBuilder() 871 .addUsername("testuser") 872 .addPassword("testpass") 873 .build(); 874 newTransporter(httpServer.getHttpUrl()); 875 RecordingTransportListener listener = new RecordingTransportListener(); 876 PutTask task = 877 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 878 transporter.put(task); 879 assertEquals(0L, listener.getDataOffset()); 880 assertEquals(6L, listener.getDataLength()); 881 assertEquals(1, listener.getStartedCount()); // w/ expectContinue enabled would have here 2 882 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 883 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 884 } 885 886 @Test 887 protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() throws Exception { 888 Map<String, String> headers = new HashMap<>(); 889 headers.put("Expect", "100-continue"); 890 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers); 891 httpServer.setAuthentication("testuser", "testpass"); 892 httpServer.setExpectSupport(HttpServer.ExpectContinue.FAIL); 893 auth = new AuthenticationBuilder() 894 .addUsername("testuser") 895 .addPassword("testpass") 896 .build(); 897 newTransporter(httpServer.getHttpUrl()); 898 RecordingTransportListener listener = new RecordingTransportListener(); 899 PutTask task = 900 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 901 transporter.put(task); 902 assertEquals(0L, listener.getDataOffset()); 903 assertEquals(6L, listener.getDataLength()); 904 assertEquals(1, listener.getStartedCount()); 905 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 906 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 907 } 908 909 @Test 910 protected void testPut_Unauthenticated() throws Exception { 911 httpServer.setAuthentication("testuser", "testpass"); 912 RecordingTransportListener listener = new RecordingTransportListener(); 913 PutTask task = 914 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 915 try { 916 transporter.put(task); 917 fail("Expected error"); 918 } catch (HttpTransporterException e) { 919 assertEquals(401, e.getStatusCode()); 920 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 921 } 922 assertEquals(0, listener.getStartedCount()); 923 assertEquals(0, listener.getProgressedCount()); 924 } 925 926 @Test 927 protected void testPut_ProxyAuthenticated() throws Exception { 928 httpServer.setProxyAuthentication("testuser", "testpass"); 929 Authentication auth = new AuthenticationBuilder() 930 .addUsername("testuser") 931 .addPassword("testpass") 932 .build(); 933 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 934 newTransporter("http://bad.localhost:1/"); 935 RecordingTransportListener listener = new RecordingTransportListener(); 936 PutTask task = 937 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 938 transporter.put(task); 939 assertEquals(0L, listener.getDataOffset()); 940 assertEquals(6L, listener.getDataLength()); 941 assertEquals(1, listener.getStartedCount()); 942 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 943 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 944 } 945 946 @Test 947 protected void testPut_ProxyUnauthenticated() throws Exception { 948 httpServer.setProxyAuthentication("testuser", "testpass"); 949 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort()); 950 newTransporter("http://bad.localhost:1/"); 951 RecordingTransportListener listener = new RecordingTransportListener(); 952 PutTask task = 953 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 954 try { 955 transporter.put(task); 956 fail("Expected error"); 957 } catch (HttpTransporterException e) { 958 assertEquals(407, e.getStatusCode()); 959 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 960 } 961 assertEquals(0, listener.getStartedCount()); 962 assertEquals(0, listener.getProgressedCount()); 963 } 964 965 @Test 966 protected void testPut_SSL() throws Exception { 967 httpServer.addSslConnector(); 968 httpServer.setAuthentication("testuser", "testpass"); 969 auth = new AuthenticationBuilder() 970 .addUsername("testuser") 971 .addPassword("testpass") 972 .build(); 973 newTransporter(httpServer.getHttpsUrl()); 974 RecordingTransportListener listener = new RecordingTransportListener(); 975 PutTask task = 976 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 977 transporter.put(task); 978 assertEquals(0L, listener.getDataOffset()); 979 assertEquals(6L, listener.getDataLength()); 980 assertEquals(1, listener.getStartedCount()); 981 assertTrue(listener.getProgressedCount() > 0, "Count: " + listener.getProgressedCount()); 982 assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); 983 } 984 985 @Test 986 protected void testPut_FileHandleLeak() throws Exception { 987 for (int i = 0; i < 100; i++) { 988 File src = TestFileUtils.createTempFile("upload"); 989 File dst = new File(repoDir, "file.txt"); 990 transporter.put(new PutTask(URI.create("repo/file.txt")).setDataPath(src.toPath())); 991 assertTrue(src.delete(), i + ", " + src.getAbsolutePath()); 992 assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath()); 993 } 994 } 995 996 @Test 997 protected void testPut_Closed() throws Exception { 998 transporter.close(); 999 try { 1000 transporter.put(new PutTask(URI.create("repo/missing.txt"))); 1001 fail("Expected error"); 1002 } catch (IllegalStateException e) { 1003 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1004 } 1005 } 1006 1007 @Test 1008 protected void testPut_StartCancelled() throws Exception { 1009 RecordingTransportListener listener = new RecordingTransportListener(); 1010 listener.cancelStart(); 1011 PutTask task = 1012 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 1013 try { 1014 transporter.put(task); 1015 fail("Expected error"); 1016 } catch (TransferCancelledException e) { 1017 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1018 } 1019 assertEquals(0L, listener.getDataOffset()); 1020 assertEquals(6L, listener.getDataLength()); 1021 assertEquals(1, listener.getStartedCount()); 1022 assertEquals(0, listener.getProgressedCount()); 1023 } 1024 1025 @Test 1026 protected void testPut_ProgressCancelled() throws Exception { 1027 RecordingTransportListener listener = new RecordingTransportListener(); 1028 listener.cancelProgress(); 1029 PutTask task = 1030 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 1031 try { 1032 transporter.put(task); 1033 fail("Expected error"); 1034 } catch (TransferCancelledException e) { 1035 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1036 } 1037 assertEquals(0L, listener.getDataOffset()); 1038 assertEquals(6L, listener.getDataLength()); 1039 assertEquals(1, listener.getStartedCount()); 1040 assertEquals(1, listener.getProgressedCount()); 1041 } 1042 1043 @Test 1044 protected void testGetPut_AuthCache() throws Exception { 1045 httpServer.setAuthentication("testuser", "testpass"); 1046 auth = new AuthenticationBuilder() 1047 .addUsername("testuser") 1048 .addPassword("testpass") 1049 .build(); 1050 newTransporter(httpServer.getHttpUrl()); 1051 GetTask get = new GetTask(URI.create("repo/file.txt")); 1052 transporter.get(get); 1053 RecordingTransportListener listener = new RecordingTransportListener(); 1054 PutTask task = 1055 new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataString("upload"); 1056 transporter.put(task); 1057 assertEquals(1, listener.getStartedCount()); 1058 } 1059 1060 @Test 1061 protected void testPut_PreemptiveIsDefault() throws Exception { 1062 httpServer.setAuthentication("testuser", "testpass"); 1063 auth = new AuthenticationBuilder() 1064 .addUsername("testuser") 1065 .addPassword("testpass") 1066 .build(); 1067 newTransporter(httpServer.getHttpUrl()); 1068 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1069 transporter.put(task); 1070 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1071 } 1072 1073 @Test 1074 protected void testPut_AuthCache() throws Exception { 1075 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH, false); 1076 httpServer.setAuthentication("testuser", "testpass"); 1077 auth = new AuthenticationBuilder() 1078 .addUsername("testuser") 1079 .addPassword("testpass") 1080 .build(); 1081 newTransporter(httpServer.getHttpUrl()); 1082 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1083 transporter.put(task); 1084 assertEquals(2, httpServer.getLogEntries().size()); // put (challenged) + put w/ auth 1085 httpServer.getLogEntries().clear(); 1086 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1087 transporter.put(task); 1088 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1089 } 1090 1091 @Test 1092 protected void testPut_AuthCache_Preemptive() throws Exception { 1093 httpServer.setAuthentication("testuser", "testpass"); 1094 auth = new AuthenticationBuilder() 1095 .addUsername("testuser") 1096 .addPassword("testpass") 1097 .build(); 1098 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true); 1099 newTransporter(httpServer.getHttpUrl()); 1100 PutTask task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1101 transporter.put(task); 1102 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1103 httpServer.getLogEntries().clear(); 1104 task = new PutTask(URI.create("repo/file.txt")).setDataString("upload"); 1105 transporter.put(task); 1106 assertEquals(1, httpServer.getLogEntries().size()); // put w/ auth 1107 } 1108 1109 @Test 1110 @Timeout(20) 1111 protected void testConcurrency() throws Exception { 1112 httpServer.setAuthentication("testuser", "testpass"); 1113 auth = new AuthenticationBuilder() 1114 .addUsername("testuser") 1115 .addPassword("testpass") 1116 .build(); 1117 newTransporter(httpServer.getHttpUrl()); 1118 final AtomicReference<Throwable> error = new AtomicReference<>(); 1119 Thread[] threads = new Thread[20]; 1120 for (int i = 0; i < threads.length; i++) { 1121 final String path = "repo/file.txt?i=" + i; 1122 threads[i] = new Thread(() -> { 1123 try { 1124 for (int j = 0; j < 100; j++) { 1125 GetTask task = new GetTask(URI.create(path)); 1126 transporter.get(task); 1127 assertEquals("test", task.getDataString()); 1128 } 1129 } catch (Throwable t) { 1130 error.compareAndSet(null, t); 1131 System.err.println(path); 1132 t.printStackTrace(); 1133 } 1134 }); 1135 threads[i].setName("Task-" + i); 1136 } 1137 for (Thread thread : threads) { 1138 thread.start(); 1139 } 1140 for (Thread thread : threads) { 1141 thread.join(); 1142 } 1143 assertNull(error.get(), String.valueOf(error.get())); 1144 } 1145 1146 @Test 1147 @Timeout(10) 1148 protected void testConnectTimeout() throws Exception { 1149 session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, 100); 1150 int port = 1; 1151 newTransporter("http://localhost:" + port); 1152 try { 1153 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1154 fail("Expected error"); 1155 } catch (Exception e) { 1156 // impl specific "timeout" exception 1157 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1158 } 1159 } 1160 1161 @Test 1162 @Timeout(10) 1163 protected void testRequestTimeout() throws Exception { 1164 session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, 100); 1165 ServerSocket server = new ServerSocket(0); 1166 try (server) { 1167 newTransporter("http://localhost:" + server.getLocalPort()); 1168 try { 1169 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1170 fail("Expected error"); 1171 } catch (Exception e) { 1172 assertTrue(e.getClass().getSimpleName().contains("Timeout")); 1173 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e)); 1174 } 1175 } 1176 } 1177 1178 @Test 1179 protected void testUserAgent() throws Exception { 1180 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0"); 1181 newTransporter(httpServer.getHttpUrl()); 1182 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1183 assertEquals(1, httpServer.getLogEntries().size()); 1184 for (HttpServer.LogEntry log : httpServer.getLogEntries()) { 1185 assertEquals("SomeTest/1.0", log.getHeaders().get("User-Agent")); 1186 } 1187 } 1188 1189 @Test 1190 protected void testCustomHeaders() throws Exception { 1191 Map<String, String> headers = new HashMap<>(); 1192 headers.put("User-Agent", "Custom/1.0"); 1193 headers.put("X-CustomHeader", "Custom-Value"); 1194 session.setConfigProperty(ConfigurationProperties.USER_AGENT, "SomeTest/1.0"); 1195 session.setConfigProperty(ConfigurationProperties.HTTP_HEADERS + ".test", headers); 1196 newTransporter(httpServer.getHttpUrl()); 1197 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1198 assertEquals(1, httpServer.getLogEntries().size()); 1199 for (HttpServer.LogEntry log : httpServer.getLogEntries()) { 1200 for (Map.Entry<String, String> entry : headers.entrySet()) { 1201 assertEquals(entry.getValue(), log.getHeaders().get(entry.getKey()), entry.getKey()); 1202 } 1203 } 1204 } 1205 1206 @Test 1207 protected void testServerAuthScope_NotUsedForProxy() throws Exception { 1208 String username = "testuser", password = "testpass"; 1209 httpServer.setProxyAuthentication(username, password); 1210 auth = new AuthenticationBuilder() 1211 .addUsername(username) 1212 .addPassword(password) 1213 .build(); 1214 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort()); 1215 newTransporter("http://" + httpServer.getHost() + ":12/"); 1216 try { 1217 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1218 fail("Server auth must not be used as proxy auth"); 1219 } catch (HttpTransporterException e) { 1220 assertEquals(407, e.getStatusCode()); 1221 } catch (IOException e) { 1222 // accepted as well: point is to fail 1223 } 1224 } 1225 1226 @Test 1227 protected void testProxyAuthScope_NotUsedForServer() throws Exception { 1228 String username = "testuser", password = "testpass"; 1229 httpServer.setAuthentication(username, password); 1230 Authentication auth = new AuthenticationBuilder() 1231 .addUsername(username) 1232 .addPassword(password) 1233 .build(); 1234 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 1235 newTransporter("http://" + httpServer.getHost() + ":12/"); 1236 try { 1237 transporter.get(new GetTask(URI.create("repo/file.txt"))); 1238 fail("Proxy auth must not be used as server auth"); 1239 } catch (HttpTransporterException e) { 1240 assertEquals(401, e.getStatusCode()); 1241 } catch (IOException e) { 1242 // accepted as well: point is to fail 1243 } 1244 } 1245 1246 @Test 1247 protected void testAuthSchemeReuse() throws Exception { 1248 httpServer.setAuthentication("testuser", "testpass"); 1249 httpServer.setProxyAuthentication("proxyuser", "proxypass"); 1250 session.setCache(new DefaultRepositoryCache()); 1251 auth = new AuthenticationBuilder() 1252 .addUsername("testuser") 1253 .addPassword("testpass") 1254 .build(); 1255 Authentication auth = new AuthenticationBuilder() 1256 .addUsername("proxyuser") 1257 .addPassword("proxypass") 1258 .build(); 1259 proxy = new Proxy(Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth); 1260 newTransporter("http://bad.localhost:1/"); 1261 GetTask task = new GetTask(URI.create("repo/file.txt")); 1262 transporter.get(task); 1263 assertEquals("test", task.getDataString()); 1264 assertEquals(3, httpServer.getLogEntries().size()); 1265 httpServer.getLogEntries().clear(); 1266 newTransporter("http://bad.localhost:1/"); 1267 task = new GetTask(URI.create("repo/file.txt")); 1268 transporter.get(task); 1269 assertEquals("test", task.getDataString()); 1270 assertEquals(1, httpServer.getLogEntries().size()); 1271 assertNotNull(httpServer.getLogEntries().get(0).getHeaders().get("Authorization")); 1272 assertNotNull(httpServer.getLogEntries().get(0).getHeaders().get("Proxy-Authorization")); 1273 } 1274 1275 @Test 1276 protected void testAuthSchemePreemptive() throws Exception { 1277 httpServer.setAuthentication("testuser", "testpass"); 1278 session.setCache(new DefaultRepositoryCache()); 1279 auth = new AuthenticationBuilder() 1280 .addUsername("testuser") 1281 .addPassword("testpass") 1282 .build(); 1283 1284 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, false); 1285 newTransporter(httpServer.getHttpUrl()); 1286 GetTask task = new GetTask(URI.create("repo/file.txt")); 1287 transporter.get(task); 1288 assertEquals("test", task.getDataString()); 1289 // there ARE challenge round-trips 1290 assertEquals(2, httpServer.getLogEntries().size()); 1291 1292 httpServer.getLogEntries().clear(); 1293 1294 session.setConfigProperty(ConfigurationProperties.HTTP_PREEMPTIVE_AUTH, true); 1295 newTransporter(httpServer.getHttpUrl()); 1296 task = new GetTask(URI.create("repo/file.txt")); 1297 transporter.get(task); 1298 assertEquals("test", task.getDataString()); 1299 // there are NO challenge round-trips, all goes through at first 1300 assertEquals(1, httpServer.getLogEntries().size()); 1301 } 1302 1303 @Test 1304 void testInit_BadProtocol() { 1305 assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void")); 1306 } 1307 1308 @Test 1309 void testInit_BadUrl() { 1310 assertThrows(NoTransporterException.class, () -> newTransporter("http://localhost:NaN")); 1311 } 1312 1313 @Test 1314 void testInit_CaseInsensitiveProtocol() throws Exception { 1315 newTransporter("http://localhost"); 1316 newTransporter("HTTP://localhost"); 1317 newTransporter("Http://localhost"); 1318 newTransporter("https://localhost"); 1319 newTransporter("HTTPS://localhost"); 1320 newTransporter("HttpS://localhost"); 1321 } 1322}