Package org.apache.harmony.luni.tests.internal.net.www.protocol.https

Source Code of org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest$ServerWork

/*
*  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 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.AssertionFailedError;
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. <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).
*/
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();
        }
    }

    /**
     * Checks if HTTPS connection performs initial SSL handshake with the server
     * working over SSL, sends encrypted HTTP request, and receives expected
     * HTTP response. After that it checks that the established connection is
     * persistent. After HTTPS session if finished test checks connection state
     * parameters established by HttpsURLConnection.
     */
    public void testHttpsPersistentConnection() 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) doPersistentInteraction(
                    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 the behaviour in case of sending the data to the server over
     * persistent connection.
     */
    public void testPersistence_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) doPersistentInteraction(
                    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. Checks that
     * persistent connection to the host exists and can be used no in spite of
     * explicit Proxy specifying.
     */
    public void testPersistentProxyConnection() 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) doPersistentInteraction(
                    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);
            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);
            } 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 = "org/apache/harmony/luni/tests/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,
                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, false);
    }

    /**
     * Performs interaction between client's HttpURLConnection and servers side
     * (ServerSocket) over persistent connection.
     */
    public static Socket doPersistentInteraction(
            final HttpURLConnection clientConnection,
            final ServerSocket serverSocket) throws Throwable {
        return doInteraction(clientConnection, serverSocket, OK_CODE, false,
                true);
    }

    /**
     * Performs interaction between client's HttpURLConnection and servers side
     * (ServerSocket) over persistent connection. Server will response with
     * specified response code.
     */
    public static Socket doPersistentInteraction(
            final HttpURLConnection clientConnection,
            final ServerSocket serverSocket, final int responseCode)
            throws Throwable {
        return doInteraction(clientConnection, serverSocket, responseCode,
                false, true);
    }

    /**
     * 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, final boolean checkPersistence)
            throws Throwable {

        // set up the connection
        clientConnection.setDoInput(true);
        clientConnection.setConnectTimeout(TIMEOUT);
        clientConnection.setReadTimeout(TIMEOUT);

        ServerWork server = new ServerWork(serverSocket, responseCode,
                doAuthentication, checkPersistence);

        ClientConnectionWork client = new ClientConnectionWork(clientConnection);

        server.start();
        client.start();

        client.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 (checkPersistence) {
            ClientConnectionWork client2 = new ClientConnectionWork(
                    (HttpURLConnection) clientConnection.getURL()
                            .openConnection());
            client2.start();
            client2.join();
            if (client2.thrown != null) {
                if (responseCode != OK_CODE) { // not OK response expected
                    // it is probably expected exception, keep it as is
                    throw client2.thrown;
                }
                if ((client2.thrown instanceof SocketTimeoutException)
                        && (server.thrown != null)) {
                    // server's exception is more informative in this case
                    throw new Exception(server.thrown);
                } else {
                    throw new Exception(client2.thrown);
                }
            }
        }

        server.join();

        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 = "<HTML>\n"
                + "<HEAD><TITLE>Plain Response Content</TITLE></HEAD>\n"
                + "</HTML>";

        /**
         * The tail of the response.
         */
        static 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 String plainResponse = responseHead + plainResponseTail;

        /**
         * The content of the response to be sent during HTTPS session.
         */
        static 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 String httpsResponseTail = "Content-type: text/html\n"
                + "Content-length: " + httpsResponseContent.length() + "\n\n"
                + httpsResponseContent;

        /**
         * The response requiring client's proxy authentication.
         */
        static 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 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;

        // do we check for connection persistence
        private boolean checkPersistence;

        // 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, 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, boolean checkPersistence) {
            this.serverSocket = serverSocket;
            this.responseCode = responseCode;
            this.needProxyAuthentication = needProxyAuthentication;
            this.checkPersistence = checkPersistence;
            // 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();

                // how many times established connection will be used
                int number_of_uses = checkPersistence ? 2 : 1;
                for (int it = 0; it < number_of_uses; it++) {
                    if (checkPersistence) {
                        log("==========================================");
                        log("Use established connection for " + (it + 1)
                                + " time");
                    }

                    num = is.read(buff);
                    String message = new String(buff, 0, num, "UTF-8");
                    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, "UTF-8");
                            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("UTF-8"));
                        log("Simple NON-Proxy work is DONE");
                        continue;
                    }

                    // Do proxy work
                    if (needProxyAuthentication) {
                        log("Authentication required ...");
                        // send Authentication Request
                        os.write(respAuthenticationRequired.getBytes("UTF-8"));
                        // 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, "UTF-8");
                        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);
                    }

                    if (peerSocket instanceof SSLSocket) {
                        // it will be so if we are have second iteration
                        // over persistent connection
                        os
                                .write(("HTTP/1.1 " + OK_CODE + "\n" + httpsResponseTail)
                                        .getBytes("UTF-8"));
                        log("Sent OK RESPONSE over SSL");
                    } else {
                        // 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("UTF-8"));
                        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, "UTF-8");
                        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, "UTF-8");
                            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("UTF-8"));
                    }
                    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 <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("UTF-8"));
                }
                // read the content of HTTP(s) response
                InputStream is = connection.getInputStream();
                log("Input Stream obtained: " + is.getClass());
                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, "UTF-8");
                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());
    }
}
TOP

Related Classes of org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest$ServerWork

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.