diff options
author | elonen <elonen@iki.fi> | 2015-05-08 23:56:26 +0300 |
---|---|---|
committer | elonen <elonen@iki.fi> | 2015-05-08 23:56:26 +0300 |
commit | fb42a3003b5ebb3e0b0aacaac60890d3beb4798c (patch) | |
tree | 2ac6bbba8404494088895b5010810f894701a406 | |
parent | 3526d931ae635536355e46a6b242405d309c70f3 (diff) | |
parent | ea010c0e02424390f85516578248b0ba495d9678 (diff) | |
download | nanohttpd-fb42a3003b5ebb3e0b0aacaac60890d3beb4798c.tar.gz |
Merge pull request #107 from eighthave/master
Merge HTTPS support to master.
-rw-r--r-- | core/src/main/java/fi/iki/elonen/NanoHTTPD.java | 94 |
1 files changed, 91 insertions, 3 deletions
diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java index f797557..7ad1843 100644 --- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java +++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java @@ -72,6 +72,10 @@ import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; + +import java.security.KeyStore; +import javax.net.ssl.*; + /** * A simple, tiny, nicely embeddable HTTP server in Java * <p/> @@ -145,6 +149,7 @@ public abstract class NanoHTTPD { private final int myPort; private ServerSocket myServerSocket; private Set<Socket> openConnections = new HashSet<Socket>(); + private SSLServerSocketFactory sslServerSocketFactory; private Thread myThread; /** * Pluggable strategy for asynchronously executing requests. @@ -182,13 +187,90 @@ public abstract class NanoHTTPD { } } + /** + * Creates an SSLSocketFactory for HTTPS. + * + * Pass a KeyStore resource with your certificate and passphrase + */ + public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException { + SSLServerSocketFactory res = null; + try { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); + keystore.load(keystoreStream, passphrase); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(keystore); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keystore, passphrase); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + res = ctx.getServerSocketFactory(); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + return res; + } + + /** + * Creates an SSLSocketFactory for HTTPS. + * + * Pass a loaded KeyStore and a loaded KeyManagerFactory. + * These objects must properly loaded/initialized by the caller. + */ + public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException { + SSLServerSocketFactory res = null; + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(loadedKeyStore); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(loadedKeyFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + res = ctx.getServerSocketFactory(); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + return res; + } + + /** + * Creates an SSLSocketFactory for HTTPS. + * + * Pass a loaded KeyStore and an array of loaded KeyManagers. + * These objects must properly loaded/initialized by the caller. + */ + public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException { + SSLServerSocketFactory res = null; + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(loadedKeyStore); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); + res = ctx.getServerSocketFactory(); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + return res; + } + + /** + * Call before start() to serve over HTTPS instead of HTTP + */ + public void makeSecure(SSLServerSocketFactory sslServerSocketFactory) { + this.sslServerSocketFactory = sslServerSocketFactory; + } + /** * Start the server. * * @throws IOException if the socket is in use. */ public void start() throws IOException { - myServerSocket = new ServerSocket(); + if (sslServerSocketFactory != null) { + SSLServerSocket ss = (SSLServerSocket) sslServerSocketFactory.createServerSocket(); + ss.setNeedClientAuth(false); + myServerSocket = ss; + } else { + myServerSocket = new ServerSocket(); + } myServerSocket.setReuseAddress(true); myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); @@ -214,8 +296,11 @@ public abstract class NanoHTTPD { } } catch (Exception e) { // When the socket is closed by the client, we throw our own SocketException - // to break the "keep alive" loop above. - if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) { + // to break the "keep alive" loop above. If the exception was anything other + // than the expected SocketException OR a SocketTimeoutException, print the + // stacktrace + if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && + !(e instanceof SocketTimeoutException)) { LOG.log(Level.SEVERE, "Communication with the client broken", e); } } finally { @@ -986,6 +1071,9 @@ public abstract class NanoHTTPD { // throw it out to close socket object (finalAccept) throw e; } catch (SocketTimeoutException ste) { + // treat socket timeouts the same way we treat socket exceptions + // i.e. close the stream & finalAccept object by throwing the + // exception up the call stack. throw ste; } catch (IOException ioe) { Response r = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); |