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