Bug 537160 - NPE in
org.eclipse.equinox.http.jetty.internal.HttpServerManager.updated() with
HTTPS

Add Tests to test HTTPS endpoint.

Change-Id: I93ea68d4543e273c725482ad2829f00e6a9133c3
Signed-off-by: Anjum Fatima <anjum.eclipse@gmail.com>
diff --git a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
index e3590d8..b4d65a4 100644
--- a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
+++ b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
@@ -188,6 +188,7 @@
 			// HTTPS connector
 			httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(https_config)); //$NON-NLS-1$
 			httpsConnector.setPort(Details.getInt(dictionary, JettyConstants.HTTPS_PORT, 443));
+			httpsConnector.setHost(Details.getString(dictionary, JettyConstants.HTTPS_HOST, null));
 		}
 		return httpsConnector;
 	}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
index 34a0ad3..981c2c0 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
@@ -12,6 +12,7 @@
  org.apache.commons.fileupload;version="1.2.2",
  org.apache.commons.fileupload.disk;version="1.2.2",
  org.apache.commons.fileupload.servlet;version="1.2.2",
+ org.eclipse.equinox.http.jetty;version="1.4.0",
  org.eclipse.equinox.http.servlet;version="1.1.0",
  org.eclipse.equinox.http.servlet.context;version="1.0.0",
  org.eclipse.equinox.http.servlet.session;version="1.0.0",
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
index 2bb9155..527492c 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
@@ -39,6 +39,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.equinox.http.jetty.JettyConstants;
 import org.eclipse.equinox.http.servlet.context.ContextPathCustomizer;
 import org.eclipse.equinox.http.servlet.tests.bundle.Activator;
 import org.eclipse.equinox.http.servlet.tests.bundle.BundleAdvisor;
@@ -142,6 +143,11 @@
 		}
 		return value;
 	}
+	
+	protected void setJettyProperty(String key, String value) {
+		String qualifiedKey = JETTY_PROPERTY_PREFIX + key;
+		System.setProperty(qualifiedKey, value);
+	}
 
 	protected String getPort() {
 		String defaultPort = getProperty(OSGI_HTTP_PORT_PROPERTY);
@@ -209,6 +215,33 @@
 		String contextPath = getContextPath();
 		requestAdvisor = new ServletRequestAdvisor(port, contextPath);
 	}
