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