HttpsURLConnectionTest.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package org.apache.harmony.luni.tests.internal.net.www.protocol.https; 19 20import dalvik.annotation.AndroidOnly; 21import dalvik.annotation.KnownFailure; 22import dalvik.annotation.TestTargetClass; 23import dalvik.annotation.TestTargets; 24import dalvik.annotation.TestLevel; 25import dalvik.annotation.TestTargetNew; 26 27import java.io.BufferedInputStream; 28import java.io.File; 29import java.io.FileInputStream; 30import java.io.FileNotFoundException; 31import java.io.FileOutputStream; 32import java.io.IOException; 33import java.io.InputStream; 34import java.io.OutputStream; 35import java.io.PrintStream; 36import java.net.Authenticator; 37import java.net.HttpURLConnection; 38import java.net.InetSocketAddress; 39import java.net.PasswordAuthentication; 40import java.net.Proxy; 41import java.net.ServerSocket; 42import java.net.Socket; 43import java.net.SocketTimeoutException; 44import java.net.URL; 45import java.security.KeyStore; 46import java.security.cert.Certificate; 47import java.util.Arrays; 48import javax.net.ssl.HostnameVerifier; 49import javax.net.ssl.HttpsURLConnection; 50import javax.net.ssl.KeyManagerFactory; 51import javax.net.ssl.SSLContext; 52import javax.net.ssl.SSLServerSocket; 53import javax.net.ssl.SSLSession; 54import javax.net.ssl.SSLSocket; 55import javax.net.ssl.SSLSocketFactory; 56import javax.net.ssl.TrustManagerFactory; 57 58import junit.framework.TestCase; 59 60/** 61 * Implementation independent test for HttpsURLConnection. 62 * The test needs certstore file placed in system classpath 63 * and named as "key_store." + the type of the 64 * default KeyStore installed in the system in lower case. 65 * <br> 66 * For example: if default KeyStore type in the system is BKS 67 * (i.e. java.security file sets up the property keystore.type=BKS), 68 * thus classpath should point to the directory with "key_store.bks" 69 * file. 70 * <br> 71 * This certstore file should contain self-signed certificate 72 * generated by keytool utility in a usual way. 73 * <br> 74 * The password to the certstore should be "password" (without quotes). 75 */ 76@TestTargetClass(HttpsURLConnection.class) 77@AndroidOnly("we only have a .bks key store in the test resources") 78public class HttpsURLConnectionTest extends TestCase { 79 80 // the password to the store 81 private static final String KS_PASSWORD = "password"; 82 83 // turn on/off logging 84 private static final boolean DO_LOG = false; 85 86 // read/connection timeout value 87 private static final int TIMEOUT = 5000; 88 89 // OK response code 90 private static final int OK_CODE = 200; 91 92 // Not Found response code 93 private static final int NOT_FOUND_CODE = 404; 94 95 // Proxy authentication required response code 96 private static final int AUTHENTICATION_REQUIRED_CODE = 407; 97 98 // fields keeping the system values of corresponding properties 99 private static String systemKeyStoreType; 100 101 private static String systemKeyStore; 102 103 private static String systemKeyStorePassword; 104 105 private static String systemTrustStoreType; 106 107 private static String systemTrustStore; 108 109 private static String systemTrustStorePassword; 110 111 private static File store; 112 113 static { 114 try { 115 store = File.createTempFile("key_store", "bks"); 116 } catch (Exception e) { 117 // ignore 118 } 119 } 120 121 /** 122 * Checks that HttpsURLConnection's default SSLSocketFactory is operable. 123 */ 124 @TestTargetNew( 125 level = TestLevel.PARTIAL_COMPLETE, 126 notes = "Verifies that HttpsURLConnection's default SSLSocketFactory is operable.", 127 method = "getDefaultSSLSocketFactory", 128 args = {} 129 ) 130 public void testGetDefaultSSLSocketFactory() throws Exception { 131 // set up the properties defining the default values needed by SSL stuff 132 setUpStoreProperties(); 133 134 try { 135 SSLSocketFactory defaultSSLSF = HttpsURLConnection 136 .getDefaultSSLSocketFactory(); 137 ServerSocket ss = new ServerSocket(0); 138 Socket s = defaultSSLSF 139 .createSocket("localhost", ss.getLocalPort()); 140 ss.accept(); 141 s.close(); 142 ss.close(); 143 } finally { 144 // roll the properties back to system values 145 tearDownStoreProperties(); 146 } 147 } 148 149 /** 150 * Checks if HTTPS connection performs initial SSL handshake with the 151 * server working over SSL, sends encrypted HTTP request, 152 * and receives expected HTTP response. After HTTPS session if finished 153 * test checks connection state parameters established by 154 * HttpsURLConnection. 155 */ 156 @TestTargetNew( 157 level = TestLevel.PARTIAL_COMPLETE, 158 notes = "Verifies if HTTPS connection performs initial SSL handshake with the server working over SSL, sends encrypted HTTP request, and receives expected HTTP response.", 159 method = "setDefaultHostnameVerifier", 160 args = {javax.net.ssl.HostnameVerifier.class} 161 ) 162 @KnownFailure("VM aborts after more than one test was run.") 163 public void testHttpsConnection() throws Throwable { 164 // set up the properties defining the default values needed by SSL stuff 165 setUpStoreProperties(); 166 167 try { 168 // create the SSL server socket acting as a server 169 SSLContext ctx = getContext(); 170 ServerSocket ss = ctx.getServerSocketFactory() 171 .createServerSocket(0); 172 173 // create the HostnameVerifier to check hostname verification 174 TestHostnameVerifier hnv = new TestHostnameVerifier(); 175 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 176 177 // create url connection to be tested 178 URL url = new URL("https://localhost:" + ss.getLocalPort()); 179 HttpsURLConnection connection = (HttpsURLConnection) url 180 .openConnection(); 181 182 // perform the interaction between the peers 183 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 184 185 // check the connection state 186 checkConnectionStateParameters(connection, peerSocket); 187 188 // should silently exit 189 connection.connect(); 190 } finally { 191 // roll the properties back to system values 192 tearDownStoreProperties(); 193 } 194 } 195 196 /** 197 * Tests the behaviour of HTTPS connection in case of unavailability 198 * of requested resource. 199 */ 200 @TestTargets({ 201 @TestTargetNew( 202 level = TestLevel.PARTIAL, 203 notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", 204 method = "setDoInput", 205 args = {boolean.class} 206 ), 207 @TestTargetNew( 208 level = TestLevel.PARTIAL, 209 notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", 210 method = "setConnectTimeout", 211 args = {int.class} 212 ), 213 @TestTargetNew( 214 level = TestLevel.PARTIAL, 215 notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", 216 method = "setReadTimeout", 217 args = {int.class} 218 ) 219 }) 220 @KnownFailure("VM aborts after more than one test was run.") 221 public void testHttpsConnection_Not_Found_Response() throws Throwable { 222 // set up the properties defining the default values needed by SSL stuff 223 setUpStoreProperties(); 224 225 try { 226 // create the SSL server socket acting as a server 227 SSLContext ctx = getContext(); 228 ServerSocket ss = ctx.getServerSocketFactory() 229 .createServerSocket(0); 230 231 // create the HostnameVerifier to check hostname verification 232 TestHostnameVerifier hnv = new TestHostnameVerifier(); 233 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 234 235 // create url connection to be tested 236 URL url = new URL("https://localhost:" + ss.getLocalPort()); 237 HttpsURLConnection connection = (HttpsURLConnection) url 238 .openConnection(); 239 240 try { 241 doInteraction(connection, ss, NOT_FOUND_CODE); 242 fail("Expected exception was not thrown."); 243 } catch (FileNotFoundException e) { 244 if (DO_LOG) { 245 System.out.println("Expected exception was thrown: " 246 + e.getMessage()); 247 } 248 } 249 250 // should silently exit 251 connection.connect(); 252 } finally { 253 // roll the properties back to system values 254 tearDownStoreProperties(); 255 } 256 } 257 258 /** 259 * Tests possibility to set up the default SSLSocketFactory 260 * to be used by HttpsURLConnection. 261 */ 262 @TestTargetNew( 263 level = TestLevel.PARTIAL_COMPLETE, 264 notes = "Verifies possibility to set up the default SSLSocketFactory to be used by HttpsURLConnection.", 265 method = "setDefaultSSLSocketFactory", 266 args = {javax.net.ssl.SSLSocketFactory.class} 267 ) 268 public void testSetDefaultSSLSocketFactory() throws Throwable { 269 // create the SSLServerSocket which will be used by server side 270 SSLContext ctx = getContext(); 271 SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() 272 .createServerSocket(0); 273 274 SSLSocketFactory socketFactory = (SSLSocketFactory) ctx 275 .getSocketFactory(); 276 // set up the factory as default 277 HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory); 278 // check the result 279 assertSame("Default SSLSocketFactory differs from expected", 280 socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory()); 281 282 // create the HostnameVerifier to check hostname verification 283 TestHostnameVerifier hnv = new TestHostnameVerifier(); 284 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 285 286 // create HttpsURLConnection to be tested 287 URL url = new URL("https://localhost:" + ss.getLocalPort()); 288 HttpsURLConnection connection = (HttpsURLConnection) url 289 .openConnection(); 290 291 TestHostnameVerifier hnv_late = new TestHostnameVerifier(); 292 // late initialization: should not be used for created connection 293 HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); 294 295 // perform the interaction between the peers 296 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 297 // check the connection state 298 checkConnectionStateParameters(connection, peerSocket); 299 // check the verification process 300 assertTrue("Hostname verification was not done", hnv.verified); 301 assertFalse( 302 "Hostname verification should not be done by this verifier", 303 hnv_late.verified); 304 // check the used SSLSocketFactory 305 assertSame("Default SSLSocketFactory should be used", 306 HttpsURLConnection.getDefaultSSLSocketFactory(), connection 307 .getSSLSocketFactory()); 308 309 // should silently exit 310 connection.connect(); 311 } 312 313 /** 314 * Tests possibility to set up the SSLSocketFactory 315 * to be used by HttpsURLConnection. 316 */ 317 @TestTargetNew( 318 level = TestLevel.PARTIAL_COMPLETE, 319 notes = "Verifies possibility to set up the SSLSocketFactory to be used by HttpsURLConnection.", 320 method = "setSSLSocketFactory", 321 args = {javax.net.ssl.SSLSocketFactory.class} 322 ) 323 public void testSetSSLSocketFactory() throws Throwable { 324 // create the SSLServerSocket which will be used by server side 325 SSLContext ctx = getContext(); 326 SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() 327 .createServerSocket(0); 328 329 // create the HostnameVerifier to check hostname verification 330 TestHostnameVerifier hnv = new TestHostnameVerifier(); 331 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 332 333 // create HttpsURLConnection to be tested 334 URL url = new URL("https://localhost:" + ss.getLocalPort()); 335 HttpsURLConnection connection = (HttpsURLConnection) url 336 .openConnection(); 337 338 SSLSocketFactory socketFactory = (SSLSocketFactory) ctx 339 .getSocketFactory(); 340 connection.setSSLSocketFactory(socketFactory); 341 342 TestHostnameVerifier hnv_late = new TestHostnameVerifier(); 343 // late initialization: should not be used for created connection 344 HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); 345 346 // perform the interaction between the peers 347 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 348 // check the connection state 349 checkConnectionStateParameters(connection, peerSocket); 350 // check the verification process 351 assertTrue("Hostname verification was not done", hnv.verified); 352 assertFalse( 353 "Hostname verification should not be done by this verifier", 354 hnv_late.verified); 355 // check the used SSLSocketFactory 356 assertNotSame("Default SSLSocketFactory should not be used", 357 HttpsURLConnection.getDefaultSSLSocketFactory(), connection 358 .getSSLSocketFactory()); 359 assertSame("Result differs from expected", socketFactory, connection 360 .getSSLSocketFactory()); 361 362 // should silently exit 363 connection.connect(); 364 } 365 366 /** 367 * Tests the behaviour of HttpsURLConnection in case of retrieving 368 * of the connection state parameters before connection has been made. 369 */ 370 @TestTargets({ 371 @TestTargetNew( 372 level = TestLevel.PARTIAL_COMPLETE, 373 notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", 374 method = "getCipherSuite", 375 args = {} 376 ), 377 @TestTargetNew( 378 level = TestLevel.PARTIAL_COMPLETE, 379 notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", 380 method = "getPeerPrincipal", 381 args = {} 382 ), 383 @TestTargetNew( 384 level = TestLevel.PARTIAL_COMPLETE, 385 notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", 386 method = "getLocalPrincipal", 387 args = {} 388 ), 389 @TestTargetNew( 390 level = TestLevel.PARTIAL_COMPLETE, 391 notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", 392 method = "getServerCertificates", 393 args = {} 394 ), 395 @TestTargetNew( 396 level = TestLevel.PARTIAL_COMPLETE, 397 notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", 398 method = "getLocalCertificates", 399 args = {} 400 ) 401 }) 402 public void testUnconnectedStateParameters() throws Throwable { 403 // create HttpsURLConnection to be tested 404 URL url = new URL("https://localhost:55555"); 405 HttpsURLConnection connection = (HttpsURLConnection) url 406 .openConnection(); 407 408 try { 409 connection.getCipherSuite(); 410 fail("Expected IllegalStateException was not thrown"); 411 } catch (IllegalStateException e) {} 412 try { 413 connection.getPeerPrincipal(); 414 fail("Expected IllegalStateException was not thrown"); 415 } catch (IllegalStateException e) {} 416 try { 417 connection.getLocalPrincipal(); 418 fail("Expected IllegalStateException was not thrown"); 419 } catch (IllegalStateException e) {} 420 421 try { 422 connection.getServerCertificates(); 423 fail("Expected IllegalStateException was not thrown"); 424 } catch (IllegalStateException e) {} 425 try { 426 connection.getLocalCertificates(); 427 fail("Expected IllegalStateException was not thrown"); 428 } catch (IllegalStateException e) {} 429 } 430 431 /** 432 * Tests if setHostnameVerifier() method replaces default verifier. 433 */ 434 @TestTargetNew( 435 level = TestLevel.PARTIAL_COMPLETE, 436 notes = "Verifies if setHostnameVerifier() method replaces default verifier.", 437 method = "setHostnameVerifier", 438 args = {javax.net.ssl.HostnameVerifier.class} 439 ) 440 @KnownFailure("VM aborts after more than one test was run.") 441 public void testSetHostnameVerifier() throws Throwable { 442 // setting up the properties pointing to the key/trust stores 443 setUpStoreProperties(); 444 445 try { 446 // create the SSLServerSocket which will be used by server side 447 SSLServerSocket ss = (SSLServerSocket) getContext() 448 .getServerSocketFactory().createServerSocket(0); 449 450 // create the HostnameVerifier to check that Hostname verification 451 // is done 452 TestHostnameVerifier hnv = new TestHostnameVerifier(); 453 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 454 455 // create HttpsURLConnection to be tested 456 URL url = new URL("https://localhost:" + ss.getLocalPort()); 457 HttpsURLConnection connection = (HttpsURLConnection) url 458 .openConnection(); 459 460 TestHostnameVerifier hnv_late = new TestHostnameVerifier(); 461 // replace default verifier 462 connection.setHostnameVerifier(hnv_late); 463 464 // perform the interaction between the peers and check the results 465 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 466 assertTrue("Hostname verification was not done", hnv_late.verified); 467 assertFalse( 468 "Hostname verification should not be done by this verifier", 469 hnv.verified); 470 checkConnectionStateParameters(connection, peerSocket); 471 472 // should silently exit 473 connection.connect(); 474 } finally { 475 // roll the properties back to system values 476 tearDownStoreProperties(); 477 } 478 } 479 480 /** 481 * Tests the behaviour in case of sending the data to the server. 482 */ 483 @TestTargetNew( 484 level = TestLevel.PARTIAL, 485 notes = "Verifies the behaviour in case of sending the data to the server.", 486 method = "setDoOutput", 487 args = {boolean.class} 488 ) 489 @KnownFailure("VM aborts after more than one test was run.") 490 public void test_doOutput() throws Throwable { 491 // setting up the properties pointing to the key/trust stores 492 setUpStoreProperties(); 493 494 try { 495 // create the SSLServerSocket which will be used by server side 496 SSLServerSocket ss = (SSLServerSocket) getContext() 497 .getServerSocketFactory().createServerSocket(0); 498 499 // create the HostnameVerifier to check that Hostname verification 500 // is done 501 TestHostnameVerifier hnv = new TestHostnameVerifier(); 502 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 503 504 // create HttpsURLConnection to be tested 505 URL url = new URL("https://localhost:" + ss.getLocalPort()); 506 HttpsURLConnection connection = (HttpsURLConnection) url 507 .openConnection(); 508 connection.setDoOutput(true); 509 510 // perform the interaction between the peers and check the results 511 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 512 checkConnectionStateParameters(connection, peerSocket); 513 514 // should silently exit 515 connection.connect(); 516 } finally { 517 // roll the properties back to system values 518 tearDownStoreProperties(); 519 } 520 } 521 522 /** 523 * Tests HTTPS connection process made through the proxy server. 524 */ 525 @TestTargets({ 526 @TestTargetNew( 527 level = TestLevel.PARTIAL, 528 notes = "Verifies HTTPS connection process made through the proxy server.", 529 method = "setDoInput", 530 args = {boolean.class} 531 ), 532 @TestTargetNew( 533 level = TestLevel.PARTIAL, 534 notes = "Verifies HTTPS connection process made through the proxy server.", 535 method = "setConnectTimeout", 536 args = {int.class} 537 ), 538 @TestTargetNew( 539 level = TestLevel.PARTIAL, 540 notes = "Verifies HTTPS connection process made through the proxy server.", 541 method = "setReadTimeout", 542 args = {int.class} 543 ) 544 }) 545 @KnownFailure("VM aborts after more than one test was run.") 546 public void testProxyConnection() throws Throwable { 547 // setting up the properties pointing to the key/trust stores 548 setUpStoreProperties(); 549 550 try { 551 // create the SSLServerSocket which will be used by server side 552 ServerSocket ss = new ServerSocket(0); 553 554 // create the HostnameVerifier to check that Hostname verification 555 // is done 556 TestHostnameVerifier hnv = new TestHostnameVerifier(); 557 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 558 559 // create HttpsURLConnection to be tested 560 URL url = new URL("https://requested.host:55556/requested.data"); 561 HttpsURLConnection connection = (HttpsURLConnection) url 562 .openConnection(new Proxy(Proxy.Type.HTTP, 563 new InetSocketAddress("localhost", ss 564 .getLocalPort()))); 565 566 // perform the interaction between the peers and check the results 567 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 568 checkConnectionStateParameters(connection, peerSocket); 569 570 // should silently exit 571 connection.connect(); 572 } finally { 573 // roll the properties back to system values 574 tearDownStoreProperties(); 575 } 576 } 577 578 /** 579 * Tests HTTPS connection process made through the proxy server. 580 * Proxy server needs authentication. 581 */ 582 @TestTargets({ 583 @TestTargetNew( 584 level = TestLevel.PARTIAL, 585 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", 586 method = "setDoInput", 587 args = {boolean.class} 588 ), 589 @TestTargetNew( 590 level = TestLevel.PARTIAL, 591 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", 592 method = "setConnectTimeout", 593 args = {int.class} 594 ), 595 @TestTargetNew( 596 level = TestLevel.PARTIAL, 597 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", 598 method = "setReadTimeout", 599 args = {int.class} 600 ) 601 }) 602 @KnownFailure("VM aborts after more than one test was run.") 603 public void testProxyAuthConnection() throws Throwable { 604 // setting up the properties pointing to the key/trust stores 605 setUpStoreProperties(); 606 607 try { 608 // create the SSLServerSocket which will be used by server side 609 ServerSocket ss = new ServerSocket(0); 610 611 // create the HostnameVerifier to check that Hostname verification 612 // is done 613 TestHostnameVerifier hnv = new TestHostnameVerifier(); 614 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 615 616 Authenticator.setDefault(new Authenticator() { 617 618 protected PasswordAuthentication getPasswordAuthentication() { 619 return new PasswordAuthentication("user", "password" 620 .toCharArray()); 621 } 622 }); 623 624 // create HttpsURLConnection to be tested 625 URL url = new URL("https://requested.host:55555/requested.data"); 626 HttpsURLConnection connection = (HttpsURLConnection) url 627 .openConnection(new Proxy(Proxy.Type.HTTP, 628 new InetSocketAddress("localhost", ss 629 .getLocalPort()))); 630 631 // perform the interaction between the peers and check the results 632 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 633 checkConnectionStateParameters(connection, peerSocket); 634 635 // should silently exit 636 connection.connect(); 637 } finally { 638 // roll the properties back to system values 639 tearDownStoreProperties(); 640 } 641 } 642 643 /** 644 * Tests HTTPS connection process made through the proxy server. 645 * 2 HTTPS connections are opened for one URL. For the first time 646 * the connection is opened through one proxy, 647 * for the second time through another. 648 */ 649 @TestTargets({ 650 @TestTargetNew( 651 level = TestLevel.PARTIAL_COMPLETE, 652 notes = "Verifies HTTPS connection process made through the proxy server.", 653 method = "getCipherSuite", 654 args = {} 655 ), 656 @TestTargetNew( 657 level = TestLevel.PARTIAL_COMPLETE, 658 notes = "Verifies HTTPS connection process made through the proxy server.", 659 method = "getLocalPrincipal", 660 args = {} 661 ), 662 @TestTargetNew( 663 level = TestLevel.PARTIAL_COMPLETE, 664 notes = "Verifies HTTPS connection process made through the proxy server.", 665 method = "getPeerPrincipal", 666 args = {} 667 ) 668 }) 669 @KnownFailure("VM aborts after more than one test was run.") 670 public void testConsequentProxyConnection() throws Throwable { 671 // setting up the properties pointing to the key/trust stores 672 setUpStoreProperties(); 673 674 try { 675 // create the SSLServerSocket which will be used by server side 676 ServerSocket ss = new ServerSocket(0); 677 678 // create the HostnameVerifier to check that Hostname verification 679 // is done 680 TestHostnameVerifier hnv = new TestHostnameVerifier(); 681 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 682 683 // create HttpsURLConnection to be tested 684 URL url = new URL("https://requested.host:55555/requested.data"); 685 HttpsURLConnection connection = (HttpsURLConnection) url 686 .openConnection(new Proxy(Proxy.Type.HTTP, 687 new InetSocketAddress("localhost", ss 688 .getLocalPort()))); 689 690 // perform the interaction between the peers and check the results 691 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 692 checkConnectionStateParameters(connection, peerSocket); 693 694 // create another SSLServerSocket which will be used by server side 695 ss = new ServerSocket(0); 696 697 connection = (HttpsURLConnection) url.openConnection(new Proxy( 698 Proxy.Type.HTTP, new InetSocketAddress("localhost", ss 699 .getLocalPort()))); 700 701 // perform the interaction between the peers and check the results 702 peerSocket = (SSLSocket) doInteraction(connection, ss); 703 checkConnectionStateParameters(connection, peerSocket); 704 } finally { 705 // roll the properties back to system values 706 tearDownStoreProperties(); 707 } 708 } 709 710 /** 711 * Tests HTTPS connection process made through the proxy server. 712 * Proxy server needs authentication. 713 * Client sends data to the server. 714 */ 715 @TestTargets({ 716 @TestTargetNew( 717 level = TestLevel.PARTIAL, 718 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", 719 method = "setDoInput", 720 args = {boolean.class} 721 ), 722 @TestTargetNew( 723 level = TestLevel.PARTIAL, 724 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", 725 method = "setConnectTimeout", 726 args = {int.class} 727 ), 728 @TestTargetNew( 729 level = TestLevel.PARTIAL, 730 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", 731 method = "setReadTimeout", 732 args = {int.class} 733 ), 734 @TestTargetNew( 735 level = TestLevel.PARTIAL, 736 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", 737 method = "setDoOutput", 738 args = {boolean.class} 739 ) 740 }) 741 @KnownFailure("VM aborts after more than one test was run.") 742 public void testProxyAuthConnection_doOutput() throws Throwable { 743 // setting up the properties pointing to the key/trust stores 744 setUpStoreProperties(); 745 746 try { 747 // create the SSLServerSocket which will be used by server side 748 ServerSocket ss = new ServerSocket(0); 749 750 // create the HostnameVerifier to check that Hostname verification 751 // is done 752 TestHostnameVerifier hnv = new TestHostnameVerifier(); 753 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 754 755 Authenticator.setDefault(new Authenticator() { 756 757 protected PasswordAuthentication getPasswordAuthentication() { 758 return new PasswordAuthentication("user", "password" 759 .toCharArray()); 760 } 761 }); 762 763 // create HttpsURLConnection to be tested 764 URL url = new URL("https://requested.host:55554/requested.data"); 765 HttpsURLConnection connection = (HttpsURLConnection) url 766 .openConnection(new Proxy(Proxy.Type.HTTP, 767 new InetSocketAddress("localhost", ss 768 .getLocalPort()))); 769 connection.setDoOutput(true); 770 771 // perform the interaction between the peers and check the results 772 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss, 773 OK_CODE, true); 774 checkConnectionStateParameters(connection, peerSocket); 775 } finally { 776 // roll the properties back to system values 777 tearDownStoreProperties(); 778 } 779 } 780 781 /** 782 * Tests HTTPS connection process made through the proxy server. 783 * Proxy server needs authentication but client fails to authenticate 784 * (Authenticator was not set up in the system). 785 */ 786 @TestTargets({ 787 @TestTargetNew( 788 level = TestLevel.PARTIAL, 789 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).", 790 method = "setDoInput", 791 args = {boolean.class} 792 ), 793 @TestTargetNew( 794 level = TestLevel.PARTIAL, 795 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).", 796 method = "setConnectTimeout", 797 args = {int.class} 798 ), 799 @TestTargetNew( 800 level = TestLevel.PARTIAL, 801 notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).", 802 method = "setReadTimeout", 803 args = {int.class} 804 ) 805 }) 806 @KnownFailure("VM aborts after more than one test was run.") 807 public void testProxyAuthConnectionFailed() throws Throwable { 808 // setting up the properties pointing to the key/trust stores 809 setUpStoreProperties(); 810 811 try { 812 // create the SSLServerSocket which will be used by server side 813 ServerSocket ss = new ServerSocket(0); 814 815 // create the HostnameVerifier to check that Hostname verification 816 // is done 817 TestHostnameVerifier hnv = new TestHostnameVerifier(); 818 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 819 820 // create HttpsURLConnection to be tested 821 URL url = new URL("https://requested.host:55555/requested.data"); 822 HttpURLConnection connection = (HttpURLConnection) url 823 .openConnection(new Proxy(Proxy.Type.HTTP, 824 new InetSocketAddress("localhost", ss 825 .getLocalPort()))); 826 827 // perform the interaction between the peers and check the results 828 try { 829 doInteraction(connection, ss, AUTHENTICATION_REQUIRED_CODE, 830 true); 831 } catch (IOException e) { 832 // SSL Tunnelling failed 833 if (DO_LOG) { 834 System.out.println("Got expected IOException: " 835 + e.getMessage()); 836 } 837 } 838 } finally { 839 // roll the properties back to system values 840 tearDownStoreProperties(); 841 } 842 } 843 844 /** 845 * Tests the behaviour of HTTPS connection in case of unavailability 846 * of requested resource. 847 */ 848 @TestTargets({ 849 @TestTargetNew( 850 level = TestLevel.PARTIAL, 851 notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", 852 method = "setDoInput", 853 args = {boolean.class} 854 ), 855 @TestTargetNew( 856 level = TestLevel.PARTIAL, 857 notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", 858 method = "setConnectTimeout", 859 args = {int.class} 860 ), 861 @TestTargetNew( 862 level = TestLevel.PARTIAL, 863 notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", 864 method = "setReadTimeout", 865 args = {int.class} 866 ) 867 }) 868 @KnownFailure("VM aborts after more than one test was run.") 869 public void testProxyConnection_Not_Found_Response() throws Throwable { 870 // setting up the properties pointing to the key/trust stores 871 setUpStoreProperties(); 872 873 try { 874 // create the SSLServerSocket which will be used by server side 875 ServerSocket ss = new ServerSocket(0); 876 877 // create the HostnameVerifier to check that Hostname verification 878 // is done 879 TestHostnameVerifier hnv = new TestHostnameVerifier(); 880 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 881 882 // create HttpsURLConnection to be tested 883 URL url = new URL("https://localhost:" + ss.getLocalPort()); 884 HttpURLConnection connection = (HttpURLConnection) url 885 .openConnection(new Proxy(Proxy.Type.HTTP, 886 new InetSocketAddress("localhost", ss 887 .getLocalPort()))); 888 889 try { 890 doInteraction(connection, ss, NOT_FOUND_CODE); // NOT FOUND 891 fail("Expected exception was not thrown."); 892 } catch (FileNotFoundException e) { 893 if (DO_LOG) { 894 System.out.println("Expected exception was thrown: " 895 + e.getMessage()); 896 } 897 } 898 } finally { 899 // roll the properties back to system values 900 tearDownStoreProperties(); 901 } 902 } 903 904 // --------------------------------------------------------------------- 905 // ------------------------ Staff Methods ------------------------------ 906 // --------------------------------------------------------------------- 907 908 /** 909 * Log the name of the test case to be executed. 910 */ 911 public void setUp() throws Exception { 912 if (DO_LOG) { 913 System.out.println(); 914 System.out.println("------------------------"); 915 System.out.println("------ " + getName()); 916 System.out.println("------------------------"); 917 } 918 919 if (store != null) { 920 String ksFileName = "org/apache/harmony/luni/tests/key_store." 921 + KeyStore.getDefaultType().toLowerCase(); 922 InputStream in = ClassLoader.getSystemClassLoader() 923 .getResourceAsStream(ksFileName); 924 FileOutputStream out = new FileOutputStream(store); 925 BufferedInputStream bufIn = new BufferedInputStream(in, 8192); 926 while (bufIn.available() > 0) { 927 byte[] buf = new byte[128]; 928 int read = bufIn.read(buf); 929 out.write(buf, 0, read); 930 } 931 bufIn.close(); 932 out.close(); 933 } else { 934 fail("couldn't set up key store"); 935 } 936 } 937 938 public void tearDown() { 939 if (store != null) { 940 store.delete(); 941 } 942 } 943 944 /** 945 * Checks the HttpsURLConnection getter's values and compares 946 * them with actual corresponding values of remote peer. 947 */ 948 public static void checkConnectionStateParameters( 949 HttpsURLConnection clientConnection, SSLSocket serverPeer) 950 throws Exception { 951 SSLSession session = serverPeer.getSession(); 952 953 assertEquals(session.getCipherSuite(), clientConnection 954 .getCipherSuite()); 955 956 assertEquals(session.getLocalPrincipal(), clientConnection 957 .getPeerPrincipal()); 958 959 assertEquals(session.getPeerPrincipal(), clientConnection 960 .getLocalPrincipal()); 961 962 Certificate[] serverCertificates = clientConnection 963 .getServerCertificates(); 964 Certificate[] localCertificates = session.getLocalCertificates(); 965 assertTrue("Server certificates differ from expected", Arrays.equals( 966 serverCertificates, localCertificates)); 967 968 localCertificates = clientConnection.getLocalCertificates(); 969 serverCertificates = session.getPeerCertificates(); 970 assertTrue("Local certificates differ from expected", Arrays.equals( 971 serverCertificates, localCertificates)); 972 } 973 974 /** 975 * Returns the file name of the key/trust store. The key store file 976 * (named as "key_store." + extension equals to the default KeyStore 977 * type installed in the system in lower case) is searched in classpath. 978 * @throws AssertionFailedError if property was not set 979 * or file does not exist. 980 */ 981 private static String getKeyStoreFileName() { 982 return store.getAbsolutePath(); 983 } 984 985 /** 986 * Builds and returns the context used for secure socket creation. 987 */ 988 private static SSLContext getContext() throws Exception { 989 String type = KeyStore.getDefaultType(); 990 SSLContext ctx; 991 992 String keyStore = getKeyStoreFileName(); 993 File keyStoreFile = new File(keyStore); 994 995 FileInputStream fis = new FileInputStream(keyStoreFile); 996 997 KeyStore ks = KeyStore.getInstance(type); 998 ks.load(fis, KS_PASSWORD.toCharArray()); 999 1000 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory 1001 .getDefaultAlgorithm()); 1002 kmf.init(ks, KS_PASSWORD.toCharArray()); 1003 1004 TrustManagerFactory tmf = TrustManagerFactory 1005 .getInstance(TrustManagerFactory.getDefaultAlgorithm()); 1006 tmf.init(ks); 1007 1008 ctx = SSLContext.getInstance("TLSv1"); 1009 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 1010 1011 return ctx; 1012 } 1013 1014 /** 1015 * Sets up the properties pointing to the key store and trust store 1016 * and used as default values by JSSE staff. This is needed to test 1017 * HTTPS behaviour in the case of default SSL Socket Factories. 1018 */ 1019 private static void setUpStoreProperties() throws Exception { 1020 String type = KeyStore.getDefaultType(); 1021 1022 systemKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType"); 1023 systemKeyStore = System.getProperty("javax.net.ssl.keyStore"); 1024 systemKeyStorePassword = System 1025 .getProperty("javax.net.ssl.keyStorePassword"); 1026 1027 systemTrustStoreType = System 1028 .getProperty("javax.net.ssl.trustStoreType"); 1029 systemTrustStore = System.getProperty("javax.net.ssl.trustStore"); 1030 systemTrustStorePassword = System 1031 .getProperty("javax.net.ssl.trustStorePassword"); 1032 1033 System.setProperty("javax.net.ssl.keyStoreType", type); 1034 System.setProperty("javax.net.ssl.keyStore", getKeyStoreFileName()); 1035 System.setProperty("javax.net.ssl.keyStorePassword", KS_PASSWORD); 1036 1037 System.setProperty("javax.net.ssl.trustStoreType", type); 1038 System.setProperty("javax.net.ssl.trustStore", getKeyStoreFileName()); 1039 System.setProperty("javax.net.ssl.trustStorePassword", KS_PASSWORD); 1040 } 1041 1042 /** 1043 * Rolls back the values of system properties. 1044 */ 1045 private static void tearDownStoreProperties() { 1046 if (systemKeyStoreType == null) { 1047 System.clearProperty("javax.net.ssl.keyStoreType"); 1048 } else { 1049 System 1050 .setProperty("javax.net.ssl.keyStoreType", 1051 systemKeyStoreType); 1052 } 1053 if (systemKeyStore == null) { 1054 System.clearProperty("javax.net.ssl.keyStore"); 1055 } else { 1056 System.setProperty("javax.net.ssl.keyStore", systemKeyStore); 1057 } 1058 if (systemKeyStorePassword == null) { 1059 System.clearProperty("javax.net.ssl.keyStorePassword"); 1060 } else { 1061 System.setProperty("javax.net.ssl.keyStorePassword", 1062 systemKeyStorePassword); 1063 } 1064 1065 if (systemTrustStoreType == null) { 1066 System.clearProperty("javax.net.ssl.trustStoreType"); 1067 } else { 1068 System.setProperty("javax.net.ssl.trustStoreType", 1069 systemTrustStoreType); 1070 } 1071 if (systemTrustStore == null) { 1072 System.clearProperty("javax.net.ssl.trustStore"); 1073 } else { 1074 System.setProperty("javax.net.ssl.trustStore", systemTrustStore); 1075 } 1076 if (systemTrustStorePassword == null) { 1077 System.clearProperty("javax.net.ssl.trustStorePassword"); 1078 } else { 1079 System.setProperty("javax.net.ssl.trustStorePassword", 1080 systemTrustStorePassword); 1081 } 1082 } 1083 1084 /** 1085 * Performs interaction between client's HttpURLConnection and 1086 * servers side (ServerSocket). 1087 */ 1088 public static Socket doInteraction( 1089 final HttpURLConnection clientConnection, 1090 final ServerSocket serverSocket) throws Throwable { 1091 return doInteraction(clientConnection, serverSocket, OK_CODE, false); 1092 } 1093 1094 /** 1095 * Performs interaction between client's HttpURLConnection and 1096 * servers side (ServerSocket). Server will response with specified 1097 * response code. 1098 */ 1099 public static Socket doInteraction( 1100 final HttpURLConnection clientConnection, 1101 final ServerSocket serverSocket, final int responseCode) 1102 throws Throwable { 1103 return doInteraction(clientConnection, serverSocket, responseCode, 1104 false); 1105 } 1106 1107 /** 1108 * Performs interaction between client's HttpURLConnection and 1109 * servers side (ServerSocket). Server will response with specified 1110 * response code. 1111 * @param doAuthentication specifies 1112 * if the server needs client authentication. 1113 */ 1114 public static Socket doInteraction( 1115 final HttpURLConnection clientConnection, 1116 final ServerSocket serverSocket, final int responseCode, 1117 final boolean doAuthentication) throws Throwable { 1118 1119 // set up the connection 1120 clientConnection.setDoInput(true); 1121 clientConnection.setConnectTimeout(TIMEOUT); 1122 clientConnection.setReadTimeout(TIMEOUT); 1123 1124 ServerWork server = new ServerWork(serverSocket, responseCode, 1125 doAuthentication); 1126 1127 ClientConnectionWork client = new ClientConnectionWork(clientConnection); 1128 1129 server.start(); 1130 client.start(); 1131 1132 client.join(); 1133 server.join(); 1134 1135 if (client.thrown != null) { 1136 if (responseCode != OK_CODE) { // not OK response expected 1137 // it is probably expected exception, keep it as is 1138 throw client.thrown; 1139 } 1140 if ((client.thrown instanceof SocketTimeoutException) 1141 && (server.thrown != null)) { 1142 // server's exception is more informative in this case 1143 throw new Exception(server.thrown); 1144 } else { 1145 throw new Exception(client.thrown); 1146 } 1147 } 1148 if (server.thrown != null) { 1149 throw server.thrown; 1150 } 1151 return server.peerSocket; 1152 } 1153 1154 /** 1155 * The host name verifier used in test. 1156 */ 1157 static class TestHostnameVerifier implements HostnameVerifier { 1158 1159 boolean verified = false; 1160 1161 public boolean verify(String hostname, SSLSession session) { 1162 if (DO_LOG) { 1163 System.out.println("***> verification " + hostname + " " 1164 + session.getPeerHost()); 1165 } 1166 verified = true; 1167 return true; 1168 } 1169 } 1170 1171 /** 1172 * The base class for mock Client and Server. 1173 */ 1174 static class Work extends Thread { 1175 1176 /** 1177 * The header of OK HTTP response. 1178 */ 1179 static String responseHead = "HTTP/1.1 200 OK\n"; 1180 1181 /** 1182 * The content of the response. 1183 */ 1184 static String plainResponseContent = "<HTML>\n" 1185 + "<HEAD><TITLE>Plain Response Content</TITLE></HEAD>\n" 1186 + "</HTML>"; 1187 1188 /** 1189 * The tail of the response. 1190 */ 1191 static String plainResponseTail = "Content-type: text/html\n" 1192 + "Content-length: " + plainResponseContent.length() + "\n\n" 1193 + plainResponseContent; 1194 1195 /** 1196 * The response message to be sent in plain (HTTP) format. 1197 */ 1198 static String plainResponse = responseHead + plainResponseTail; 1199 1200 /** 1201 * The content of the response to be sent during HTTPS session. 1202 */ 1203 static String httpsResponseContent = "<HTML>\n" 1204 + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n" 1205 + "</HTML>"; 1206 1207 /** 1208 * The tail of the response to be sent during HTTPS session. 1209 */ 1210 static String httpsResponseTail = "Content-type: text/html\n" 1211 + "Content-length: " + httpsResponseContent.length() + "\n\n" 1212 + httpsResponseContent; 1213 1214 /** 1215 * The response requiring client's proxy authentication. 1216 */ 1217 static String respAuthenticationRequired = "HTTP/1.0 407 Proxy authentication required\n" 1218 + "Proxy-authenticate: Basic realm=\"localhost\"\n\n"; 1219 1220 /** 1221 * The data to be posted by client to the server. 1222 */ 1223 static String clientsData = "_.-^ Client's Data ^-._"; 1224 1225 /** 1226 * The exception thrown during peers interaction. 1227 */ 1228 protected Throwable thrown; 1229 1230 /** 1231 * The print stream used for debug log. 1232 * If it is null debug info will not be printed. 1233 */ 1234 private PrintStream out = new PrintStream(System.out); 1235 1236 /** 1237 * Prints log message. 1238 */ 1239 public synchronized void log(String message) { 1240 if (DO_LOG && (out != null)) { 1241 System.out.println("[" + getName() + "]: " + message); 1242 } 1243 } 1244 } 1245 1246 /** 1247 * The class used for server side works. 1248 */ 1249 static class ServerWork extends Work { 1250 1251 // the server socket used for connection 1252 private ServerSocket serverSocket; 1253 1254 // the socket connected with client peer 1255 private Socket peerSocket; 1256 1257 // indicates if the server acts as proxy server 1258 private boolean actAsProxy; 1259 1260 // indicates if the server needs proxy authentication 1261 private boolean needProxyAuthentication; 1262 1263 // response code to be send to the client peer 1264 private int responseCode; 1265 1266 /** 1267 * Creates the thread acting as a server side. 1268 */ 1269 public ServerWork(ServerSocket serverSocket) { 1270 // the server does not require proxy authentication 1271 // and sends OK_CODE (OK) response code 1272 this(serverSocket, OK_CODE, false); 1273 } 1274 1275 /** 1276 * Creates the thread acting as a server side. 1277 * @param serverSocket the server socket to be used during connection 1278 * @param responseCode the response code to be sent to the client 1279 * @param needProxyAuthentication 1280 * indicates if the server needs proxy authentication 1281 */ 1282 public ServerWork(ServerSocket serverSocket, int responseCode, 1283 boolean needProxyAuthentication) { 1284 this.serverSocket = serverSocket; 1285 this.responseCode = responseCode; 1286 this.needProxyAuthentication = needProxyAuthentication; 1287 // will act as a proxy server if the specified server socket 1288 // is not a secure server socket 1289 if (serverSocket instanceof SSLServerSocket) { 1290 // demand client to send its certificate 1291 ((SSLServerSocket) serverSocket).setNeedClientAuth(true); 1292 // work as a HTTPS server, not as HTTP proxy 1293 this.actAsProxy = false; 1294 } else { 1295 this.actAsProxy = true; 1296 } 1297 this.actAsProxy = !(serverSocket instanceof SSLServerSocket); 1298 setName(this.actAsProxy ? "Proxy Server" : "Server"); 1299 } 1300 1301 /** 1302 * Closes the connection. 1303 */ 1304 public void closeSocket(Socket socket) { 1305 try { 1306 socket.getInputStream().close(); 1307 } catch (IOException e) {} 1308 try { 1309 socket.getOutputStream().close(); 1310 } catch (IOException e) {} 1311 try { 1312 socket.close(); 1313 } catch (IOException e) {} 1314 } 1315 1316 /** 1317 * Performs the actual server work. 1318 * If some exception occurs during the work it will be 1319 * stored in the <code>thrown</code> field. 1320 */ 1321 public void run() { 1322 // the buffer used for reading the messages 1323 byte[] buff = new byte[2048]; 1324 // the number of bytes read into the buffer 1325 int num; 1326 try { 1327 // configure the server socket to avoid blocking 1328 serverSocket.setSoTimeout(TIMEOUT); 1329 // accept client connection 1330 peerSocket = serverSocket.accept(); 1331 // configure the client connection to avoid blocking 1332 peerSocket.setSoTimeout(TIMEOUT); 1333 log("Client connection ACCEPTED"); 1334 1335 InputStream is = peerSocket.getInputStream(); 1336 OutputStream os = peerSocket.getOutputStream(); 1337 1338 num = is.read(buff); 1339 String message = new String(buff, 0, num); 1340 log("Got request:\n" + message); 1341 log("------------------"); 1342 1343 if (!actAsProxy) { 1344 // Act as Server (not Proxy) side 1345 if (message.startsWith("POST")) { 1346 // client connection sent some data 1347 log("try to read client data"); 1348 num = is.read(buff); 1349 message = new String(buff, 0, num); 1350 log("client's data: '" + message + "'"); 1351 // check the received data 1352 assertEquals(clientsData, message); 1353 } 1354 // just send the response 1355 os 1356 .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) 1357 .getBytes()); 1358 // and return 1359 log("Work is DONE"); 1360 return; 1361 } 1362 1363 // Do proxy work 1364 if (needProxyAuthentication) { 1365 log("Authentication required ..."); 1366 // send Authentication Request 1367 os.write(respAuthenticationRequired.getBytes()); 1368 // read response 1369 num = is.read(buff); 1370 if (num == -1) { 1371 // this connection was closed, 1372 // do clean up and create new one: 1373 closeSocket(peerSocket); 1374 peerSocket = serverSocket.accept(); 1375 peerSocket.setSoTimeout(TIMEOUT); 1376 log("New client connection ACCEPTED"); 1377 is = peerSocket.getInputStream(); 1378 os = peerSocket.getOutputStream(); 1379 num = is.read(buff); 1380 } 1381 message = new String(buff, 0, num); 1382 log("Got authenticated request:\n" + message); 1383 log("------------------"); 1384 // check provided authorization credentials 1385 assertTrue("Received message does not contain " 1386 + "authorization credentials", message 1387 .toLowerCase().indexOf("proxy-authorization:") > 0); 1388 } 1389 1390 // The content of this response will reach proxied HTTPUC 1391 // but will not reach proxied HTTPSUC 1392 // In case of HTTP connection it will be the final message, 1393 // in case of HTTPS connection this message will just indicate 1394 // that connection with remote host has been done 1395 // (i.e. SSL tunnel has been established). 1396 os.write(plainResponse.getBytes()); 1397 log("Sent OK RESPONSE"); 1398 1399 if (message.startsWith("CONNECT")) { // request for SSL tunnel 1400 log("Perform SSL Handshake..."); 1401 // create sslSocket acting as a remote server peer 1402 SSLSocket sslSocket = (SSLSocket) getContext() 1403 .getSocketFactory().createSocket(peerSocket, 1404 "localhost", peerSocket.getPort(), true); // do autoclose 1405 sslSocket.setUseClientMode(false); 1406 // demand client authentication 1407 sslSocket.setNeedClientAuth(true); 1408 sslSocket.startHandshake(); 1409 peerSocket = sslSocket; 1410 is = peerSocket.getInputStream(); 1411 os = peerSocket.getOutputStream(); 1412 1413 // read the HTTP request sent by secure connection 1414 // (HTTPS request) 1415 num = is.read(buff); 1416 message = new String(buff, 0, num); 1417 log("[Remote Server] Request from SSL tunnel:\n" + message); 1418 log("------------------"); 1419 1420 if (message.startsWith("POST")) { 1421 // client connection sent some data 1422 log("[Remote Server] try to read client data"); 1423 num = is.read(buff); 1424 message = new String(buff, 0, num); 1425 log("[Remote Server] client's data: '" + message + "'"); 1426 // check the received data 1427 assertEquals(clientsData, message); 1428 } 1429 1430 log("[Remote Server] Sending the response by SSL tunnel.."); 1431 // send the response with specified response code 1432 os 1433 .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) 1434 .getBytes()); 1435 } 1436 log("Work is DONE"); 1437 } catch (Throwable e) { 1438 if (DO_LOG) { 1439 e.printStackTrace(); 1440 } 1441 thrown = e; 1442 } finally { 1443 closeSocket(peerSocket); 1444 try { 1445 serverSocket.close(); 1446 } catch (IOException e) {} 1447 } 1448 } 1449 } 1450 1451 /** 1452 * The class used for client side works. It could be used to test 1453 * both HttpURLConnection and HttpsURLConnection. 1454 */ 1455 static class ClientConnectionWork extends Work { 1456 1457 // connection to be used to contact the server side 1458 private HttpURLConnection connection; 1459 1460 /** 1461 * Creates the thread acting as a client side. 1462 * @param connection connection to be used to contact the server side 1463 */ 1464 public ClientConnectionWork(HttpURLConnection connection) { 1465 this.connection = connection; 1466 setName("Client Connection"); 1467 log("Created over connection: " + connection.getClass()); 1468 } 1469 1470 /** 1471 * Performs the actual client work. 1472 * If some exception occurs during the work it will be 1473 * stored in the <code>thrown<code> field. 1474 */ 1475 public void run() { 1476 try { 1477 log("Opening the connection.."); 1478 connection.connect(); 1479 log("Connection has been ESTABLISHED, using proxy: " 1480 + connection.usingProxy()); 1481 if (connection.getDoOutput()) { 1482 // connection configured to post data, do so 1483 connection.getOutputStream().write(clientsData.getBytes()); 1484 } 1485 // read the content of HTTP(s) response 1486 InputStream is = connection.getInputStream(); 1487 log("Input Stream obtained"); 1488 byte[] buff = new byte[2048]; 1489 int num = 0; 1490 int byt = 0; 1491 while ((num < buff.length) && (is.available() > 0) 1492 && ((byt = is.read()) != -1)) { 1493 buff[num++] = (byte) byt; 1494 } 1495 String message = new String(buff, 0, num); 1496 log("Got content:\n" + message); 1497 log("------------------"); 1498 log("Response code: " + connection.getResponseCode()); 1499 1500 if (connection instanceof HttpsURLConnection) { 1501 assertEquals(httpsResponseContent, message); 1502 } else { 1503 assertEquals(plainResponseContent, message); 1504 } 1505 } catch (Throwable e) { 1506 if (DO_LOG) { 1507 e.printStackTrace(); 1508 } 1509 thrown = e; 1510 } 1511 } 1512 } 1513} 1514