+	
+	protected void startJettyWithSSL(String port, String ksPath, String ksPassword, String keyPassword) throws Exception {
+		if(port == null) {
+			throw new IllegalArgumentException("Port cannot be null");		
+		}
+		if (ksPath == null) {
+			throw new IllegalArgumentException("Keystore path  cannot be null");			
+		}
+		setJettyProperty(JettyConstants.HTTP_ENABLED, "false");
+		setJettyProperty(JettyConstants.HTTPS_ENABLED, "true");
+		
+		setJettyProperty(JettyConstants.HTTPS_PORT, port);
+		
+		setJettyProperty(JettyConstants.SSL_KEYSTORE, ksPath);
+		
+		if(ksPassword != null) {
+			setJettyProperty(JettyConstants.SSL_PASSWORD, ksPassword);
+		}
+		if(keyPassword != null) {
+			setJettyProperty(JettyConstants.SSL_KEYPASSWORD, keyPassword);
+		}
+		
+		advisor.startBundle(EQUINOX_JETTY_BUNDLE);
+		String contextPath = getContextPath();
+		requestAdvisor = new ServletRequestAdvisor(port, contextPath, ksPath, ksPassword);	
+	}
+
 
 	protected void stopBundles() throws BundleException {
 		for (int i = BUNDLES.length - 1; i >= 0; i--) {
@@ -220,6 +253,12 @@
 	protected void stopJetty() throws BundleException {
 		advisor.stopBundle(EQUINOX_JETTY_BUNDLE);
 	}
+	
+	protected void stopJettyWithSSL() throws BundleException {
+		advisor.stopBundle(EQUINOX_JETTY_BUNDLE);
+		setJettyProperty(JettyConstants.HTTP_ENABLED, "true");
+		setJettyProperty(JettyConstants.HTTPS_ENABLED, "false");			
+	}
 
 	protected void uninstallBundle(Bundle bundle) throws BundleException {
 		installer.uninstallBundle(bundle);
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
index c423ba6..74a8985 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
@@ -32,6 +32,7 @@
 import java.net.CookieManager;
 import java.net.CookiePolicy;
 import java.net.URL;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -4309,6 +4310,27 @@
 		String actual = requestAdvisor.request(testName.getMethodName());
 		Assert.assertEquals(expected, actual);
 	}
-
-
+	
+	@Test
+	public void testHTTPSEndpoint() throws Exception {
+		stopJetty();
+		File keyStoreFile = getBundleContext().getDataFile("server-keystore.jks");
+		URL keyStoreURL = getClass().getResource("server-keystore.jks");
+		if (!keyStoreFile.exists()) {
+			Files.copy(keyStoreURL.openStream(), keyStoreFile.toPath());
+		}
+		
+		startJettyWithSSL("8443", keyStoreFile.getAbsolutePath(), "secret", "secret");
+		
+		Bundle bundle = installBundle(TEST_BUNDLE_1);
+		try {
+			bundle.start();
+			
+			String actual = requestAdvisor.requestHttps("TestServlet10");
+			assertEquals("Expected output not found", "a", actual);
+		} finally {
+			uninstallBundle(bundle);
+			stopJettyWithSSL();
+		}
+	}
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/server-keystore.jks b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/server-keystore.jks
new file mode 100644
index 0000000..f782374
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/server-keystore.jks
Binary files differ
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
index 0918d46..f422420 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
@@ -14,22 +14,32 @@
  *******************************************************************************/
 package org.eclipse.equinox.http.servlet.tests.util;
 
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
-
+import java.security.KeyStore;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
 /*
  * The ServletRequestAdvisor is responsible for composing URLs and using them
  * to performing servlet requests.
@@ -37,8 +47,14 @@
 public class ServletRequestAdvisor extends Object {
 	private final String contextPath;
 	private final String port;
+	private final String ksPath;
+	private final String ksPassword;
 
 	public ServletRequestAdvisor(String port, String contextPath) {
+		this(port, contextPath, null, null);
+	}
+	
+	public ServletRequestAdvisor(String port, String contextPath, String ksPath, String ksPassword) {
 		super();
 		if (port == null)
 		 {
@@ -46,11 +62,16 @@
 		}
 		this.port = port;
 		this.contextPath = contextPath;
+		this.ksPath = ksPath;
+		this.ksPassword = ksPassword;
 	}
 
-	private String createUrlSpec(String value) {
+	private String createUrlSpec(String value, boolean isHttps) {
 		StringBuffer buffer = new StringBuffer(100);
 		String protocol = "http://"; //$NON-NLS-1$
+		if (isHttps) {
+			protocol = "https://";
+		}
 		String host = "localhost"; //$NON-NLS-1$
 		buffer.append(protocol);
 		buffer.append(host);
@@ -62,8 +83,13 @@
 			buffer.append(value);
 		}
 		return buffer.toString();
+		
 	}
 
+	private String createUrlSpec(String value) {
+		return createUrlSpec(value, false);
+	}
+	
 	private String drain(InputStream stream) throws IOException {
 		byte[] bytes = new byte[100];
 		StringBuffer buffer = new StringBuffer(500);
@@ -99,6 +125,70 @@
 			stream.close();
 		}
 	}
+	
+	public String requestHttps(String value) throws Exception {
+		String spec = createUrlSpec(value, true);
+		log("Requesting " + spec); //$NON-NLS-1$
+		URL url = new URL(spec);
+		SSLContext sslContext = SSLContext.getInstance("SSL");
+		initializeSSLContext(sslContext, ksPath, ksPassword);
+		
+		HttpsURLConnection httpsConn = (HttpsURLConnection)url.openConnection();
+		httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
+	    httpsConn.setRequestMethod("GET");
+	    httpsConn.setDoOutput(false);
+	    httpsConn.setDoInput(true);
+	    httpsConn.setConnectTimeout(150 * 1000);
+	    httpsConn.setReadTimeout(150 * 1000);
+	    httpsConn.connect();
+	    
+	    assertEquals("Request to the url " + spec + " was not successful", 200 , httpsConn.getResponseCode());
+	    InputStream stream = httpsConn.getInputStream();
+		try {
+			return drain(stream);
+		} finally {
+			stream.close();
+		}	
+	}
+
+	private void initializeSSLContext(SSLContext sslContext, String ksPath, String ksPassword) throws Exception {
+		KeyManager keyManagers[] = null;
+        if (ksPath != null) {
+       	 	KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            File ksFile = new File(ksPath);
+            KeyStore keyStore = KeyStore.getInstance("JKS");
+
+            try(InputStream ksStream = new FileInputStream(ksFile)){
+            	keyStore.load(ksStream, ksPassword.toCharArray());
+            	kmFactory.init(keyStore, ksPassword.toCharArray());
+	            keyManagers = kmFactory.getKeyManagers();
+            }          
+        }
+        
+       TrustManager[] trustManagers = getTrustManager();
+       
+       sslContext.init(keyManagers, trustManagers, null);  
+		
+	}
+	
+	private TrustManager[] getTrustManager() {
+		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
+            @Override
+            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                return null;
+            }
+
+            @Override
+            public void checkClientTrusted(
+                                           java.security.cert.X509Certificate[] certs, String authType) {}
+
+            @Override
+            public void checkServerTrusted(
+                                           java.security.cert.X509Certificate[] certs, String authType) {}
+        } };
+
+        return trustAllCerts;
+	}
 
 	public Map<String, List<String>> request(String value, Map<String, List<String>> headers) throws IOException {
 		String spec = createUrlSpec(value);