aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorritchie <ritchie@gmx.at>2015-05-14 08:06:20 +0200
committerritchie <ritchie@gmx.at>2015-05-14 08:06:20 +0200
commit6a77bee0dad6e34e9789b5065c4367286f619efd (patch)
tree40c90c7810e185d439d8223eaf4a9cf77783bf11 /core
parentdec699357b2281a580023c6c564612b7b89c5fe8 (diff)
parent0e84bd46b35c640e049e5350af44a81472bf9953 (diff)
downloadnanohttpd-6a77bee0dad6e34e9789b5065c4367286f619efd.tar.gz
Merge branch 'local' into response-constructor-fix
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/fi/iki/elonen/NanoHTTPD.java220
1 files changed, 131 insertions, 89 deletions
diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
index e1be81b..2a7713e 100644
--- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
+++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
@@ -60,14 +60,13 @@ import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.logging.Level;
@@ -138,7 +137,60 @@ public abstract class NanoHTTPD {
*/
public interface AsyncRunner {
- void exec(Runnable code);
+ void closeAll();
+
+ void closed(ClientHandler clientHandler);
+
+ void exec(ClientHandler code);
+ }
+
+ /**
+ * The runnable that will be used for every new client connection.
+ */
+ public class ClientHandler implements Runnable {
+
+ private final InputStream inputStream;
+
+ private final Socket acceptSocket;
+
+ private ClientHandler(InputStream inputStream, Socket acceptSocket) {
+ this.inputStream = inputStream;
+ this.acceptSocket = acceptSocket;
+ }
+
+ public void close() {
+ safeClose(this.inputStream);
+ safeClose(this.acceptSocket);
+ }
+
+ @Override
+ public void run() {
+ OutputStream outputStream = null;
+ try {
+ outputStream = this.acceptSocket.getOutputStream();
+ TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create();
+ HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress());
+ while (!this.acceptSocket.isClosed()) {
+ session.execute();
+ }
+ } catch (Exception e) {
+ // When the socket is closed by the client,
+ // we throw our own SocketException
+ // 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)) {
+ NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
+ }
+ } finally {
+ safeClose(outputStream);
+ safeClose(this.inputStream);
+ safeClose(this.acceptSocket);
+ NanoHTTPD.this.asyncRunner.closed(this);
+ }
+ }
}
public static class Cookie {
@@ -274,12 +326,28 @@ public abstract class NanoHTTPD {
private long requestCount;
+ private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>());
+
@Override
- public void exec(Runnable code) {
+ public void closeAll() {
+ // copy of the list for concurrency
+ for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) {
+ clientHandler.close();
+ }
+ }
+
+ @Override
+ public void closed(ClientHandler clientHandler) {
+ this.running.remove(clientHandler);
+ }
+
+ @Override
+ public void exec(ClientHandler clientHandler) {
++this.requestCount;
- Thread t = new Thread(code);
+ Thread t = new Thread(clientHandler);
t.setDaemon(true);
t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")");
+ this.running.add(clientHandler);
t.start();
}
}
@@ -1276,6 +1344,32 @@ public abstract class NanoHTTPD {
}
/**
+ * The runnable that will be used for the main listening thread.
+ */
+ public class ServerRunnable implements Runnable {
+
+ private final int timeout;
+
+ private ServerRunnable(int timeout) {
+ this.timeout = timeout;
+ }
+
+ @Override
+ public void run() {
+ do {
+ try {
+ final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept();
+ finalAccept.setSoTimeout(this.timeout);
+ final InputStream inputStream = finalAccept.getInputStream();
+ NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream));
+ } catch (IOException e) {
+ NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
+ }
+ } while (!NanoHTTPD.this.myServerSocket.isClosed());
+ }
+ }
+
+ /**
* A temp file.
* <p/>
* <p>
@@ -1420,8 +1514,6 @@ public abstract class NanoHTTPD {
private ServerSocket myServerSocket;
- private final Set<Socket> openConnections = new HashSet<Socket>();
-
private SSLServerSocketFactory sslServerSocketFactory;
private Thread myThread;
@@ -1461,21 +1553,37 @@ public abstract class NanoHTTPD {
setAsyncRunner(new DefaultAsyncRunner());
}
- // -------------------------------------------------------------------------------
- // //
- //
- // Temp file handling strategy.
- //
- // -------------------------------------------------------------------------------
- // //
-
/**
* Forcibly closes all connections that are open.
*/
public synchronized void closeAllConnections() {
- for (Socket socket : this.openConnections) {
- safeClose(socket);
- }
+ stop();
+ }
+
+ /**
+ * create a instance of the client handler, subclasses can return a subclass
+ * of the ClientHandler.
+ *
+ * @param finalAccept
+ * the socket the cleint is connected to
+ * @param inputStream
+ * the input stream
+ * @return the client handler
+ */
+ protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) {
+ return new ClientHandler(inputStream, finalAccept);
+ }
+
+ /**
+ * Instantiate the server runnable, can be overwritten by subclasses to
+ * provide a subclass of the ServerRunnable.
+ *
+ * @param timeout
+ * the socet timeout to use.
+ * @return the server runnable.
+ */
+ protected ServerRunnable createServerRunnable(final int timeout) {
+ return new ServerRunnable(timeout);
}
/**
@@ -1493,6 +1601,9 @@ public abstract class NanoHTTPD {
return this.decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER));
}
+ // -------------------------------------------------------------------------------
+ // //
+
/**
* Decode parameters from a URL, handing the case where a single parameter
* name might have been supplied several times, by return lists of values.
@@ -1541,9 +1652,6 @@ public abstract class NanoHTTPD {
return decoded;
}
- // -------------------------------------------------------------------------------
- // //
-
public final int getListeningPort() {
return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort();
}
@@ -1560,16 +1668,6 @@ public abstract class NanoHTTPD {
}
/**
- * Registers that a new connection has been set up.
- *
- * @param socket
- * the {@link Socket} for the connection.
- */
- public synchronized void registerConnection(Socket socket) {
- this.openConnections.add(socket);
- }
-
- /**
* Override this to customize the server.
* <p/>
* <p/>
@@ -1669,53 +1767,7 @@ public abstract class NanoHTTPD {
this.myServerSocket.setReuseAddress(true);
this.myServerSocket.bind(this.hostname != null ? new InetSocketAddress(this.hostname, this.myPort) : new InetSocketAddress(this.myPort));
- this.myThread = new Thread(new Runnable() {
-
- @Override
- public void run() {
- do {
- try {
- final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept();
- registerConnection(finalAccept);
- finalAccept.setSoTimeout(timeout);
- final InputStream inputStream = finalAccept.getInputStream();
- NanoHTTPD.this.asyncRunner.exec(new Runnable() {
-
- @Override
- public void run() {
- OutputStream outputStream = null;
- try {
- outputStream = finalAccept.getOutputStream();
- TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create();
- HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
- while (!finalAccept.isClosed()) {
- session.execute();
- }
- } catch (Exception e) {
- // When the socket is closed by the client,
- // we throw our own SocketException
- // 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)) {
- NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
- }
- } finally {
- safeClose(outputStream);
- safeClose(inputStream);
- safeClose(finalAccept);
- unRegisterConnection(finalAccept);
- }
- }
- });
- } catch (IOException e) {
- NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
- }
- } while (!NanoHTTPD.this.myServerSocket.isClosed());
- }
- });
+ this.myThread = new Thread(createServerRunnable(timeout));
this.myThread.setDaemon(true);
this.myThread.setName("NanoHttpd Main Listener");
this.myThread.start();
@@ -1727,7 +1779,7 @@ public abstract class NanoHTTPD {
public void stop() {
try {
safeClose(this.myServerSocket);
- closeAllConnections();
+ this.asyncRunner.closeAll();
if (this.myThread != null) {
this.myThread.join();
}
@@ -1736,16 +1788,6 @@ public abstract class NanoHTTPD {
}
}
- /**
- * Registers that a connection has been closed
- *
- * @param socket
- * the {@link Socket} for the connection.
- */
- public synchronized void unRegisterConnection(Socket socket) {
- this.openConnections.remove(socket);
- }
-
public final boolean wasStarted() {
return this.myServerSocket != null && this.myThread != null;
}