aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/java
diff options
context:
space:
mode:
authorritchie <ritchie@gmx.at>2015-05-10 13:41:19 +0200
committerritchie <ritchie@gmx.at>2015-05-10 13:41:19 +0200
commit9058464950a9734da0a7ff2dc47f3081bbb5117c (patch)
tree6e6ab43930c3d0e134dc194e4a8a4a57cb1c7e7f /core/src/main/java
parenta59803b641f7eee45cfd0cf170e669dfe2b1eb8d (diff)
downloadnanohttpd-9058464950a9734da0a7ff2dc47f3081bbb5117c.tar.gz
after the last pull request in this series the formatter is now active
Diffstat (limited to 'core/src/main/java')
-rw-r--r--core/src/main/java/fi/iki/elonen/NanoHTTPD.java548
1 files changed, 337 insertions, 211 deletions
diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
index 5132bbf..0705196 100644
--- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
+++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
@@ -72,7 +72,6 @@ import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
-
import java.security.KeyStore;
import javax.net.ssl.*;
@@ -81,7 +80,10 @@ import javax.net.ssl.*;
* <p/>
* <p/>
* NanoHTTPD
- * <p>Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 2010 by Konstantinos Togias</p>
+ * <p>
+ * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen,
+ * 2010 by Konstantinos Togias
+ * </p>
* <p/>
* <p/>
* <b>Features + limitations: </b>
@@ -90,8 +92,10 @@ import javax.net.ssl.*;
* <li>Only one Java file</li>
* <li>Java 5 compatible</li>
* <li>Released as open source, Modified BSD licence</li>
- * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li>
- * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li>
+ * <li>No fixed config files, logging, authorization etc. (Implement yourself if
+ * you need them.)</li>
+ * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT
+ * support in 1.25)</li>
* <li>Supports both dynamic content and file serving</li>
* <li>Supports file upload (since version 1.2, 2010)</li>
* <li>Supports partial content (streaming)</li>
@@ -106,7 +110,8 @@ import javax.net.ssl.*;
* <li>File server supports simple skipping for files (continue download)</li>
* <li>File server serves also very long files without memory overhead</li>
* <li>Contains a built-in list of most common MIME types</li>
- * <li>All header names are converted to lower case so they don't vary between browsers/clients</li>
+ * <li>All header names are converted to lower case so they don't vary between
+ * browsers/clients</li>
* <p/>
* </ul>
* <p/>
@@ -118,43 +123,56 @@ import javax.net.ssl.*;
* <p/>
* </ul>
* <p/>
- * See the separate "LICENSE.md" file for the distribution license (Modified BSD licence)
+ * See the separate "LICENSE.md" file for the distribution license (Modified BSD
+ * licence)
*/
public abstract class NanoHTTPD {
+
/**
* Maximum time to wait on Socket.getInputStream().read() (in milliseconds)
- * This is required as the Keep-Alive HTTP connections would otherwise
- * block the socket reading thread forever (or as long the browser is open).
+ * This is required as the Keep-Alive HTTP connections would otherwise block
+ * the socket reading thread forever (or as long the browser is open).
*/
public static final int SOCKET_READ_TIMEOUT = 5000;
+
/**
* Common MIME type for dynamic content: plain text
*/
public static final String MIME_PLAINTEXT = "text/plain";
+
/**
* Common MIME type for dynamic content: html
*/
public static final String MIME_HTML = "text/html";
+
/**
- * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
+ * Pseudo-Parameter to use to store the actual query string in the
+ * parameters map for later re-processing.
*/
private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
-
- /**
- * logger to log to.
- */
- private static Logger LOG = Logger.getLogger(NanoHTTPD.class.getName());
-
+
+ /**
+ * logger to log to.
+ */
+ private static Logger LOG = Logger.getLogger(NanoHTTPD.class.getName());
+
private final String hostname;
+
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.
*/
private AsyncRunner asyncRunner;
+
/**
* Pluggable strategy for creating and cleaning up temporary files.
*/
@@ -182,86 +200,84 @@ public abstract class NanoHTTPD {
try {
closeable.close();
} catch (IOException e) {
- LOG.log(Level.SEVERE, "Could not close",e);
+ LOG.log(Level.SEVERE, "Could not close", e);
}
}
}
- /**
- * 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;
- }
+ /**
+ * 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.
+ *
+ * @throws IOException
+ * if the socket is in use.
*/
public void start() throws IOException {
if (sslServerSocketFactory != null) {
@@ -275,6 +291,7 @@ public abstract class NanoHTTPD {
myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
myThread = new Thread(new Runnable() {
+
@Override
public void run() {
do {
@@ -284,6 +301,7 @@ public abstract class NanoHTTPD {
finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
final InputStream inputStream = finalAccept.getInputStream();
asyncRunner.exec(new Runnable() {
+
@Override
public void run() {
OutputStream outputStream = null;
@@ -295,13 +313,15 @@ public abstract class NanoHTTPD {
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
+ // 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)) {
- LOG.log(Level.FINE, "Communication with the client broken", e);
+ if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) {
+ LOG.log(Level.FINE, "Communication with the client broken", e);
}
} finally {
safeClose(outputStream);
@@ -312,7 +332,7 @@ public abstract class NanoHTTPD {
}
});
} catch (IOException e) {
- LOG.log(Level.FINE, "Communication with the client broken", e);
+ LOG.log(Level.FINE, "Communication with the client broken", e);
}
} while (!myServerSocket.isClosed());
}
@@ -333,14 +353,15 @@ public abstract class NanoHTTPD {
myThread.join();
}
} catch (Exception e) {
- LOG.log(Level.SEVERE, "Could not stop all connections", e);
+ LOG.log(Level.SEVERE, "Could not stop all connections", e);
}
}
/**
* Registers that a new connection has been set up.
- *
- * @param socket the {@link Socket} for the connection.
+ *
+ * @param socket
+ * the {@link Socket} for the connection.
*/
public synchronized void registerConnection(Socket socket) {
openConnections.add(socket);
@@ -348,7 +369,7 @@ public abstract class NanoHTTPD {
/**
* Registers that a connection has been closed
- *
+ *
* @param socket
* the {@link Socket} for the connection.
*/
@@ -382,16 +403,21 @@ public abstract class NanoHTTPD {
* <p/>
* <p/>
* (By default, this returns a 404 "Not Found" plain text error response.)
- *
- * @param uri Percent-decoded URI without parameters, for example "/index.cgi"
- * @param method "GET", "POST" etc.
- * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data.
- * @param headers Header entries, percent decoded
+ *
+ * @param uri
+ * Percent-decoded URI without parameters, for example
+ * "/index.cgi"
+ * @param method
+ * "GET", "POST" etc.
+ * @param parms
+ * Parsed, percent decoded parameters from URI and, in case of
+ * POST, data.
+ * @param headers
+ * Header entries, percent decoded
* @return HTTP response, see class Response for details
*/
@Deprecated
- public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,
- Map<String, String> files) {
+ public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) {
return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found");
}
@@ -400,8 +426,9 @@ public abstract class NanoHTTPD {
* <p/>
* <p/>
* (By default, this returns a 404 "Not Found" plain text error response.)
- *
- * @param session The HTTP session
+ *
+ * @param session
+ * The HTTP session
* @return HTTP response, see class Response for details
*/
public Response serve(IHTTPSession session) {
@@ -424,39 +451,46 @@ public abstract class NanoHTTPD {
/**
* Decode percent encoded <code>String</code> values.
- *
- * @param str the percent encoded <code>String</code>
- * @return expanded form of the input, for example "foo%20bar" becomes "foo bar"
+ *
+ * @param str
+ * the percent encoded <code>String</code>
+ * @return expanded form of the input, for example "foo%20bar" becomes
+ * "foo bar"
*/
protected String decodePercent(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF8");
} catch (UnsupportedEncodingException ignored) {
- LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored);
+ LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored);
}
return decoded;
}
/**
- * Decode parameters from a URL, handing the case where a single parameter name might have been
- * supplied several times, by return lists of values. In general these lists will contain a single
- * element.
- *
- * @param parms original <b>NanoHTTPD</b> parameters values, as passed to the <code>serve()</code> method.
- * @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
+ * Decode parameters from a URL, handing the case where a single parameter
+ * name might have been supplied several times, by return lists of values.
+ * In general these lists will contain a single element.
+ *
+ * @param parms
+ * original <b>NanoHTTPD</b> parameters values, as passed to the
+ * <code>serve()</code> method.
+ * @return a map of <code>String</code> (parameter name) to
+ * <code>List&lt;String&gt;</code> (a list of the values supplied).
*/
protected Map<String, List<String>> decodeParameters(Map<String, String> parms) {
return this.decodeParameters(parms.get(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. In general these lists will contain a single
- * element.
- *
- * @param queryString a query string pulled from the URL.
- * @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
+ * Decode parameters from a URL, handing the case where a single parameter
+ * name might have been supplied several times, by return lists of values.
+ * In general these lists will contain a single element.
+ *
+ * @param queryString
+ * a query string pulled from the URL.
+ * @return a map of <code>String</code> (parameter name) to
+ * <code>List&lt;String&gt;</code> (a list of the values supplied).
*/
protected Map<String, List<String>> decodeParameters(String queryString) {
Map<String, List<String>> parms = new HashMap<String, List<String>>();
@@ -478,41 +512,53 @@ public abstract class NanoHTTPD {
return parms;
}
- // ------------------------------------------------------------------------------- //
+ // -------------------------------------------------------------------------------
+ // //
//
// Threading Strategy.
//
- // ------------------------------------------------------------------------------- //
+ // -------------------------------------------------------------------------------
+ // //
/**
* Pluggable strategy for asynchronously executing requests.
- *
- * @param asyncRunner new strategy for handling threads.
+ *
+ * @param asyncRunner
+ * new strategy for handling threads.
*/
public void setAsyncRunner(AsyncRunner asyncRunner) {
this.asyncRunner = asyncRunner;
}
- // ------------------------------------------------------------------------------- //
+ // -------------------------------------------------------------------------------
+ // //
//
// Temp file handling strategy.
//
- // ------------------------------------------------------------------------------- //
+ // -------------------------------------------------------------------------------
+ // //
/**
* Pluggable strategy for creating and cleaning up temporary files.
- *
- * @param tempFileManagerFactory new strategy for handling temp files.
+ *
+ * @param tempFileManagerFactory
+ * new strategy for handling temp files.
*/
public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
this.tempFileManagerFactory = tempFileManagerFactory;
}
/**
- * HTTP Request methods, with the ability to decode a <code>String</code> back to its enum value.
+ * HTTP Request methods, with the ability to decode a <code>String</code>
+ * back to its enum value.
*/
public enum Method {
- GET, PUT, POST, DELETE, HEAD, OPTIONS;
+ GET,
+ PUT,
+ POST,
+ DELETE,
+ HEAD,
+ OPTIONS;
static Method lookup(String method) {
for (Method m : Method.values()) {
@@ -528,6 +574,7 @@ public abstract class NanoHTTPD {
* Pluggable strategy for asynchronously executing requests.
*/
public interface AsyncRunner {
+
void exec(Runnable code);
}
@@ -535,18 +582,23 @@ public abstract class NanoHTTPD {
* Factory to create temp file managers.
*/
public interface TempFileManagerFactory {
+
TempFileManager create();
}
- // ------------------------------------------------------------------------------- //
+ // -------------------------------------------------------------------------------
+ // //
/**
* Temp file manager.
* <p/>
- * <p>Temp file managers are created 1-to-1 with incoming requests, to create and cleanup
- * temporary files created as a result of handling the request.</p>
+ * <p>
+ * Temp file managers are created 1-to-1 with incoming requests, to create
+ * and cleanup temporary files created as a result of handling the request.
+ * </p>
*/
public interface TempFileManager {
+
TempFile createTempFile() throws Exception;
void clear();
@@ -555,10 +607,13 @@ public abstract class NanoHTTPD {
/**
* A temp file.
* <p/>
- * <p>Temp files are responsible for managing the actual temporary storage and cleaning
- * themselves up when no longer needed.</p>
+ * <p>
+ * Temp files are responsible for managing the actual temporary storage and
+ * cleaning themselves up when no longer needed.
+ * </p>
*/
public interface TempFile {
+
OutputStream open() throws Exception;
void delete() throws Exception;
@@ -569,11 +624,14 @@ public abstract class NanoHTTPD {
/**
* Default threading strategy for NanoHTTPD.
* <p/>
- * <p>By default, the server spawns a new Thread for every incoming request. These are set
- * to <i>daemon</i> status, and named according to the request number. The name is
- * useful when profiling the application.</p>
+ * <p>
+ * By default, the server spawns a new Thread for every incoming request.
+ * These are set to <i>daemon</i> status, and named according to the request
+ * number. The name is useful when profiling the application.
+ * </p>
*/
public static class DefaultAsyncRunner implements AsyncRunner {
+
private long requestCount;
@Override
@@ -589,14 +647,17 @@ public abstract class NanoHTTPD {
/**
* Default strategy for creating and cleaning up temporary files.
* <p/>
- * <p>This class stores its files in the standard location (that is,
- * wherever <code>java.io.tmpdir</code> points to). Files are added
- * to an internal list, and deleted when no longer needed (that is,
- * when <code>clear()</code> is invoked at the end of processing a
- * request).</p>
+ * <p>
+ * This class stores its files in the standard location (that is, wherever
+ * <code>java.io.tmpdir</code> points to). Files are added to an internal
+ * list, and deleted when no longer needed (that is, when
+ * <code>clear()</code> is invoked at the end of processing a request).
+ * </p>
*/
public static class DefaultTempFileManager implements TempFileManager {
+
private final String tmpdir;
+
private final List<TempFile> tempFiles;
public DefaultTempFileManager() {
@@ -617,7 +678,7 @@ public abstract class NanoHTTPD {
try {
file.delete();
} catch (Exception ignored) {
- LOG.log(Level.WARNING, "could not delete file ", ignored);
+ LOG.log(Level.WARNING, "could not delete file ", ignored);
}
}
tempFiles.clear();
@@ -627,11 +688,15 @@ public abstract class NanoHTTPD {
/**
* Default strategy for creating and cleaning up temporary files.
* <p/>
- * <p>By default, files are created by <code>File.createTempFile()</code> in
- * the directory specified.</p>
+ * <p>
+ * By default, files are created by <code>File.createTempFile()</code> in
+ * the directory specified.
+ * </p>
*/
public static class DefaultTempFile implements TempFile {
+
private File file;
+
private OutputStream fstream;
public DefaultTempFile(String tempdir) throws IOException {
@@ -660,33 +725,40 @@ public abstract class NanoHTTPD {
* HTTP response. Return one of these from serve().
*/
public static class Response {
+
/**
* HTTP status code after processing, e.g. "200 OK", Status.OK
*/
private IStatus status;
+
/**
* MIME type of content, e.g. "text/html"
*/
private String mimeType;
+
/**
* Data of the response, may be null.
*/
private InputStream data;
+
/**
* Headers for the HTTP response. Use addHeader() to add lines.
*/
private Map<String, String> header = new HashMap<String, String>();
+
/**
* The request method that spawned this response.
*/
private Method requestMethod;
+
/**
* Use chunkedTransfer
*/
private boolean chunkedTransfer;
/**
- * Default constructor: response = Status.OK, mime = MIME_HTML and your supplied message
+ * Default constructor: response = Status.OK, mime = MIME_HTML and your
+ * supplied message
*/
public Response(String msg) {
this(Status.OK, MIME_HTML, msg);
@@ -710,7 +782,7 @@ public abstract class NanoHTTPD {
try {
this.data = txt != null ? new ByteArrayInputStream(txt.getBytes("UTF-8")) : null;
} catch (java.io.UnsupportedEncodingException uee) {
- LOG.log(Level.SEVERE,"encoding problem",uee);
+ LOG.log(Level.SEVERE, "encoding problem", uee);
}
}
@@ -769,7 +841,7 @@ public abstract class NanoHTTPD {
outputStream.flush();
safeClose(data);
} catch (IOException ioe) {
- LOG.log(Level.SEVERE, "Could not send response to the client", ioe);
+ LOG.log(Level.SEVERE, "Could not send response to the client", ioe);
}
}
@@ -784,7 +856,7 @@ public abstract class NanoHTTPD {
}
}
- pw.print("Content-Length: "+ size +"\r\n");
+ pw.print("Content-Length: " + size + "\r\n");
return size;
}
@@ -870,7 +942,9 @@ public abstract class NanoHTTPD {
}
public interface IStatus {
+
int getRequestStatus();
+
String getDescription();
}
@@ -878,11 +952,24 @@ public abstract class NanoHTTPD {
* Some HTTP response status codes
*/
public enum Status implements IStatus {
- SWITCH_PROTOCOL(101, "Switching Protocols"), OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), NO_CONTENT(204, "No Content"), PARTIAL_CONTENT(206, "Partial Content"), REDIRECT(301,
- "Moved Permanently"), NOT_MODIFIED(304, "Not Modified"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401,
- "Unauthorized"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), METHOD_NOT_ALLOWED(405, "Method Not Allowed"), RANGE_NOT_SATISFIABLE(416,
- "Requested Range Not Satisfiable"), INTERNAL_ERROR(500, "Internal Server Error");
+ SWITCH_PROTOCOL(101, "Switching Protocols"),
+ OK(200, "OK"),
+ CREATED(201, "Created"),
+ ACCEPTED(202, "Accepted"),
+ NO_CONTENT(204, "No Content"),
+ PARTIAL_CONTENT(206, "Partial Content"),
+ REDIRECT(301, "Moved Permanently"),
+ NOT_MODIFIED(304, "Not Modified"),
+ BAD_REQUEST(400, "Bad Request"),
+ UNAUTHORIZED(401, "Unauthorized"),
+ FORBIDDEN(403, "Forbidden"),
+ NOT_FOUND(404, "Not Found"),
+ METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
+ RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
+ INTERNAL_ERROR(500, "Internal Server Error");
+
private final int requestStatus;
+
private final String description;
Status(int requestStatus, String description) {
@@ -903,6 +990,7 @@ public abstract class NanoHTTPD {
}
public static final class ResponseException extends Exception {
+
private static final long serialVersionUID = 6569838532917408380L;
private final Response.Status status;
@@ -926,6 +1014,7 @@ public abstract class NanoHTTPD {
* Default strategy for creating and cleaning up temporary files.
*/
private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
+
@Override
public TempFileManager create() {
return new DefaultTempFileManager();
@@ -933,9 +1022,11 @@ public abstract class NanoHTTPD {
}
/**
- * Handles one session, i.e. parses the HTTP request and returns the response.
+ * Handles one session, i.e. parses the HTTP request and returns the
+ * response.
*/
public interface IHTTPSession {
+
void execute() throws IOException;
Map<String, String> getParms();
@@ -957,24 +1048,39 @@ public abstract class NanoHTTPD {
/**
* Adds the files in the request body to the files map.
- * @param files map to modify
+ *
+ * @param files
+ * map to modify
*/
void parseBody(Map<String, String> files) throws IOException, ResponseException;
}
protected class HTTPSession implements IHTTPSession {
+
public static final int BUFSIZE = 8192;
+
private final TempFileManager tempFileManager;
+
private final OutputStream outputStream;
+
private PushbackInputStream inputStream;
+
private int splitbyte;
+
private int rlen;
+
private String uri;
+
private Method method;
+
private Map<String, String> parms;
+
private Map<String, String> headers;
+
private CookieHandler cookies;
+
private String queryParameterString;
+
private String remoteIp;
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
@@ -997,7 +1103,8 @@ public abstract class NanoHTTPD {
// Read the first 8192 bytes.
// The full header should fit in here.
// Apache's default header limit is 8KB.
- // Do NOT assume that a single read will get the entire header at once!
+ // Do NOT assume that a single read will get the entire header
+ // at once!
byte[] buf = new byte[BUFSIZE];
splitbyte = 0;
rlen = 0;
@@ -1030,17 +1137,16 @@ public abstract class NanoHTTPD {
}
parms = new HashMap<String, String>();
- if(null == headers) {
+ if (null == headers) {
headers = new HashMap<String, String>();
} else {
headers.clear();
}
if (null != remoteIp) {
- headers.put("remote-addr", remoteIp);
- headers.put("http-client-ip", remoteIp);
- }
-
+ headers.put("remote-addr", remoteIp);
+ headers.put("http-client-ip", remoteIp);
+ }
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
@@ -1074,7 +1180,7 @@ public abstract class NanoHTTPD {
// 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;
+ throw ste;
} catch (IOException ioe) {
Response r = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
r.send(outputStream);
@@ -1108,7 +1214,7 @@ public abstract class NanoHTTPD {
// Now read all the body and write it to f
byte[] buf = new byte[512];
while (rlen >= 0 && size > 0) {
- rlen = inputStream.read(buf, 0, (int)Math.min(size, 512));
+ rlen = inputStream.read(buf, 0, (int) Math.min(size, 512));
size -= rlen;
if (rlen > 0) {
randomAccessFile.write(buf, 0, rlen);
@@ -1140,7 +1246,8 @@ public abstract class NanoHTTPD {
if ("multipart/form-data".equalsIgnoreCase(contentType)) {
// Handle multipart/form-data
if (!st.hasMoreTokens()) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
+ throw new ResponseException(Response.Status.BAD_REQUEST,
+ "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
}
String boundaryStartString = "boundary=";
@@ -1164,10 +1271,12 @@ public abstract class NanoHTTPD {
postLine = postLineBuffer.toString().trim();
// Handle application/x-www-form-urlencoded
if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) {
- decodeParms(postLine, parms);
+ decodeParms(postLine, parms);
} else if (postLine.length() != 0) {
- // Special case for raw POST data => create a special files entry "postData" with raw content data
- files.put("postData", postLine);
+ // Special case for raw POST data => create a
+ // special files entry "postData" with raw content
+ // data
+ files.put("postData", postLine);
}
}
} else if (Method.PUT.equals(method)) {
@@ -1182,8 +1291,7 @@ public abstract class NanoHTTPD {
/**
* Decodes the sent headers and loads the data into Key/value pairs
*/
- private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers)
- throws ResponseException {
+ private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException {
try {
// Read the request line
String inLine = in.readLine();
@@ -1237,15 +1345,15 @@ public abstract class NanoHTTPD {
/**
* Decodes the Multipart Body data and put it into Key/Value pairs.
*/
- private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
- Map<String, String> files) throws ResponseException {
+ private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms, Map<String, String> files) throws ResponseException {
try {
int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
int boundarycount = 1;
String mpline = in.readLine();
while (mpline != null) {
if (!mpline.contains(boundary)) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
+ throw new ResponseException(Response.Status.BAD_REQUEST,
+ "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
}
boundarycount++;
Map<String, String> item = new HashMap<String, String>();
@@ -1260,7 +1368,8 @@ public abstract class NanoHTTPD {
if (mpline != null) {
String contentDisposition = item.get("content-disposition");
if (contentDisposition == null) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
+ throw new ResponseException(Response.Status.BAD_REQUEST,
+ "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
}
StringTokenizer st = new StringTokenizer(contentDisposition, ";");
Map<String, String> disposition = new HashMap<String, String>();
@@ -1293,14 +1402,14 @@ public abstract class NanoHTTPD {
}
int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
- if(!files.containsKey(pname)) {
- files.put(pname, path);
+ if (!files.containsKey(pname)) {
+ files.put(pname, path);
} else {
- int count = 2;
- while(files.containsKey(pname+count)) {
- count++;
- }
- files.put(pname+count, path);
+ int count = 2;
+ while (files.containsKey(pname + count)) {
+ count++;
+ }
+ files.put(pname + count, path);
}
value = disposition.get("filename");
value = value.substring(1, value.length() - 1);
@@ -1317,7 +1426,8 @@ public abstract class NanoHTTPD {
}
/**
- * Find byte index separating header from body. It must be the last byte of the first two sequential new lines.
+ * Find byte index separating header from body. It must be the last byte
+ * of the first two sequential new lines.
*/
private int findHeaderEnd(final byte[] buf, int rlen) {
int splitbyte = 0;
@@ -1361,7 +1471,8 @@ public abstract class NanoHTTPD {
}
/**
- * Retrieves the content of a sent file and saves it to a temporary file. The full path to the saved file is returned.
+ * Retrieves the content of a sent file and saves it to a temporary
+ * file. The full path to the saved file is returned.
*/
private String saveTmpFile(ByteBuffer b, int offset, int len) {
String path = "";
@@ -1389,12 +1500,13 @@ public abstract class NanoHTTPD {
TempFile tempFile = tempFileManager.createTempFile();
return new RandomAccessFile(tempFile.getName(), "rw");
} catch (Exception e) {
- throw new Error(e); // we won't recover, so throw an error
+ throw new Error(e); // we won't recover, so throw an error
}
}
/**
- * It returns the offset separating multipart file headers from the file's data.
+ * It returns the offset separating multipart file headers from the
+ * file's data.
*/
private int stripMultipartHeaders(ByteBuffer b, int offset) {
int i;
@@ -1407,8 +1519,10 @@ public abstract class NanoHTTPD {
}
/**
- * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
- * adds them to given Map. NOTE: this doesn't support multiple identical keys due to the simplicity of Map.
+ * Decodes parameters in percent-encoded URI-format ( e.g.
+ * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given
+ * Map. NOTE: this doesn't support multiple identical keys due to the
+ * simplicity of Map.
*/
private void decodeParms(String parms, Map<String, String> p) {
if (parms == null) {
@@ -1422,8 +1536,7 @@ public abstract class NanoHTTPD {
String e = st.nextToken();
int sep = e.indexOf('=');
if (sep >= 0) {
- p.put(decodePercent(e.substring(0, sep)).trim(),
- decodePercent(e.substring(sep + 1)));
+ p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1)));
} else {
p.put(decodePercent(e).trim(), "");
}
@@ -1435,7 +1548,7 @@ public abstract class NanoHTTPD {
return parms;
}
- public String getQueryParameterString() {
+ public String getQueryParameterString() {
return queryParameterString;
}
@@ -1466,6 +1579,7 @@ public abstract class NanoHTTPD {
}
public static class Cookie {
+
private String n, v, e;
public Cookie(String name, String value, String expires) {
@@ -1499,14 +1613,16 @@ public abstract class NanoHTTPD {
}
/**
- * Provides rudimentary support for cookies.
- * Doesn't support 'path', 'secure' nor 'httpOnly'.
- * Feel free to improve it and/or add unsupported features.
- *
+ * Provides rudimentary support for cookies. Doesn't support 'path',
+ * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported
+ * features.
+ *
* @author LordFokas
*/
public class CookieHandler implements Iterable<String> {
+
private HashMap<String, String> cookies = new HashMap<String, String>();
+
private ArrayList<Cookie> queue = new ArrayList<Cookie>();
public CookieHandler(Map<String, String> httpHeaders) {
@@ -1522,14 +1638,16 @@ public abstract class NanoHTTPD {
}
}
- @Override public Iterator<String> iterator() {
+ @Override
+ public Iterator<String> iterator() {
return cookies.keySet().iterator();
}
/**
* Read a cookie from the HTTP Headers.
- *
- * @param name The cookie's name.
+ *
+ * @param name
+ * The cookie's name.
* @return The cookie's value if it exists, null otherwise.
*/
public String read(String name) {
@@ -1538,10 +1656,13 @@ public abstract class NanoHTTPD {
/**
* Sets a cookie.
- *
- * @param name The cookie's name.
- * @param value The cookie's value.
- * @param expires How many days until the cookie expires.
+ *
+ * @param name
+ * The cookie's name.
+ * @param value
+ * The cookie's value.
+ * @param expires
+ * How many days until the cookie expires.
*/
public void set(String name, String value, int expires) {
queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
@@ -1552,18 +1673,23 @@ public abstract class NanoHTTPD {
}
/**
- * Set a cookie with an expiration date from a month ago, effectively deleting it on the client side.
- *
- * @param name The cookie name.
+ * Set a cookie with an expiration date from a month ago, effectively
+ * deleting it on the client side.
+ *
+ * @param name
+ * The cookie name.
*/
public void delete(String name) {
set(name, "-delete-", -30);
}
/**
- * Internally used by the webserver to add all queued cookies into the Response's HTTP Headers.
- *
- * @param response The Response object to which headers the queued cookies will be added.
+ * Internally used by the webserver to add all queued cookies into the
+ * Response's HTTP Headers.
+ *
+ * @param response
+ * The Response object to which headers the queued cookies
+ * will be added.
*/
public void unloadQueue(Response response) {
for (Cookie cookie : queue) {