diff options
author | Richard van Nieuwenhoven <richard.vannieuwenhoven@adesso.at> | 2015-10-23 06:57:48 +0200 |
---|---|---|
committer | Richard van Nieuwenhoven <richard.vannieuwenhoven@adesso.at> | 2015-10-23 06:57:48 +0200 |
commit | 6bb307d2064fcae01fcbb06eb9f7c7f89ddc26ee (patch) | |
tree | d760ac6f0677fbcf72164745c27b2a6c18a48cc2 /core | |
parent | fbc38a43e727e481e09a96b33998eaefa0ced61c (diff) | |
parent | 4c3db1479b17ae004301a6214e9adb88040fefc4 (diff) | |
download | nanohttpd-6bb307d2064fcae01fcbb06eb9f7c7f89ddc26ee.tar.gz |
Merge pull request #238 from Hoeze/master
Added ServerSocketFactory to allow custom ServerSockets;
Diffstat (limited to 'core')
3 files changed, 284 insertions, 34 deletions
diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java index 3c2a403..906d435 100644 --- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java +++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java @@ -475,6 +475,49 @@ public abstract class NanoHTTPD { private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE); + /** + * Creates a normal ServerSocket for TCP connections + */ + public static class DefaultServerSocketFactory implements ServerSocketFactory { + + @Override + public ServerSocket create() throws IOException { + return new ServerSocket(); + } + + } + + /** + * Creates a new SSLServerSocket + */ + public static class SecureServerSocketFactory implements ServerSocketFactory { + + private SSLServerSocketFactory sslServerSocketFactory; + + private String[] sslProtocols; + + public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { + this.sslServerSocketFactory = sslServerSocketFactory; + this.sslProtocols = sslProtocols; + } + + @Override + public ServerSocket create() throws IOException { + SSLServerSocket ss = null; + ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); + if (this.sslProtocols != null) { + ss.setEnabledProtocols(this.sslProtocols); + } else { + ss.setEnabledProtocols(ss.getSupportedProtocols()); + } + ss.setUseClientMode(false); + ss.setWantClientAuth(false); + ss.setNeedClientAuth(false); + return ss; + } + + } + private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)"; private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE); @@ -839,10 +882,17 @@ public abstract class NanoHTTPD { */ private int findHeaderEnd(final byte[] buf, int rlen) { int splitbyte = 0; - while (splitbyte + 3 < rlen) { - if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { + while (splitbyte + 1 < rlen) { + + // RFC2616 + if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 3 < rlen && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { return splitbyte + 4; } + + // tolerance + if (buf[splitbyte] == '\n' && buf[splitbyte + 1] == '\n') { + return splitbyte + 2; + } splitbyte++; } return 0; @@ -1540,11 +1590,11 @@ public abstract class NanoHTTPD { */ public interface TempFile { - void delete() throws Exception; + public void delete() throws Exception; - String getName(); + public String getName(); - OutputStream open() throws Exception; + public OutputStream open() throws Exception; } /** @@ -1559,7 +1609,7 @@ public abstract class NanoHTTPD { void clear(); - TempFile createTempFile(String filename_hint) throws Exception; + public TempFile createTempFile(String filename_hint) throws Exception; } /** @@ -1567,7 +1617,16 @@ public abstract class NanoHTTPD { */ public interface TempFileManagerFactory { - TempFileManager create(); + public TempFileManager create(); + } + + /** + * Factory to create ServerSocketFactories. + */ + public interface ServerSocketFactory { + + public ServerSocket create() throws IOException; + } /** @@ -1726,9 +1785,7 @@ public abstract class NanoHTTPD { private volatile ServerSocket myServerSocket; - private SSLServerSocketFactory sslServerSocketFactory; - - private String[] sslProtocols; + private ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory(); private Thread myThread; @@ -1883,12 +1940,27 @@ public abstract class NanoHTTPD { return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); } + public ServerSocketFactory getServerSocketFactory() { + return serverSocketFactory; + } + + public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) { + this.serverSocketFactory = serverSocketFactory; + } + + public String getHostname() { + return hostname; + } + + public TempFileManagerFactory getTempFileManagerFactory() { + return tempFileManagerFactory; + } + /** * Call before start() to serve over HTTPS instead of HTTP */ public void makeSecure(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { - this.sslServerSocketFactory = sslServerSocketFactory; - this.sslProtocols = sslProtocols; + this.serverSocketFactory = new SecureServerSocketFactory(sslServerSocketFactory, sslProtocols); } /** @@ -2012,6 +2084,13 @@ public abstract class NanoHTTPD { } /** + * Starts the server (in setDaemon(true) mode). + */ + public void start(final int timeout) throws IOException { + start(timeout, true); + } + + /** * Start the server. * * @param timeout @@ -2022,21 +2101,7 @@ public abstract class NanoHTTPD { * if the socket is in use. */ public void start(final int timeout, boolean daemon) throws IOException { - if (this.sslServerSocketFactory != null) { - SSLServerSocket ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); - if (this.sslProtocols != null) { - ss.setEnabledProtocols(this.sslProtocols); - } else { - ss.setEnabledProtocols(ss.getSupportedProtocols()); - } - ss.setUseClientMode(false); - ss.setWantClientAuth(false); - ss.setNeedClientAuth(false); - ss.setSoTimeout(timeout); - this.myServerSocket = ss; - } else { - this.myServerSocket = new ServerSocket(); - } + this.myServerSocket = this.getServerSocketFactory().create(); this.myServerSocket.setReuseAddress(true); ServerRunnable serverRunnable = createServerRunnable(timeout); @@ -2059,13 +2124,6 @@ public abstract class NanoHTTPD { } /** - * Starts the server (in setDaemon(true) mode). - */ - public void start(final int timeout) throws IOException { - start(timeout, true); - } - - /** * Stop the server. */ public void stop() { diff --git a/core/src/test/java/fi/iki/elonen/SSLServerSocketFactoryTest.java b/core/src/test/java/fi/iki/elonen/SSLServerSocketFactoryTest.java new file mode 100644 index 0000000..1722058 --- /dev/null +++ b/core/src/test/java/fi/iki/elonen/SSLServerSocketFactoryTest.java @@ -0,0 +1,90 @@ +package fi.iki.elonen; + +import java.io.File; + +/* + * #%L + * NanoHttpd-Core + * %% + * Copyright (C) 2012 - 2015 nanohttpd + * %% + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the nanohttpd nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +import java.io.IOException; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpTrace; +import org.apache.http.impl.client.DefaultHttpClient; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import fi.iki.elonen.NanoHTTPD.SecureServerSocketFactory; + +public class SSLServerSocketFactoryTest extends HttpServerTest { + + @Test + public void testSSLConnection() throws ClientProtocolException, IOException { + DefaultHttpClient httpclient = new DefaultHttpClient(); + HttpTrace httphead = new HttpTrace("https://localhost:9043/index.html"); + HttpResponse response = httpclient.execute(httphead); + HttpEntity entity = response.getEntity(); + Assert.assertEquals(200, response.getStatusLine().getStatusCode()); + + Assert.assertEquals(9043, this.testServer.getListeningPort()); + Assert.assertTrue(this.testServer.isAlive()); + } + + @Before + public void setUp() throws Exception { + System.setProperty("javax.net.ssl.trustStore", new File("src/test/resources/keystore.jks").getAbsolutePath()); + this.testServer = new TestServer(9043); + this.testServer.setServerSocketFactory(new SecureServerSocketFactory(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null)); + this.tempFileManager = new TestTempFileManager(); + this.testServer.start(); + try { + long start = System.currentTimeMillis(); + Thread.sleep(100L); + while (!this.testServer.wasStarted()) { + Thread.sleep(100L); + if (System.currentTimeMillis() - start > 2000) { + Assert.fail("could not start server"); + } + } + } catch (InterruptedException e) { + } + } + + @After + public void tearDown() { + this.testServer.stop(); + } +} diff --git a/core/src/test/java/fi/iki/elonen/ServerSocketFactoryTest.java b/core/src/test/java/fi/iki/elonen/ServerSocketFactoryTest.java new file mode 100644 index 0000000..17112ef --- /dev/null +++ b/core/src/test/java/fi/iki/elonen/ServerSocketFactoryTest.java @@ -0,0 +1,102 @@ +package fi.iki.elonen; + +import java.io.File; + +/* + * #%L + * NanoHttpd-Core + * %% + * Copyright (C) 2012 - 2015 nanohttpd + * %% + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the nanohttpd nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +import java.io.IOException; +import java.net.ServerSocket; + +import org.junit.Assert; +import org.junit.Test; + +import fi.iki.elonen.HttpServerTest.TestServer; +import fi.iki.elonen.NanoHTTPD.SecureServerSocketFactory; + +public class ServerSocketFactoryTest extends NanoHTTPD { + + public static final int PORT = 8192; + + public ServerSocketFactoryTest() { + super(PORT); + + this.setServerSocketFactory(new TestFactory()); + } + + @Test + public void isCustomServerSocketFactory() { + System.out.println("CustomServerSocketFactory test"); + Assert.assertTrue(this.getServerSocketFactory() instanceof TestFactory); + } + + @Test + public void testCreateServerSocket() { + System.out.println("CreateServerSocket test"); + ServerSocket ss = null; + try { + ss = this.getServerSocketFactory().create(); + } catch (IOException e) { + } + Assert.assertTrue(ss != null); + } + + @Test + public void testSSLServerSocketFail() { + String[] protocols = { + "" + }; + System.setProperty("javax.net.ssl.trustStore", new File("src/test/resources/keystore.jks").getAbsolutePath()); + ServerSocketFactory ssFactory = new SecureServerSocketFactory(null, protocols); + ServerSocket ss = null; + try { + ss = ssFactory.create(); + } catch (Exception e) { + } + Assert.assertTrue(ss == null); + + } + + private class TestFactory implements ServerSocketFactory { + + @Override + public ServerSocket create() { + try { + return new ServerSocket(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + } +} |