aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard van Nieuwenhoven <richard.vannieuwenhoven@adesso.at>2015-10-23 06:57:48 +0200
committerRichard van Nieuwenhoven <richard.vannieuwenhoven@adesso.at>2015-10-23 06:57:48 +0200
commit6bb307d2064fcae01fcbb06eb9f7c7f89ddc26ee (patch)
treed760ac6f0677fbcf72164745c27b2a6c18a48cc2
parentfbc38a43e727e481e09a96b33998eaefa0ced61c (diff)
parent4c3db1479b17ae004301a6214e9adb88040fefc4 (diff)
downloadnanohttpd-6bb307d2064fcae01fcbb06eb9f7c7f89ddc26ee.tar.gz
Merge pull request #238 from Hoeze/master
Added ServerSocketFactory to allow custom ServerSockets;
-rw-r--r--core/src/main/java/fi/iki/elonen/NanoHTTPD.java126
-rw-r--r--core/src/test/java/fi/iki/elonen/SSLServerSocketFactoryTest.java90
-rw-r--r--core/src/test/java/fi/iki/elonen/ServerSocketFactoryTest.java102
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;
+ }
+ }
+}