Index: modules/luni/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
===================================================================
--- modules/luni/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java (revision 0)
+++ modules/luni/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java (revision 0)
@@ -0,0 +1,1255 @@
+/*
+ * Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ * Licensed 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.tests.internal.net.www.protocol.https;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+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;
+import junit.framework.TestSuite;
+
+/**
+ * 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.
+ *
+ * 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.
+ *
+ * This certstore file should contain self-signed certificate
+ * generated by keytool utility in a usual way.
+ *
+ * The password to the certstore should be "password" (without quotes).
+ */
+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;
+
+ /**
+ * Checks that HttpsURLConnection's default SSLSocketFactory is operable.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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).
+ */
+ 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.
+ */
+ 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("------------------------");
+ }
+ }
+
+ /**
+ * 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() throws Exception {
+ String ksFileName =
+ "key_store." + KeyStore.getDefaultType().toLowerCase();
+ URL url = ClassLoader.getSystemClassLoader()
+ .getResource(ksFileName);
+ assertNotNull("Expected KeyStore file: '" + ksFileName
+ + "' for default KeyStore of type '"
+ + KeyStore.getDefaultType() + "' does not exist.", url);
+ return new File(url.toURI()).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 String responseHead = "HTTP/1.1 200 OK\n";
+
+ /**
+ * The content of the response.
+ */
+ static String plainResponseContent =
+ "\n"
+ + "
thrown 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());
+ // and return
+ log("Work is DONE");
+ 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");
+ } 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 thrown 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;
+ }
+ }
+ }
+
+ public static junit.framework.Test suite() {
+ return new TestSuite(HttpsURLConnectionTest.class);
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+}
+
Index: modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java
===================================================================
--- modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java (revision 442856)
+++ modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java (working copy)
@@ -344,6 +344,10 @@
*/
private class HttpsEngine extends HttpURLConnection {
+ // In case of using proxy this field indicates
+ // if it is a SSL Tunnel establishing stage
+ private boolean makingSSLTunnel;
+
protected HttpsEngine(URL url, int port) {
super(url, port);
}
@@ -357,11 +361,63 @@
if (connected) {
return;
}
- super.connect();
- // TODO make SSL Tunnel in case of using the proxy
- setUpTransportIO(wrapConnection(socket));
+ if (usingProxy() && !makingSSLTunnel) {
+ // SSL Tunnel through the proxy was not established yet, do so
+ makingSSLTunnel = true;
+ // first - make the connection
+ super.connect();
+ // keep request method
+ String save_meth = method;
+ // make SSL Tunnel
+ method = "CONNECT";
+ try {
+ doRequest();
+ endRequest();
+ } finally {
+ // restore initial request method
+ method = save_meth;
+ }
+ if (!connected) {
+ throw new IOException("Could not make SSL Tunneling. "
+ + "Got response: " + responseMessage
+ + " (" + responseCode + ")");
+ }
+ // if there are some remaining data in the stream - read it out
+ InputStream is = socket.getInputStream();
+ while (is.available() != 0) {
+ is.read();
+ }
+ makingSSLTunnel = false;
+ } else {
+ // no need in SSL tunnel
+ super.connect();
+ }
+ if (!makingSSLTunnel) {
+ setUpTransportIO(wrapConnection(socket));
+ }
}
+ protected String requestString() {
+ if (usingProxy()) {
+ if (makingSSLTunnel) {
+ // we are making the SSL Tunneling, return remotehost:port
+ int port = url.getPort();
+ return (port > 0)
+ ? url.getHost()+":"+port
+ : url.getHost();
+ } else {
+ // we has made SSL Tunneling, return /requested.data
+ String file = url.getFile();
+ if (file == null || file.length() == 0) {
+ file = "/";
+ }
+ return file;
+ }
+ } else {
+ return super.requestString();
+ }
+ }
+
/**
* Create the secure socket over the connected socket and
* verify remote hostname.