| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.luni.tests.internal.net.www.protocol.https; |
| |
| import dalvik.annotation.AndroidOnly; |
| import dalvik.annotation.KnownFailure; |
| import dalvik.annotation.TestTargetClass; |
| import dalvik.annotation.TestTargets; |
| import dalvik.annotation.TestLevel; |
| import dalvik.annotation.TestTargetNew; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.net.Authenticator; |
| import java.net.HttpURLConnection; |
| import java.net.InetSocketAddress; |
| import java.net.PasswordAuthentication; |
| import java.net.Proxy; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.net.SocketTimeoutException; |
| import java.net.URL; |
| import java.security.KeyStore; |
| import java.security.cert.Certificate; |
| import java.util.Arrays; |
| import javax.net.ssl.HostnameVerifier; |
| import javax.net.ssl.HttpsURLConnection; |
| import javax.net.ssl.KeyManagerFactory; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLServerSocket; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.SSLSocket; |
| import javax.net.ssl.SSLSocketFactory; |
| import javax.net.ssl.TrustManagerFactory; |
| |
| import junit.framework.TestCase; |
| |
| /** |
| * Implementation independent test for HttpsURLConnection. |
| * The test needs certstore file placed in system classpath |
| * and named as "key_store." + the type of the |
| * default KeyStore installed in the system in lower case. |
| * <br> |
| * For example: if default KeyStore type in the system is BKS |
| * (i.e. java.security file sets up the property keystore.type=BKS), |
| * thus classpath should point to the directory with "key_store.bks" |
| * file. |
| * <br> |
| * This certstore file should contain self-signed certificate |
| * generated by keytool utility in a usual way. |
| * <br> |
| * The password to the certstore should be "password" (without quotes). |
| */ |
| @TestTargetClass(HttpsURLConnection.class) |
| public class HttpsURLConnectionTest extends TestCase { |
| |
| // the password to the store |
| private static final String KS_PASSWORD = "password"; |
| |
| // turn on/off logging |
| private static final boolean DO_LOG = false; |
| |
| // read/connection timeout value |
| private static final int TIMEOUT = 5000; |
| |
| // OK response code |
| private static final int OK_CODE = 200; |
| |
| // Not Found response code |
| private static final int NOT_FOUND_CODE = 404; |
| |
| // Proxy authentication required response code |
| private static final int AUTHENTICATION_REQUIRED_CODE = 407; |
| |
| // fields keeping the system values of corresponding properties |
| private static String systemKeyStoreType; |
| |
| private static String systemKeyStore; |
| |
| private static String systemKeyStorePassword; |
| |
| private static String systemTrustStoreType; |
| |
| private static String systemTrustStore; |
| |
| private static String systemTrustStorePassword; |
| |
| private static File store; |
| |
| static { |
| try { |
| store = File.createTempFile("key_store", "bks"); |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| |
| /** |
| * Checks that HttpsURLConnection's default SSLSocketFactory is operable. |
| */ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies that HttpsURLConnection's default SSLSocketFactory is operable.", |
| method = "getDefaultSSLSocketFactory", |
| args = {} |
| ) |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testGetDefaultSSLSocketFactory() throws Exception { |
| // set up the properties defining the default values needed by SSL stuff |
| setUpStoreProperties(); |
| |
| try { |
| SSLSocketFactory defaultSSLSF = HttpsURLConnection |
| .getDefaultSSLSocketFactory(); |
| ServerSocket ss = new ServerSocket(0); |
| Socket s = defaultSSLSF |
| .createSocket("localhost", ss.getLocalPort()); |
| ss.accept(); |
| s.close(); |
| ss.close(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Checks if HTTPS connection performs initial SSL handshake with the |
| * server working over SSL, sends encrypted HTTP request, |
| * and receives expected HTTP response. After HTTPS session if finished |
| * test checks connection state parameters established by |
| * HttpsURLConnection. |
| */ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies if HTTPS connection performs initial SSL handshake with the server working over SSL, sends encrypted HTTP request, and receives expected HTTP response.", |
| method = "setDefaultHostnameVerifier", |
| args = {javax.net.ssl.HostnameVerifier.class} |
| ) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testHttpsConnection() throws Throwable { |
| // set up the properties defining the default values needed by SSL stuff |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSL server socket acting as a server |
| SSLContext ctx = getContext(); |
| ServerSocket ss = ctx.getServerSocketFactory() |
| .createServerSocket(0); |
| |
| // create the HostnameVerifier to check hostname verification |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create url connection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| |
| // perform the interaction between the peers |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| |
| // check the connection state |
| checkConnectionStateParameters(connection, peerSocket); |
| |
| // should silently exit |
| connection.connect(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests the behaviour of HTTPS connection in case of unavailability |
| * of requested resource. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", |
| method = "setDoInput", |
| args = {boolean.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", |
| method = "setConnectTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", |
| method = "setReadTimeout", |
| args = {int.class} |
| ) |
| }) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testHttpsConnection_Not_Found_Response() throws Throwable { |
| // set up the properties defining the default values needed by SSL stuff |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSL server socket acting as a server |
| SSLContext ctx = getContext(); |
| ServerSocket ss = ctx.getServerSocketFactory() |
| .createServerSocket(0); |
| |
| // create the HostnameVerifier to check hostname verification |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create url connection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| |
| try { |
| doInteraction(connection, ss, NOT_FOUND_CODE); |
| fail("Expected exception was not thrown."); |
| } catch (FileNotFoundException e) { |
| if (DO_LOG) { |
| System.out.println("Expected exception was thrown: " |
| + e.getMessage()); |
| } |
| } |
| |
| // should silently exit |
| connection.connect(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests possibility to set up the default SSLSocketFactory |
| * to be used by HttpsURLConnection. |
| */ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies possibility to set up the default SSLSocketFactory to be used by HttpsURLConnection.", |
| method = "setDefaultSSLSocketFactory", |
| args = {javax.net.ssl.SSLSocketFactory.class} |
| ) |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| @KnownFailure("End to end test fails. No response data is transferred from server to client") |
| public void testSetDefaultSSLSocketFactory() throws Throwable { |
| // create the SSLServerSocket which will be used by server side |
| SSLContext ctx = getContext(); |
| SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() |
| .createServerSocket(0); |
| |
| SSLSocketFactory socketFactory = (SSLSocketFactory) ctx |
| .getSocketFactory(); |
| // set up the factory as default |
| HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory); |
| // check the result |
| assertSame("Default SSLSocketFactory differs from expected", |
| socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory()); |
| |
| // create the HostnameVerifier to check hostname verification |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| |
| TestHostnameVerifier hnv_late = new TestHostnameVerifier(); |
| // late initialization: should not be used for created connection |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); |
| |
| // perform the interaction between the peers |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| // check the connection state |
| checkConnectionStateParameters(connection, peerSocket); |
| // check the verification process |
| assertTrue("Hostname verification was not done", hnv.verified); |
| assertFalse( |
| "Hostname verification should not be done by this verifier", |
| hnv_late.verified); |
| // check the used SSLSocketFactory |
| assertSame("Default SSLSocketFactory should be used", |
| HttpsURLConnection.getDefaultSSLSocketFactory(), connection |
| .getSSLSocketFactory()); |
| |
| // should silently exit |
| connection.connect(); |
| } |
| |
| /** |
| * Tests possibility to set up the SSLSocketFactory |
| * to be used by HttpsURLConnection. |
| */ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies possibility to set up the SSLSocketFactory to be used by HttpsURLConnection.", |
| method = "setSSLSocketFactory", |
| args = {javax.net.ssl.SSLSocketFactory.class} |
| ) |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| @KnownFailure("End to end test fails. No response data is transferred from server to client") |
| public void testSetSSLSocketFactory() throws Throwable { |
| // create the SSLServerSocket which will be used by server side |
| SSLContext ctx = getContext(); |
| SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() |
| .createServerSocket(0); |
| |
| // create the HostnameVerifier to check hostname verification |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| |
| SSLSocketFactory socketFactory = (SSLSocketFactory) ctx |
| .getSocketFactory(); |
| connection.setSSLSocketFactory(socketFactory); |
| |
| TestHostnameVerifier hnv_late = new TestHostnameVerifier(); |
| // late initialization: should not be used for created connection |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); |
| |
| // perform the interaction between the peers |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| // check the connection state |
| checkConnectionStateParameters(connection, peerSocket); |
| // check the verification process |
| assertTrue("Hostname verification was not done", hnv.verified); |
| assertFalse( |
| "Hostname verification should not be done by this verifier", |
| hnv_late.verified); |
| // check the used SSLSocketFactory |
| assertNotSame("Default SSLSocketFactory should not be used", |
| HttpsURLConnection.getDefaultSSLSocketFactory(), connection |
| .getSSLSocketFactory()); |
| assertSame("Result differs from expected", socketFactory, connection |
| .getSSLSocketFactory()); |
| |
| // should silently exit |
| connection.connect(); |
| } |
| |
| /** |
| * Tests the behaviour of HttpsURLConnection in case of retrieving |
| * of the connection state parameters before connection has been made. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", |
| method = "getCipherSuite", |
| args = {} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", |
| method = "getPeerPrincipal", |
| args = {} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", |
| method = "getLocalPrincipal", |
| args = {} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", |
| method = "getServerCertificates", |
| args = {} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", |
| method = "getLocalCertificates", |
| args = {} |
| ) |
| }) |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testUnconnectedStateParameters() throws Throwable { |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://localhost:55555"); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| |
| try { |
| connection.getCipherSuite(); |
| fail("Expected IllegalStateException was not thrown"); |
| } catch (IllegalStateException e) {} |
| try { |
| connection.getPeerPrincipal(); |
| fail("Expected IllegalStateException was not thrown"); |
| } catch (IllegalStateException e) {} |
| try { |
| connection.getLocalPrincipal(); |
| fail("Expected IllegalStateException was not thrown"); |
| } catch (IllegalStateException e) {} |
| |
| try { |
| connection.getServerCertificates(); |
| fail("Expected IllegalStateException was not thrown"); |
| } catch (IllegalStateException e) {} |
| try { |
| connection.getLocalCertificates(); |
| fail("Expected IllegalStateException was not thrown"); |
| } catch (IllegalStateException e) {} |
| } |
| |
| /** |
| * Tests if setHostnameVerifier() method replaces default verifier. |
| */ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies if setHostnameVerifier() method replaces default verifier.", |
| method = "setHostnameVerifier", |
| args = {javax.net.ssl.HostnameVerifier.class} |
| ) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testSetHostnameVerifier() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| SSLServerSocket ss = (SSLServerSocket) getContext() |
| .getServerSocketFactory().createServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| |
| TestHostnameVerifier hnv_late = new TestHostnameVerifier(); |
| // replace default verifier |
| connection.setHostnameVerifier(hnv_late); |
| |
| // perform the interaction between the peers and check the results |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| assertTrue("Hostname verification was not done", hnv_late.verified); |
| assertFalse( |
| "Hostname verification should not be done by this verifier", |
| hnv.verified); |
| checkConnectionStateParameters(connection, peerSocket); |
| |
| // should silently exit |
| connection.connect(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests the behaviour in case of sending the data to the server. |
| */ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour in case of sending the data to the server.", |
| method = "setDoOutput", |
| args = {boolean.class} |
| ) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void test_doOutput() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| SSLServerSocket ss = (SSLServerSocket) getContext() |
| .getServerSocketFactory().createServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(); |
| connection.setDoOutput(true); |
| |
| // perform the interaction between the peers and check the results |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| checkConnectionStateParameters(connection, peerSocket); |
| |
| // should silently exit |
| connection.connect(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests HTTPS connection process made through the proxy server. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server.", |
| method = "setDoInput", |
| args = {boolean.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server.", |
| method = "setConnectTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server.", |
| method = "setReadTimeout", |
| args = {int.class} |
| ) |
| }) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testProxyConnection() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| ServerSocket ss = new ServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://requested.host:55556/requested.data"); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(new Proxy(Proxy.Type.HTTP, |
| new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| |
| // perform the interaction between the peers and check the results |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| checkConnectionStateParameters(connection, peerSocket); |
| |
| // should silently exit |
| connection.connect(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests HTTPS connection process made through the proxy server. |
| * Proxy server needs authentication. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", |
| method = "setDoInput", |
| args = {boolean.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", |
| method = "setConnectTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", |
| method = "setReadTimeout", |
| args = {int.class} |
| ) |
| }) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testProxyAuthConnection() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| ServerSocket ss = new ServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| Authenticator.setDefault(new Authenticator() { |
| |
| protected PasswordAuthentication getPasswordAuthentication() { |
| return new PasswordAuthentication("user", "password" |
| .toCharArray()); |
| } |
| }); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://requested.host:55555/requested.data"); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(new Proxy(Proxy.Type.HTTP, |
| new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| |
| // perform the interaction between the peers and check the results |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| checkConnectionStateParameters(connection, peerSocket); |
| |
| // should silently exit |
| connection.connect(); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests HTTPS connection process made through the proxy server. |
| * 2 HTTPS connections are opened for one URL. For the first time |
| * the connection is opened through one proxy, |
| * for the second time through another. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies HTTPS connection process made through the proxy server.", |
| method = "getCipherSuite", |
| args = {} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies HTTPS connection process made through the proxy server.", |
| method = "getLocalPrincipal", |
| args = {} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL_COMPLETE, |
| notes = "Verifies HTTPS connection process made through the proxy server.", |
| method = "getPeerPrincipal", |
| args = {} |
| ) |
| }) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testConsequentProxyConnection() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| ServerSocket ss = new ServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://requested.host:55555/requested.data"); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(new Proxy(Proxy.Type.HTTP, |
| new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| |
| // perform the interaction between the peers and check the results |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); |
| checkConnectionStateParameters(connection, peerSocket); |
| |
| // create another SSLServerSocket which will be used by server side |
| ss = new ServerSocket(0); |
| |
| connection = (HttpsURLConnection) url.openConnection(new Proxy( |
| Proxy.Type.HTTP, new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| |
| // perform the interaction between the peers and check the results |
| peerSocket = (SSLSocket) doInteraction(connection, ss); |
| checkConnectionStateParameters(connection, peerSocket); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests HTTPS connection process made through the proxy server. |
| * Proxy server needs authentication. |
| * Client sends data to the server. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", |
| method = "setDoInput", |
| args = {boolean.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", |
| method = "setConnectTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", |
| method = "setReadTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", |
| method = "setDoOutput", |
| args = {boolean.class} |
| ) |
| }) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testProxyAuthConnection_doOutput() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| ServerSocket ss = new ServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| Authenticator.setDefault(new Authenticator() { |
| |
| protected PasswordAuthentication getPasswordAuthentication() { |
| return new PasswordAuthentication("user", "password" |
| .toCharArray()); |
| } |
| }); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://requested.host:55554/requested.data"); |
| HttpsURLConnection connection = (HttpsURLConnection) url |
| .openConnection(new Proxy(Proxy.Type.HTTP, |
| new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| connection.setDoOutput(true); |
| |
| // perform the interaction between the peers and check the results |
| SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss, |
| OK_CODE, true); |
| checkConnectionStateParameters(connection, peerSocket); |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests 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). |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| 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).", |
| method = "setDoInput", |
| args = {boolean.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| 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).", |
| method = "setConnectTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| 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).", |
| method = "setReadTimeout", |
| args = {int.class} |
| ) |
| }) |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testProxyAuthConnectionFailed() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| ServerSocket ss = new ServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://requested.host:55555/requested.data"); |
| HttpURLConnection connection = (HttpURLConnection) url |
| .openConnection(new Proxy(Proxy.Type.HTTP, |
| new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| |
| // perform the interaction between the peers and check the results |
| try { |
| doInteraction(connection, ss, AUTHENTICATION_REQUIRED_CODE, |
| true); |
| } catch (IOException e) { |
| // SSL Tunnelling failed |
| if (DO_LOG) { |
| System.out.println("Got expected IOException: " |
| + e.getMessage()); |
| } |
| } |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| /** |
| * Tests the behaviour of HTTPS connection in case of unavailability |
| * of requested resource. |
| */ |
| @TestTargets({ |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", |
| method = "setDoInput", |
| args = {boolean.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", |
| method = "setConnectTimeout", |
| args = {int.class} |
| ), |
| @TestTargetNew( |
| level = TestLevel.PARTIAL, |
| notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", |
| method = "setReadTimeout", |
| args = {int.class} |
| ) |
| }) |
| @KnownFailure("Handshake fails.") |
| @AndroidOnly("we only have a .bks key store in the test resources") |
| public void testProxyConnection_Not_Found_Response() throws Throwable { |
| // setting up the properties pointing to the key/trust stores |
| setUpStoreProperties(); |
| |
| try { |
| // create the SSLServerSocket which will be used by server side |
| ServerSocket ss = new ServerSocket(0); |
| |
| // create the HostnameVerifier to check that Hostname verification |
| // is done |
| TestHostnameVerifier hnv = new TestHostnameVerifier(); |
| HttpsURLConnection.setDefaultHostnameVerifier(hnv); |
| |
| // create HttpsURLConnection to be tested |
| URL url = new URL("https://localhost:" + ss.getLocalPort()); |
| HttpURLConnection connection = (HttpURLConnection) url |
| .openConnection(new Proxy(Proxy.Type.HTTP, |
| new InetSocketAddress("localhost", ss |
| .getLocalPort()))); |
| |
| try { |
| doInteraction(connection, ss, NOT_FOUND_CODE); // NOT FOUND |
| fail("Expected exception was not thrown."); |
| } catch (FileNotFoundException e) { |
| if (DO_LOG) { |
| System.out.println("Expected exception was thrown: " |
| + e.getMessage()); |
| } |
| } |
| } finally { |
| // roll the properties back to system values |
| tearDownStoreProperties(); |
| } |
| } |
| |
| // --------------------------------------------------------------------- |
| // ------------------------ Staff Methods ------------------------------ |
| // --------------------------------------------------------------------- |
| |
| /** |
| * Log the name of the test case to be executed. |
| */ |
| public void setUp() throws Exception { |
| if (DO_LOG) { |
| System.out.println(); |
| System.out.println("------------------------"); |
| System.out.println("------ " + getName()); |
| System.out.println("------------------------"); |
| } |
| |
| if (store != null) { |
| String ksFileName = "org/apache/harmony/luni/tests/key_store." |
| + KeyStore.getDefaultType().toLowerCase(); |
| InputStream in = ClassLoader.getSystemClassLoader() |
| .getResourceAsStream(ksFileName); |
| FileOutputStream out = new FileOutputStream(store); |
| BufferedInputStream bufIn = new BufferedInputStream(in, 8192); |
| while (bufIn.available() > 0) { |
| byte[] buf = new byte[128]; |
| int read = bufIn.read(buf); |
| out.write(buf, 0, read); |
| } |
| bufIn.close(); |
| out.close(); |
| } else { |
| fail("couldn't set up key store"); |
| } |
| } |
| |
| public void tearDown() { |
| if (store != null) { |
| store.delete(); |
| } |
| } |
| |
| /** |
| * Checks the HttpsURLConnection getter's values and compares |
| * them with actual corresponding values of remote peer. |
| */ |
| public static void checkConnectionStateParameters( |
| HttpsURLConnection clientConnection, SSLSocket serverPeer) |
| throws Exception { |
| SSLSession session = serverPeer.getSession(); |
| |
| assertEquals(session.getCipherSuite(), clientConnection |
| .getCipherSuite()); |
| |
| assertEquals(session.getLocalPrincipal(), clientConnection |
| .getPeerPrincipal()); |
| |
| assertEquals(session.getPeerPrincipal(), clientConnection |
| .getLocalPrincipal()); |
| |
| Certificate[] serverCertificates = clientConnection |
| .getServerCertificates(); |
| Certificate[] localCertificates = session.getLocalCertificates(); |
| assertTrue("Server certificates differ from expected", Arrays.equals( |
| serverCertificates, localCertificates)); |
| |
| localCertificates = clientConnection.getLocalCertificates(); |
| serverCertificates = session.getPeerCertificates(); |
| assertTrue("Local certificates differ from expected", Arrays.equals( |
| serverCertificates, localCertificates)); |
| } |
| |
| /** |
| * Returns the file name of the key/trust store. The key store file |
| * (named as "key_store." + extension equals to the default KeyStore |
| * type installed in the system in lower case) is searched in classpath. |
| * @throws AssertionFailedError if property was not set |
| * or file does not exist. |
| */ |
| private static String getKeyStoreFileName() { |
| return store.getAbsolutePath(); |
| } |
| |
| /** |
| * Builds and returns the context used for secure socket creation. |
| */ |
| private static SSLContext getContext() throws Exception { |
| String type = KeyStore.getDefaultType(); |
| SSLContext ctx; |
| |
| String keyStore = getKeyStoreFileName(); |
| File keyStoreFile = new File(keyStore); |
| |
| FileInputStream fis = new FileInputStream(keyStoreFile); |
| |
| KeyStore ks = KeyStore.getInstance(type); |
| ks.load(fis, KS_PASSWORD.toCharArray()); |
| |
| KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory |
| .getDefaultAlgorithm()); |
| kmf.init(ks, KS_PASSWORD.toCharArray()); |
| |
| TrustManagerFactory tmf = TrustManagerFactory |
| .getInstance(TrustManagerFactory.getDefaultAlgorithm()); |
| tmf.init(ks); |
| |
| ctx = SSLContext.getInstance("TLSv1"); |
| ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); |
| |
| return ctx; |
| } |
| |
| /** |
| * Sets up the properties pointing to the key store and trust store |
| * and used as default values by JSSE staff. This is needed to test |
| * HTTPS behaviour in the case of default SSL Socket Factories. |
| */ |
| private static void setUpStoreProperties() throws Exception { |
| String type = KeyStore.getDefaultType(); |
| |
| systemKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType"); |
| systemKeyStore = System.getProperty("javax.net.ssl.keyStore"); |
| systemKeyStorePassword = System |
| .getProperty("javax.net.ssl.keyStorePassword"); |
| |
| systemTrustStoreType = System |
| .getProperty("javax.net.ssl.trustStoreType"); |
| systemTrustStore = System.getProperty("javax.net.ssl.trustStore"); |
| systemTrustStorePassword = System |
| .getProperty("javax.net.ssl.trustStorePassword"); |
| |
| System.setProperty("javax.net.ssl.keyStoreType", type); |
| System.setProperty("javax.net.ssl.keyStore", getKeyStoreFileName()); |
| System.setProperty("javax.net.ssl.keyStorePassword", KS_PASSWORD); |
| |
| System.setProperty("javax.net.ssl.trustStoreType", type); |
| System.setProperty("javax.net.ssl.trustStore", getKeyStoreFileName()); |
| System.setProperty("javax.net.ssl.trustStorePassword", KS_PASSWORD); |
| } |
| |
| /** |
| * Rolls back the values of system properties. |
| */ |
| private static void tearDownStoreProperties() { |
| if (systemKeyStoreType == null) { |
| System.clearProperty("javax.net.ssl.keyStoreType"); |
| } else { |
| System |
| .setProperty("javax.net.ssl.keyStoreType", |
| systemKeyStoreType); |
| } |
| if (systemKeyStore == null) { |
| System.clearProperty("javax.net.ssl.keyStore"); |
| } else { |
| System.setProperty("javax.net.ssl.keyStore", systemKeyStore); |
| } |
| if (systemKeyStorePassword == null) { |
| System.clearProperty("javax.net.ssl.keyStorePassword"); |
| } else { |
| System.setProperty("javax.net.ssl.keyStorePassword", |
| systemKeyStorePassword); |
| } |
| |
| if (systemTrustStoreType == null) { |
| System.clearProperty("javax.net.ssl.trustStoreType"); |
| } else { |
| System.setProperty("javax.net.ssl.trustStoreType", |
| systemTrustStoreType); |
| } |
| if (systemTrustStore == null) { |
| System.clearProperty("javax.net.ssl.trustStore"); |
| } else { |
| System.setProperty("javax.net.ssl.trustStore", systemTrustStore); |
| } |
| if (systemTrustStorePassword == null) { |
| System.clearProperty("javax.net.ssl.trustStorePassword"); |
| } else { |
| System.setProperty("javax.net.ssl.trustStorePassword", |
| systemTrustStorePassword); |
| } |
| } |
| |
| /** |
| * Performs interaction between client's HttpURLConnection and |
| * servers side (ServerSocket). |
| */ |
| public static Socket doInteraction( |
| final HttpURLConnection clientConnection, |
| final ServerSocket serverSocket) throws Throwable { |
| return doInteraction(clientConnection, serverSocket, OK_CODE, false); |
| } |
| |
| /** |
| * Performs interaction between client's HttpURLConnection and |
| * servers side (ServerSocket). Server will response with specified |
| * response code. |
| */ |
| public static Socket doInteraction( |
| final HttpURLConnection clientConnection, |
| final ServerSocket serverSocket, final int responseCode) |
| throws Throwable { |
| return doInteraction(clientConnection, serverSocket, responseCode, |
| false); |
| } |
| |
| /** |
| * Performs interaction between client's HttpURLConnection and |
| * servers side (ServerSocket). Server will response with specified |
| * response code. |
| * @param doAuthentication specifies |
| * if the server needs client authentication. |
| */ |
| public static Socket doInteraction( |
| final HttpURLConnection clientConnection, |
| final ServerSocket serverSocket, final int responseCode, |
| final boolean doAuthentication) throws Throwable { |
| |
| // set up the connection |
| clientConnection.setDoInput(true); |
| clientConnection.setConnectTimeout(TIMEOUT); |
| clientConnection.setReadTimeout(TIMEOUT); |
| |
| ServerWork server = new ServerWork(serverSocket, responseCode, |
| doAuthentication); |
| |
| ClientConnectionWork client = new ClientConnectionWork(clientConnection); |
| |
| server.start(); |
| client.start(); |
| |
| client.join(); |
| server.join(); |
| |
| if (client.thrown != null) { |
| if (responseCode != OK_CODE) { // not OK response expected |
| // it is probably expected exception, keep it as is |
| throw client.thrown; |
| } |
| if ((client.thrown instanceof SocketTimeoutException) |
| && (server.thrown != null)) { |
| // server's exception is more informative in this case |
| throw new Exception(server.thrown); |
| } else { |
| throw new Exception(client.thrown); |
| } |
| } |
| if (server.thrown != null) { |
| throw server.thrown; |
| } |
| return server.peerSocket; |
| } |
| |
| /** |
| * The host name verifier used in test. |
| */ |
| static class TestHostnameVerifier implements HostnameVerifier { |
| |
| boolean verified = false; |
| |
| public boolean verify(String hostname, SSLSession session) { |
| if (DO_LOG) { |
| System.out.println("***> verification " + hostname + " " |
| + session.getPeerHost()); |
| } |
| verified = true; |
| return true; |
| } |
| } |
| |
| /** |
| * The base class for mock Client and Server. |
| */ |
| static class Work extends Thread { |
| |
| /** |
| * The header of OK HTTP response. |
| */ |
| static final String responseHead = "HTTP/1.1 200 OK\n"; |
| |
| /** |
| * The content of the response. |
| */ |
| static final String plainResponseContent = "<HTML>\n" |
| + "<HEAD><TITLE>Plain Response Content</TITLE></HEAD>\n" |
| + "</HTML>"; |
| |
| /** |
| * The tail of the response. |
| */ |
| static final String plainResponseTail = "Content-type: text/html\n" |
| + "Content-length: " + plainResponseContent.length() + "\n\n" |
| + plainResponseContent; |
| |
| /** |
| * The response message to be sent in plain (HTTP) format. |
| */ |
| static final String plainResponse = responseHead + plainResponseTail; |
| |
| /** |
| * The content of the response to be sent during HTTPS session. |
| */ |
| static final String httpsResponseContent = "<HTML>\n" |
| + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n" |
| + "</HTML>"; |
| |
| /** |
| * The tail of the response to be sent during HTTPS session. |
| */ |
| static final String httpsResponseTail = "Content-type: text/html\n" |
| + "Content-length: " + httpsResponseContent.length() + "\n\n" |
| + httpsResponseContent; |
| |
| /** |
| * The response requiring client's proxy authentication. |
| */ |
| static final String respAuthenticationRequired = "HTTP/1.0 407 Proxy authentication required\n" |
| + "Proxy-authenticate: Basic realm=\"localhost\"\n\n"; |
| |
| /** |
| * The data to be posted by client to the server. |
| */ |
| static final String clientsData = "_.-^ Client's Data ^-._"; |
| |
| /** |
| * The exception thrown during peers interaction. |
| */ |
| protected Throwable thrown; |
| |
| /** |
| * The print stream used for debug log. |
| * If it is null debug info will not be printed. |
| */ |
| private PrintStream out = new PrintStream(System.out); |
| |
| /** |
| * Prints log message. |
| */ |
| public synchronized void log(String message) { |
| if (DO_LOG && (out != null)) { |
| System.out.println("[" + getName() + "]: " + message); |
| } |
| } |
| } |
| |
| /** |
| * The class used for server side works. |
| */ |
| static class ServerWork extends Work { |
| |
| // the server socket used for connection |
| private ServerSocket serverSocket; |
| |
| // the socket connected with client peer |
| private Socket peerSocket; |
| |
| // indicates if the server acts as proxy server |
| private boolean actAsProxy; |
| |
| // indicates if the server needs proxy authentication |
| private boolean needProxyAuthentication; |
| |
| // response code to be send to the client peer |
| private int responseCode; |
| |
| /** |
| * Creates the thread acting as a server side. |
| */ |
| public ServerWork(ServerSocket serverSocket) { |
| // the server does not require proxy authentication |
| // and sends OK_CODE (OK) response code |
| this(serverSocket, OK_CODE, false); |
| } |
| |
| /** |
| * Creates the thread acting as a server side. |
| * @param serverSocket the server socket to be used during connection |
| * @param responseCode the response code to be sent to the client |
| * @param needProxyAuthentication |
| * indicates if the server needs proxy authentication |
| */ |
| public ServerWork(ServerSocket serverSocket, int responseCode, |
| boolean needProxyAuthentication) { |
| this.serverSocket = serverSocket; |
| this.responseCode = responseCode; |
| this.needProxyAuthentication = needProxyAuthentication; |
| // will act as a proxy server if the specified server socket |
| // is not a secure server socket |
| if (serverSocket instanceof SSLServerSocket) { |
| // demand client to send its certificate |
| ((SSLServerSocket) serverSocket).setNeedClientAuth(true); |
| // work as a HTTPS server, not as HTTP proxy |
| this.actAsProxy = false; |
| } else { |
| this.actAsProxy = true; |
| } |
| this.actAsProxy = !(serverSocket instanceof SSLServerSocket); |
| setName(this.actAsProxy ? "Proxy Server" : "Server"); |
| } |
| |
| /** |
| * Closes the connection. |
| */ |
| public void closeSocket(Socket socket) { |
| try { |
| socket.getInputStream().close(); |
| } catch (IOException e) {} |
| try { |
| socket.getOutputStream().close(); |
| } catch (IOException e) {} |
| try { |
| socket.close(); |
| } catch (IOException e) {} |
| } |
| |
| /** |
| * Performs the actual server work. |
| * If some exception occurs during the work it will be |
| * stored in the <code>thrown</code> field. |
| */ |
| public void run() { |
| // the buffer used for reading the messages |
| byte[] buff = new byte[2048]; |
| // the number of bytes read into the buffer |
| int num; |
| try { |
| // configure the server socket to avoid blocking |
| serverSocket.setSoTimeout(TIMEOUT); |
| // accept client connection |
| peerSocket = serverSocket.accept(); |
| // configure the client connection to avoid blocking |
| peerSocket.setSoTimeout(TIMEOUT); |
| log("Client connection ACCEPTED"); |
| |
| InputStream is = peerSocket.getInputStream(); |
| OutputStream os = peerSocket.getOutputStream(); |
| |
| num = is.read(buff); |
| String message = new String(buff, 0, num); |
| log("Got request:\n" + message); |
| log("------------------"); |
| |
| if (!actAsProxy) { |
| // Act as Server (not Proxy) side |
| if (message.startsWith("POST")) { |
| // client connection sent some data |
| log("try to read client data"); |
| num = is.read(buff); |
| message = new String(buff, 0, num); |
| log("client's data: '" + message + "'"); |
| // check the received data |
| assertEquals(clientsData, message); |
| } |
| // just send the response |
| os |
| .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) |
| .getBytes()); |
| os.flush(); |
| os.close(); |
| // and return |
| log("Work is DONE !actAsProxy"); |
| return; |
| } |
| |
| // Do proxy work |
| if (needProxyAuthentication) { |
| log("Authentication required ..."); |
| // send Authentication Request |
| os.write(respAuthenticationRequired.getBytes()); |
| // read response |
| num = is.read(buff); |
| if (num == -1) { |
| // this connection was closed, |
| // do clean up and create new one: |
| closeSocket(peerSocket); |
| peerSocket = serverSocket.accept(); |
| peerSocket.setSoTimeout(TIMEOUT); |
| log("New client connection ACCEPTED"); |
| is = peerSocket.getInputStream(); |
| os = peerSocket.getOutputStream(); |
| num = is.read(buff); |
| } |
| message = new String(buff, 0, num); |
| log("Got authenticated request:\n" + message); |
| log("------------------"); |
| // check provided authorization credentials |
| assertTrue("Received message does not contain " |
| + "authorization credentials", message |
| .toLowerCase().indexOf("proxy-authorization:") > 0); |
| } |
| |
| // The content of this response will reach proxied HTTPUC |
| // but will not reach proxied HTTPSUC |
| // In case of HTTP connection it will be the final message, |
| // in case of HTTPS connection this message will just indicate |
| // that connection with remote host has been done |
| // (i.e. SSL tunnel has been established). |
| os.write(plainResponse.getBytes()); |
| log("Sent OK RESPONSE"); |
| |
| if (message.startsWith("CONNECT")) { // request for SSL tunnel |
| log("Perform SSL Handshake..."); |
| // create sslSocket acting as a remote server peer |
| SSLSocket sslSocket = (SSLSocket) getContext() |
| .getSocketFactory().createSocket(peerSocket, |
| "localhost", peerSocket.getPort(), true); // do autoclose |
| sslSocket.setUseClientMode(false); |
| // demand client authentication |
| sslSocket.setNeedClientAuth(true); |
| sslSocket.startHandshake(); |
| peerSocket = sslSocket; |
| is = peerSocket.getInputStream(); |
| os = peerSocket.getOutputStream(); |
| |
| // read the HTTP request sent by secure connection |
| // (HTTPS request) |
| num = is.read(buff); |
| message = new String(buff, 0, num); |
| log("[Remote Server] Request from SSL tunnel:\n" + message); |
| log("------------------"); |
| |
| if (message.startsWith("POST")) { |
| // client connection sent some data |
| log("[Remote Server] try to read client data"); |
| num = is.read(buff); |
| message = new String(buff, 0, num); |
| log("[Remote Server] client's data: '" + message + "'"); |
| // check the received data |
| assertEquals(clientsData, message); |
| } |
| |
| log("[Remote Server] Sending the response by SSL tunnel.."); |
| // send the response with specified response code |
| os |
| .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) |
| .getBytes()); |
| } |
| log("Work is DONE actAsProxy"); |
| } catch (Throwable e) { |
| if (DO_LOG) { |
| e.printStackTrace(); |
| } |
| thrown = e; |
| } finally { |
| closeSocket(peerSocket); |
| try { |
| serverSocket.close(); |
| } catch (IOException e) {} |
| } |
| } |
| } |
| |
| /** |
| * The class used for client side works. It could be used to test |
| * both HttpURLConnection and HttpsURLConnection. |
| */ |
| static class ClientConnectionWork extends Work { |
| |
| // connection to be used to contact the server side |
| private HttpURLConnection connection; |
| |
| /** |
| * Creates the thread acting as a client side. |
| * @param connection connection to be used to contact the server side |
| */ |
| public ClientConnectionWork(HttpURLConnection connection) { |
| this.connection = connection; |
| setName("Client Connection"); |
| log("Created over connection: " + connection.getClass()); |
| } |
| |
| /** |
| * Performs the actual client work. |
| * If some exception occurs during the work it will be |
| * stored in the <code>thrown<code> field. |
| */ |
| public void run() { |
| try { |
| log("Opening the connection.."); |
| connection.connect(); |
| log("Connection has been ESTABLISHED, using proxy: " |
| + connection.usingProxy()); |
| if (connection.getDoOutput()) { |
| // connection configured to post data, do so |
| connection.getOutputStream().write(clientsData.getBytes()); |
| } |
| // read the content of HTTP(s) response |
| InputStream is = connection.getInputStream(); |
| log("Input Stream obtained"); |
| byte[] buff = new byte[2048]; |
| int num = 0; |
| int byt = 0; |
| while ((num < buff.length) && (is.available() > 0) |
| && ((byt = is.read()) != -1)) { |
| buff[num++] = (byte) byt; |
| } |
| String message = new String(buff, 0, num); |
| log("Got content:\n" + message); |
| log("------------------"); |
| log("Response code: " + connection.getResponseCode()); |
| |
| if (connection instanceof HttpsURLConnection) { |
| assertEquals(httpsResponseContent, message); |
| } else { |
| assertEquals(plainResponseContent, message); |
| } |
| } catch (Throwable e) { |
| if (DO_LOG) { |
| e.printStackTrace(); |
| } |
| thrown = e; |
| } |
| } |
| } |
| } |