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