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