/*
* 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.
* 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();
}
}
/**
* 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 = "\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();
// 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 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("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);
}
}