Update NanoHTTPd to 12b4973a52

This commit is contained in:
StevenLawson 2013-09-17 21:31:46 -04:00
parent 7b59350833
commit e10ab45bda
11 changed files with 539 additions and 325 deletions

View file

@ -1,5 +1,5 @@
#Tue, 17 Sep 2013 12:03:45 -0400
#Tue, 17 Sep 2013 21:28:11 -0400
program.VERSION=3.2
program.BUILDNUM=595
program.BUILDDATE=09/17/2013 12\:03 PM
program.BUILDNUM=596
program.BUILDDATE=09/17/2013 09\:28 PM

View file

@ -1,3 +1,3 @@
#Build Number for ANT. Do not edit!
#Tue Sep 17 12:03:45 EDT 2013
build.number=596
#Tue Sep 17 21:28:11 EDT 2013
build.number=597

View file

@ -2,7 +2,6 @@ package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
@ -16,9 +15,9 @@ public class Module_dump extends TFM_HTTPD_Module
private File echoFile = null;
private final String body;
public Module_dump(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public Module_dump(NanoHTTPD.HTTPSession session)
{
super(uri, method, headers, params, files, socket);
super(session);
//Body needs to be computed before getResponse, so we know if a text response or a file echo is needed.
this.body = body();
@ -54,6 +53,8 @@ public class Module_dump extends TFM_HTTPD_Module
String[] args = StringUtils.split(uri, "/");
Map<String, String> files = getFiles();
responseBody
.append(paragraph("URI: " + uri))
.append(paragraph("args (Length: " + args.length + "): " + StringUtils.join(args, ",")))

View file

@ -5,7 +5,6 @@ import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collections;
@ -13,9 +12,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import me.StevenLawson.TotalFreedomMod.TFM_ConfigEntry;
import static me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.*;
import me.StevenLawson.TotalFreedomMod.TFM_ConfigEntry;
/*
* This class was adapted from https://github.com/NanoHttpd/nanohttpd/blob/master/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java
@ -55,9 +54,9 @@ public class Module_file extends TFM_HTTPD_Module
MIME_TYPES.put("class", "application/octet-stream");
}
public Module_file(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public Module_file(NanoHTTPD.HTTPSession session)
{
super(uri, method, headers, params, files, socket);
super(session);
}
private File getRootDir()
@ -175,7 +174,7 @@ public class Module_file extends TFM_HTTPD_Module
}
if (mime == null)
{
mime = NanoHTTPD.MIME_DEFAULT_BINARY;
mime = TFM_HTTPD_Manager.MIME_DEFAULT_BINARY;
}
// Calculate etag

View file

@ -1,6 +1,5 @@
package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -21,9 +20,9 @@ import static org.apache.commons.lang3.StringEscapeUtils.*;
public class Module_help extends TFM_HTTPD_Module
{
public Module_help(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public Module_help(NanoHTTPD.HTTPSession session)
{
super(uri, method, headers, params, files, socket);
super(session);
}
@Override

View file

@ -1,7 +1,5 @@
package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.net.Socket;
import java.util.Map;
import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList;
import me.StevenLawson.TotalFreedomMod.TFM_Util;
import org.bukkit.Bukkit;
@ -9,9 +7,9 @@ import org.bukkit.entity.Player;
public class Module_list extends TFM_HTTPD_Module
{
public Module_list(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public Module_list(NanoHTTPD.HTTPSession session)
{
super(uri, method, headers, params, files, socket);
super(session);
}
@Override

View file

@ -1,15 +1,13 @@
package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.io.File;
import java.net.Socket;
import java.util.Map;
import me.StevenLawson.TotalFreedomMod.TotalFreedomMod;
public class Module_permbans extends TFM_HTTPD_Module
{
public Module_permbans(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public Module_permbans(NanoHTTPD.HTTPSession session)
{
super(uri, method, headers, params, files, socket);
super(session);
}
@Override

View file

@ -2,7 +2,6 @@ package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -16,8 +15,8 @@ import me.StevenLawson.TotalFreedomMod.TFM_Log;
import me.StevenLawson.TotalFreedomMod.TFM_Superadmin;
import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
public class Module_schematic extends TFM_HTTPD_Module
{
@ -36,9 +35,9 @@ public class Module_schematic extends TFM_HTTPD_Module
+ "<button type=\"submit\">Submit</button>\n"
+ "</form>";
public Module_schematic(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public Module_schematic(NanoHTTPD.HTTPSession session)
{
super(uri, method, headers, params, files, socket);
super(session);
}
@Override
@ -153,6 +152,8 @@ public class Module_schematic extends TFM_HTTPD_Module
private boolean uploadSchematic() throws SchematicTransferException
{
Map<String, String> files = getFiles();
final String tempFileName = files.get(REQUEST_FORM_FILE_ELEMENT_NAME);
if (tempFileName == null)
{

View file

@ -1,11 +1,7 @@
package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URLDecoder;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
@ -67,17 +63,21 @@ public abstract class NanoHTTPD
*/
public static final String MIME_HTML = "text/html";
/**
* Common mime type for dynamic content: binary
* Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
*/
public static final String MIME_DEFAULT_BINARY = "application/octet-stream";
private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
private final String hostname;
private final int myPort;
private ServerSocket myServerSocket;
private Thread myThread;
/**
* Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
* Pluggable strategy for asynchronously executing requests.
*/
private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
private AsyncRunner asyncRunner;
/**
* Pluggable strategy for creating and cleaning up temporary files.
*/
private TempFileManagerFactory tempFileManagerFactory;
/**
* Constructs an HTTP server on given port.
@ -98,8 +98,51 @@ public abstract class NanoHTTPD
setAsyncRunner(new DefaultAsyncRunner());
}
private static final void safeClose(ServerSocket serverSocket)
{
if (serverSocket != null)
{
try
{
serverSocket.close();
}
catch (IOException e)
{
}
}
}
private static final void safeClose(Socket socket)
{
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
}
}
}
private static final void safeClose(Closeable closeable)
{
if (closeable != null)
{
try
{
closeable.close();
}
catch (IOException e)
{
}
}
}
/**
* Start the server.
*
* @throws IOException if the socket is in use.
*/
public void start() throws IOException
@ -187,20 +230,39 @@ public abstract class NanoHTTPD
}
}
public final int getListeningPort()
{
return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
}
public final boolean wasStarted()
{
return myServerSocket != null && myThread != null;
}
public final boolean isAlive()
{
return wasStarted() && !myServerSocket.isClosed() && myThread.isAlive();
}
/**
* Override this to customize the server.
* <p/>
* <p/>
* (By default, this delegates to serveFile() and allows directory listing.)
*
* @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 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
*/
public abstract Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,
Map<String, String> files, Socket socket);
@Deprecated
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");
}
/**
* Override this to customize the server.
@ -211,33 +273,32 @@ public abstract class NanoHTTPD
* @param session The HTTP session
* @return HTTP response, see class Response for details
*/
protected Response serve(HTTPSession session)
public Response serve(HTTPSession session)
{
Map<String, String> files = new HashMap<String, String>();
try
{
session.parseBody(files);
}
catch (IOException ioe)
{
return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
}
catch (ResponseException re)
{
return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
}
String uri = session.getUri();
Method method = session.getMethod();
Map<String, String> parms = session.getParms();
Map<String, String> headers = session.getHeaders();
Socket socket = session.getSocket();
return serve(uri, method, headers, parms, files, socket);
if (Method.PUT.equals(method) || Method.POST.equals(method))
{
try
{
session.parseBody(files);
}
catch (IOException ioe)
{
return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
}
catch (ResponseException re)
{
return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
}
}
return serve(session.getUri(), method, session.getHeaders(), session.getParms(), files);
}
/**
* 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"
*/
@ -300,6 +361,36 @@ public abstract class NanoHTTPD
return parms;
}
// ------------------------------------------------------------------------------- //
//
// Threading Strategy.
//
// ------------------------------------------------------------------------------- //
/**
* Pluggable strategy for asynchronously executing requests.
*
* @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.
*/
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.
*/
@ -319,24 +410,6 @@ public abstract class NanoHTTPD
return null;
}
}
// ------------------------------------------------------------------------------- //
//
// Threading Strategy.
//
// ------------------------------------------------------------------------------- //
/**
* Pluggable strategy for asynchronously executing requests.
*/
private AsyncRunner asyncRunner;
/**
* Pluggable strategy for asynchronously executing requests.
* @param asyncRunner new strategy for handling threads.
*/
public void setAsyncRunner(AsyncRunner asyncRunner)
{
this.asyncRunner = asyncRunner;
}
/**
* Pluggable strategy for asynchronously executing requests.
@ -346,9 +419,46 @@ public abstract class NanoHTTPD
void exec(Runnable code);
}
/**
* 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>
*/
public interface TempFileManager
{
TempFile createTempFile() throws Exception;
void clear();
}
/**
* A temp file.
* <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;
String getName();
}
/**
* 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>
@ -367,76 +477,10 @@ public abstract class NanoHTTPD
t.start();
}
}
// ------------------------------------------------------------------------------- //
//
// Temp file handling strategy.
//
// ------------------------------------------------------------------------------- //
/**
* Pluggable strategy for creating and cleaning up temporary files.
*/
private TempFileManagerFactory tempFileManagerFactory;
/**
* Pluggable strategy for creating and cleaning up temporary files.
* @param tempFileManagerFactory new strategy for handling temp files.
*/
public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory)
{
this.tempFileManagerFactory = tempFileManagerFactory;
}
/**
* Factory to create temp file managers.
*/
public interface TempFileManagerFactory
{
TempFileManager create();
}
/**
* Temp file manager.
*
* <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();
}
/**
* A temp file.
*
* <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;
String getName();
}
/**
* Default strategy for creating and cleaning up temporary files.
*/
private class DefaultTempFileManagerFactory implements TempFileManagerFactory
{
@Override
public TempFileManager create()
{
return new DefaultTempFileManager();
}
}
/**
* Default strategy for creating and cleaning up temporary files.
*
* <p/>
* <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,
@ -481,7 +525,7 @@ public abstract class NanoHTTPD
/**
* Default strategy for creating and cleaning up temporary files.
*
* <p/>
* <p></p></[>By default, files are created by <code>File.createTempFile()</code> in
* the directory specified.</p>
*/
@ -516,7 +560,6 @@ public abstract class NanoHTTPD
}
}
// ------------------------------------------------------------------------------- //
/**
* HTTP response. Return one of these from serve().
*/
@ -542,6 +585,10 @@ public abstract class NanoHTTPD
* The request method that spawned this response.
*/
private Method requestMethod;
/**
* Use chunkedTransfer
*/
private boolean chunkedTransfer;
/**
* Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message
@ -623,31 +670,15 @@ public abstract class NanoHTTPD
}
}
int pending = data != null ? data.available() : -1; // This is to support partial sends, see serveFile()
if (pending > 0)
pw.print("Connection: keep-alive\r\n");
if (requestMethod != Method.HEAD && chunkedTransfer)
{
pw.print("Connection: keep-alive\r\n");
pw.print("Content-Length: " + pending + "\r\n");
sendAsChunked(outputStream, pw);
}
pw.print("\r\n");
pw.flush();
if (requestMethod != Method.HEAD && data != null)
else
{
int BUFFER_SIZE = 16 * 1024;
byte[] buff = new byte[BUFFER_SIZE];
while (pending > 0)
{
int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending));
if (read <= 0)
{
break;
}
outputStream.write(buff, 0, read);
pending -= read;
}
sendAsFixedLength(outputStream, pw);
}
outputStream.flush();
safeClose(data);
@ -658,6 +689,50 @@ public abstract class NanoHTTPD
}
}
private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException
{
pw.print("Transfer-Encoding: chunked\r\n");
pw.print("\r\n");
pw.flush();
int BUFFER_SIZE = 16 * 1024;
byte[] CRLF = "\r\n".getBytes();
byte[] buff = new byte[BUFFER_SIZE];
int read;
while ((read = data.read(buff)) > 0)
{
outputStream.write(String.format("%x\r\n", read).getBytes());
outputStream.write(buff, 0, read);
outputStream.write(CRLF);
}
outputStream.write(String.format("0\r\n\r\n").getBytes());
}
private void sendAsFixedLength(OutputStream outputStream, PrintWriter pw) throws IOException
{
int pending = data != null ? data.available() : 0; // This is to support partial sends, see serveFile()
pw.print("Content-Length: " + pending + "\r\n");
pw.print("\r\n");
pw.flush();
if (requestMethod != Method.HEAD && data != null)
{
int BUFFER_SIZE = 16 * 1024;
byte[] buff = new byte[BUFFER_SIZE];
while (pending > 0)
{
int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending));
if (read <= 0)
{
break;
}
outputStream.write(buff, 0, read);
pending -= read;
}
}
}
public Status getStatus()
{
return status;
@ -698,6 +773,11 @@ public abstract class NanoHTTPD
this.requestMethod = requestMethod;
}
public void setChunkedTransfer(boolean chunkedTransfer)
{
this.chunkedTransfer = chunkedTransfer;
}
/**
* Some HTTP response status codes
*/
@ -728,6 +808,40 @@ public abstract class NanoHTTPD
}
}
public static final class ResponseException extends Exception
{
private final Response.Status status;
public ResponseException(Response.Status status, String message)
{
super(message);
this.status = status;
}
public ResponseException(Response.Status status, String message, Exception e)
{
super(message, e);
this.status = status;
}
public Response.Status getStatus()
{
return status;
}
}
/**
* Default strategy for creating and cleaning up temporary files.
*/
private class DefaultTempFileManagerFactory implements TempFileManagerFactory
{
@Override
public TempFileManager create()
{
return new DefaultTempFileManager();
}
}
/**
* Handles one session, i.e. parses the HTTP request and returns the response.
*/
@ -735,17 +849,18 @@ public abstract class NanoHTTPD
{
public static final int BUFSIZE = 8192;
private final TempFileManager tempFileManager;
private InputStream inputStream;
private final OutputStream outputStream;
private final Socket socket;
private InputStream inputStream;
private int splitbyte;
private int rlen;
private String uri;
private Method method;
private Map<String, String> parms;
private Map<String, String> headers;
private Socket socket;
private CookieHandler cookies;
private HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, Socket socket)
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, Socket socket)
{
this.tempFileManager = tempFileManager;
this.inputStream = inputStream;
@ -808,6 +923,8 @@ public abstract class NanoHTTPD
uri = pre.get("uri");
cookies = new CookieHandler(headers);
// Ok, now do the serve()
Response r = serve(this);
if (r == null)
@ -816,6 +933,7 @@ public abstract class NanoHTTPD
}
else
{
cookies.unloadQueue(r);
r.setRequestMethod(method);
r.send(outputStream);
}
@ -843,7 +961,7 @@ public abstract class NanoHTTPD
}
}
private void parseBody(Map<String, String> files) throws IOException, ResponseException
protected void parseBody(Map<String, String> files) throws IOException, ResponseException
{
RandomAccessFile randomAccessFile = null;
BufferedReader in = null;
@ -914,7 +1032,7 @@ public abstract class NanoHTTPD
String boundaryStartString = "boundary=";
int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length();
String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length());
if (boundary.startsWith("\"") && boundary.startsWith("\""))
if (boundary.startsWith("\"") && boundary.endsWith("\""))
{
boundary = boundary.substring(1, boundary.length() - 1);
}
@ -1189,7 +1307,7 @@ public abstract class NanoHTTPD
path = tempFile.getName();
}
catch (Exception e)
{
{ // Catch exception if any
TFM_Log.severe(e);
}
finally
@ -1285,88 +1403,140 @@ public abstract class NanoHTTPD
return inputStream;
}
public final Socket getSocket()
public CookieHandler getCookies()
{
return cookies;
}
public Socket getSocket()
{
return socket;
}
}
private static final class ResponseException extends Exception
public static class Cookie
{
private final Response.Status status;
private String n, v, e;
public ResponseException(Response.Status status, String message)
public Cookie(String name, String value, String expires)
{
super(message);
this.status = status;
n = name;
v = value;
e = expires;
}
public ResponseException(Response.Status status, String message, Exception e)
public Cookie(String name, String value)
{
super(message, e);
this.status = status;
this(name, value, 30);
}
public Response.Status getStatus()
public Cookie(String name, String value, int numDays)
{
return status;
n = name;
v = value;
e = getHTTPTime(numDays);
}
public String getHTTPHeader()
{
String fmt = "%s=%s; expires=%s";
return String.format(fmt, n, v, e);
}
public static String getHTTPTime(int days)
{
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
calendar.add(Calendar.DAY_OF_MONTH, days);
return dateFormat.format(calendar.getTime());
}
}
private static final void safeClose(ServerSocket serverSocket)
/**
* 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>
{
if (serverSocket != null)
private HashMap<String, String> cookies = new HashMap<String, String>();
private ArrayList<Cookie> queue = new ArrayList<Cookie>();
public CookieHandler(Map<String, String> httpHeaders)
{
try
String raw = httpHeaders.get("cookie");
if (raw != null)
{
serverSocket.close();
String[] tokens = raw.split(";");
for (String token : tokens)
{
String[] data = token.trim().split("=");
if (data.length == 2)
{
cookies.put(data[0], data[1]);
}
}
}
catch (IOException e)
}
@Override
public Iterator<String> iterator()
{
return cookies.keySet().iterator();
}
/**
* Read a cookie from the HTTP Headers.
*
* @param name The cookie's name.
* @return The cookie's value if it exists, null otherwise.
*/
public String read(String name)
{
return cookies.get(name);
}
/**
* Sets a cookie.
*
* @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)));
}
public void set(Cookie cookie)
{
queue.add(cookie);
}
/**
* 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.
*/
public void unloadQueue(Response response)
{
for (Cookie cookie : queue)
{
response.addHeader("Set-Cookie", cookie.getHTTPHeader());
}
}
}
private static final void safeClose(Socket socket)
{
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
}
}
}
private static final void safeClose(Closeable closeable)
{
if (closeable != null)
{
try
{
closeable.close();
}
catch (IOException e)
{
}
}
}
public final int getListeningPort()
{
return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
}
public final boolean wasStarted()
{
return myServerSocket != null && myThread != null;
}
public final boolean isAlive()
{
return wasStarted() && !myServerSocket.isClosed() && myThread.isAlive();
}
}
}

