From f266945b7e7d53c97a04ab9deada0e8ba35ce29e Mon Sep 17 00:00:00 2001 From: BWS Systems Date: Fri, 31 May 2019 15:19:24 -0500 Subject: [PATCH] working on ssl calls --- .../HABridge/plugins/http/HTTPHandler.java | 58 ++++++++++------ .../HABridge/plugins/http/HttpClientPool.java | 67 ++++++++++++++++++- .../plugins/moziot/MozIotInstance.java | 2 + 3 files changed, 105 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHandler.java b/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHandler.java index b41bf51..00b0acc 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHandler.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHandler.java @@ -5,6 +5,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; +import javax.net.ssl.SSLContext; + import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -23,37 +25,46 @@ import com.bwssystems.HABridge.api.NameValue; public class HTTPHandler { private static final Logger log = LoggerFactory.getLogger(HTTPHandler.class); private String callType; - + public HTTPHandler() { super(); callType = null; } - public HTTPHandler(String type) { super(); callType = type; } - // This function executes the url from the device repository against the // target as http or https as defined public String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) { - log.debug("doHttpRequest with url <<<" + url + ">>>, verb: " + httpVerb + ", contentType: " + contentType + ", body <<<" + body + ">>>" ); - if(headers != null && headers.length > 0) - for(int i = 0; i < headers.length; i++) - log.debug("header index " + i + " name: <<<" + headers[i].getName() + ">>>, value: <<<" + headers[i].getValue() + ">>>"); HttpUriRequest request = null; String theContent = null; URI theURI = null; + boolean usingSSL = false; ContentType parsedContentType = null; StringEntity requestBody = null; + log.debug("doHttpRequest with url <<<" + url + ">>>, verb: " + httpVerb + ", contentType: " + contentType + + ", body <<<" + body + ">>>"); + if (headers != null && headers.length > 0) { + for (int i = 0; i < headers.length; i++) { + log.debug("header index " + i + " name: <<<" + headers[i].getName() + ">>>, value: <<<" + + headers[i].getValue() + ">>>"); + } + } + if (contentType != null && !contentType.trim().isEmpty()) { parsedContentType = ContentType.parse(contentType); if (body != null && body.length() > 0) requestBody = new StringEntity(body, parsedContentType); } + + if (url.startsWith("https:")) { + usingSSL = true; + } + try { theURI = new URI(url); } catch (URISyntaxException e1) { @@ -90,7 +101,11 @@ public class HTTPHandler { CloseableHttpResponse response = null; for (int retryCount = 0; retryCount < 2; retryCount++) { try { - response = HttpClientPool.getClient().execute(request); + if (usingSSL) { + response = HttpClientPool.getSSLClient().execute(request); + } else { + response = HttpClientPool.getClient().execute(request); + } log.debug((httpVerb == null ? "GET" : httpVerb) + " execute (" + retryCount + ") on URL responded: " + response.getStatusLine().getStatusCode()); if (response != null && response.getEntity() != null) { @@ -106,22 +121,27 @@ public class HTTPHandler { // ignore // content } catch (Exception e) { - log.debug("Error ocurred in handling response entity after successful call, still responding success. " - + e.getMessage(), e); + log.debug( + "Error ocurred in handling response entity after successful call, still responding success. " + + e.getMessage(), + e); } } - if (response != null && response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300) { - if(theContent == null) + if (response != null && response.getStatusLine().getStatusCode() >= 200 + && response.getStatusLine().getStatusCode() < 300) { + if (theContent == null) theContent = ""; log.debug("Successfull response - The http response is <<<" + theContent + ">>>"); retryCount = 2; - } else if (DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex].equals(callType) && response.getStatusLine().getStatusCode() == 302) { - if(theContent == null) + } else if (DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex].equals(callType) + && response.getStatusLine().getStatusCode() == 302) { + if (theContent == null) theContent = ""; log.debug("Successfull response - The http response is <<<" + theContent + ">>>"); retryCount = 2; } else if (response != null) { - log.warn("HTTP response code was not an expected successful response of between 200 - 299, the code was: " + log.warn( + "HTTP response code was not an expected successful response of between 200 - 299, the code was: " + response.getStatusLine() + " with the content of <<<" + theContent + ">>>"); if (response.getStatusLine().getStatusCode() == 504) { log.warn("HTTP response code was 504, retrying..."); @@ -130,15 +150,15 @@ public class HTTPHandler { } else retryCount = 2; } - + } catch (ClientProtocolException e) { log.warn("Client Protocol Exception received, retyring...."); - }catch (IOException e) { + } catch (IOException e) { log.warn("Error calling out to HA gateway: IOException in log: " + e.getMessage()); retryCount = 2; } - if(retryCount < 2) { + if (retryCount < 2) { theContent = null; try { Thread.sleep(1000); @@ -149,11 +169,11 @@ public class HTTPHandler { } return theContent; } + public void setCallType(String callType) { this.callType = callType; } - public void closeHandler() { } } diff --git a/src/main/java/com/bwssystems/HABridge/plugins/http/HttpClientPool.java b/src/main/java/com/bwssystems/HABridge/plugins/http/HttpClientPool.java index 022d29d..b1790f2 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/http/HttpClientPool.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/http/HttpClientPool.java @@ -4,10 +4,22 @@ import java.io.IOException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLContext; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,9 +43,52 @@ public final class HttpClientPool { // Increase default max connection per route to 20 cm.setDefaultMaxPerRoute(20); // Build the client. - threadSafeClient = HttpClients.custom() - .setConnectionManager(cm) - .build(); + threadSafeClient = HttpClients.custom().setConnectionManager(cm).build(); + // Start up an eviction thread. + monitor = new IdleConnectionMonitorThread(cm); + // Don't stop quitting. + monitor.setDaemon(true); + monitor.start(); + } + + public CloseableHttpClient get() { + return threadSafeClient; + } + + } + + // Single-element enum to implement Singleton. + private static enum SingletonSSL { + // Just one of me so constructor will be called once. + SSLClient; + // The thread-safe client. + private final CloseableHttpClient threadSafeClient; + // The pool monitor. + private final IdleConnectionMonitorThread monitor; + private TrustStrategy acceptingTrustStrategy = null; + private SSLContext sslContext = null; + private SSLConnectionSocketFactory sslsf = null; + private Registry socketFactoryRegistry = null; + private NoopHostnameVerifier hostnameVerifier = null; + + // The constructor creates it - thus late + private SingletonSSL() { + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); + // Increase max total connection to 200 + cm.setMaxTotal(200); + // Increase default max connection per route to 20 + cm.setDefaultMaxPerRoute(20); + try { + acceptingTrustStrategy = (cert, authType) -> true; + hostnameVerifier = new NoopHostnameVerifier(); + sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); + sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); + } catch (Exception e) { + HttpClientPool.log.warn("SingletonSSL failed on SSL init"); + } + // Build the client. + threadSafeClient = HttpClients.custom().setConnectionManager(cm).setSSLSocketFactory(sslsf) + .setSSLHostnameVerifier(hostnameVerifier).build(); // Start up an eviction thread. monitor = new IdleConnectionMonitorThread(cm); // Don't stop quitting. @@ -52,6 +107,11 @@ public final class HttpClientPool { return Singleton.Client.get(); } + public static CloseableHttpClient getSSLClient() { + // The thread safe client is held by the singleton. + return SingletonSSL.SSLClient.get(); + } + // Watches for stale connections and evicts them. private static class IdleConnectionMonitorThread extends Thread { // The manager to watch. @@ -123,6 +183,7 @@ public final class HttpClientPool { public static void shutdown() throws InterruptedException, IOException { // Shutdown the monitor. Singleton.Client.monitor.shutdown(); + SingletonSSL.SSLClient.monitor.shutdown(); } } \ No newline at end of file diff --git a/src/main/java/com/bwssystems/HABridge/plugins/moziot/MozIotInstance.java b/src/main/java/com/bwssystems/HABridge/plugins/moziot/MozIotInstance.java index cc93328..117fca5 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/moziot/MozIotInstance.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/moziot/MozIotInstance.java @@ -102,8 +102,10 @@ public class MozIotInstance { headers[1].setName("Accept"); headers[1].setValue("application/json"); aUrl = aUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/login"; + log.info("gateway login URL: " + aUrl); String commandData = "{\"email\": \"" + mozIotIP.getUsername() + "\", \"password\":\"" + mozIotIP.getPassword() + "\"}"; + log.info("The login body: " + commandData); String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", commandData, headers); if (theData != null) { log.info("GET Mozilla login - data: " + theData);