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