View file

@ -3,12 +3,10 @@ package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.HTTPSession;
import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.Response;
import me.StevenLawson.TotalFreedomMod.TFM_ConfigEntry;
import me.StevenLawson.TotalFreedomMod.TFM_Log;
@ -19,6 +17,9 @@ import org.bukkit.Bukkit;
public class TFM_HTTPD_Manager
{
@Deprecated
public static String MIME_DEFAULT_BINARY = "application/octet-stream";
//
private static final Pattern EXT_REGEX = Pattern.compile("\\.([^\\.\\s]+)$");
//
public static final int PORT = TFM_ConfigEntry.HTTPD_PORT.getInteger();
@ -69,36 +70,118 @@ public class TFM_HTTPD_Manager
private static enum ModuleType
{
DUMP(false, "dump"),
HELP(true, "help"),
LIST(true, "list"),
FILE(false, "file"),
SCHEMATIC(false, "schematic"),
PERMBANS(false, "permbans");
private final boolean runOnBukkitThread;
private final String name;
private ModuleType(boolean runOnBukkitThread, String name)
DUMP(new ModuleExecutable(false, "dump")
{
this.runOnBukkitThread = runOnBukkitThread;
this.name = name;
@Override
public Response getResponse(HTTPSession session)
{
return new Response(Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "The DUMP module is disabled. It is intended for debugging use only.");
}
}),
HELP(new ModuleExecutable(true, "help")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_help(session).getResponse();
}
}),
LIST(new ModuleExecutable(true, "list")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_list(session).getResponse();
}
}),
FILE(new ModuleExecutable(false, "file")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_file(session).getResponse();
}
}),
SCHEMATIC(new ModuleExecutable(false, "schematic")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_schematic(session).getResponse();
}
}),
PERMBANS(new ModuleExecutable(false, "permbans")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_permbans(session).getResponse();
}
});
//
private final ModuleExecutable moduleExecutable;
private ModuleType(ModuleExecutable moduleExecutable)
{
this.moduleExecutable = moduleExecutable;
}
public boolean isRunOnBukkitThread()
private abstract static class ModuleExecutable
{
return runOnBukkitThread;
private final boolean runOnBukkitThread;
private final String name;
public ModuleExecutable(boolean runOnBukkitThread, String name)
{
this.runOnBukkitThread = runOnBukkitThread;
this.name = name;
}
public Response execute(final HTTPSession session)
{
try
{
if (this.runOnBukkitThread)
{
return Bukkit.getScheduler().callSyncMethod(TotalFreedomMod.plugin, new Callable<Response>()
{
@Override
public Response call() throws Exception
{
return getResponse(session);
}
}).get();
}
else
{
return getResponse(session);
}
}
catch (Exception ex)
{
TFM_Log.severe(ex);
}
return null;
}
public abstract Response getResponse(HTTPSession session);
public String getName()
{
return name;
}
}
public String getName()
public ModuleExecutable getModuleExecutable()
{
return name;
return moduleExecutable;
}
private static ModuleType getByName(String needle)
{
for (ModuleType type : values())
{
if (type.getName().equalsIgnoreCase(needle))
if (type.getModuleExecutable().getName().equalsIgnoreCase(needle))
{
return type;
}
@ -120,68 +203,15 @@ public class TFM_HTTPD_Manager
}
@Override
public Response serve(
final String uri,
final Method method,
final Map<String, String> headers,
final Map<String, String> params,
final Map<String, String> files,
final Socket socket)
public Response serve(HTTPSession session)
{
Response response = null;
Response response;
try
{
final String[] args = StringUtils.split(uri, "/");
final String[] args = StringUtils.split(session.getUri(), "/");
final ModuleType moduleType = args.length >= 1 ? ModuleType.getByName(args[0]) : ModuleType.FILE;
if (moduleType.isRunOnBukkitThread())
{
Future<Response> responseCall = Bukkit.getScheduler().callSyncMethod(TotalFreedomMod.plugin, new Callable<Response>()
{
@Override
public Response call() throws Exception
{
switch (moduleType)
{
case HELP:
return new Module_help(uri, method, headers, params, files, socket).getResponse();
case LIST:
return new Module_list(uri, method, headers, params, files, socket).getResponse();
default:
return null;
}
}
});
try
{
response = responseCall.get();
}
catch (Exception ex)
{
TFM_Log.severe(ex);
}
}
else
{
switch (moduleType)
{
case DUMP:
//response = new Module_dump(uri, method, headers, params, files, socket).getResponse();
response = new Response(Response.Status.OK, MIME_PLAINTEXT, "The DUMP module is disabled. It is intended for debugging use only.");
break;
case SCHEMATIC:
response = new Module_schematic(uri, method, headers, params, files, socket).getResponse();
break;
case PERMBANS:
response = new Module_permbans(uri, method, headers, params, files, socket).getResponse();
break;
default:
response = new Module_file(uri, method, headers, params, files, socket).getResponse();
}
}
response = moduleType.getModuleExecutable().execute(session);
}
catch (Exception ex)
{
@ -215,10 +245,10 @@ public class TFM_HTTPD_Manager
if (mimetype == null || mimetype.trim().isEmpty())
{
mimetype = NanoHTTPD.MIME_DEFAULT_BINARY;
mimetype = MIME_DEFAULT_BINARY;
}
response = new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, mimetype, new FileInputStream(file));
response = new Response(Response.Status.OK, mimetype, new FileInputStream(file));
response.addHeader("Content-Length", "" + file.length());
}
catch (IOException ex)

View file

@ -1,8 +1,10 @@
package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.*;
import me.StevenLawson.TotalFreedomMod.TFM_Log;
public abstract class TFM_HTTPD_Module
{
@ -10,17 +12,17 @@ public abstract class TFM_HTTPD_Module
protected final Method method;
protected final Map<String, String> headers;
protected final Map<String, String> params;
protected final Map<String, String> files;
protected final Socket socket;
protected final HTTPSession session;
public TFM_HTTPD_Module(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
public TFM_HTTPD_Module(HTTPSession session)
{
this.uri = uri;
this.method = method;
this.headers = headers;
this.params = params;
this.files = files;
this.socket = socket;
this.uri = session.getUri();
this.method = session.getMethod();
this.headers = session.getHeaders();
this.params = session.getParms();
this.socket = session.getSocket();
this.session = session;
}
public String getBody()
@ -47,4 +49,20 @@ public abstract class TFM_HTTPD_Module
{
return new TFM_HTTPD_PageBuilder(getBody(), getTitle(), getStyle(), getScript()).getResponse();
}
protected final Map<String, String> getFiles()
{
Map<String, String> files = new HashMap<String, String>();
try
{
session.parseBody(files);
}
catch (Exception ex)
{
TFM_Log.severe(ex);
}
return files;
}
}