|
@@ -1,6 +1,20 @@
|
|
package me.yoqi.android.nanohttpdemo.net;
|
|
package me.yoqi.android.nanohttpdemo.net;
|
|
|
|
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.FilenameFilter;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.io.InputStream;
|
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
|
+import java.net.URLEncoder;
|
|
|
|
+import java.util.Arrays;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.StringTokenizer;
|
|
|
|
+
|
|
import fi.iki.elonen.NanoHTTPD;
|
|
import fi.iki.elonen.NanoHTTPD;
|
|
|
|
+import me.yoqi.android.nanohttpdemo.App;
|
|
|
|
|
|
/**
|
|
/**
|
|
* @author liuyuqi.gov@msn.cn
|
|
* @author liuyuqi.gov@msn.cn
|
|
@@ -8,24 +22,199 @@ import fi.iki.elonen.NanoHTTPD;
|
|
*/
|
|
*/
|
|
public class LocalHttpService extends NanoHTTPD {
|
|
public class LocalHttpService extends NanoHTTPD {
|
|
|
|
|
|
|
|
+ private final File rootDir;
|
|
|
|
+ static Object waitMonitor = new Object();
|
|
|
|
+
|
|
|
|
+ private static final Map<String, String> MIME_TYPES = new HashMap<String, String>() {{
|
|
|
|
+ put("css", "text/css");
|
|
|
|
+ put("htm", "text/html");
|
|
|
|
+ put("html", "text/html");
|
|
|
|
+ put("xml", "text/xml");
|
|
|
|
+ put("java", "text/x-java-source, text/java");
|
|
|
|
+ put("txt", "text/plain");
|
|
|
|
+ put("asc", "text/plain");
|
|
|
|
+ put("gif", "image/gif");
|
|
|
|
+ put("jpg", "image/jpeg");
|
|
|
|
+ put("jpeg", "image/jpeg");
|
|
|
|
+ put("png", "image/png");
|
|
|
|
+ put("mp3", "audio/mpeg");
|
|
|
|
+ put("m3u", "audio/mpeg-url");
|
|
|
|
+ put("mp4", "video/mp4");
|
|
|
|
+ put("ogv", "video/ogg");
|
|
|
|
+ put("flv", "video/x-flv");
|
|
|
|
+ put("mov", "video/quicktime");
|
|
|
|
+ put("swf", "application/x-shockwave-flash");
|
|
|
|
+ put("js", "application/javascript");
|
|
|
|
+ put("pdf", "application/pdf");
|
|
|
|
+ put("doc", "application/msword");
|
|
|
|
+ put("ogg", "application/x-ogg");
|
|
|
|
+ put("zip", "application/octet-stream");
|
|
|
|
+ put("exe", "application/octet-stream");
|
|
|
|
+ put("class", "application/octet-stream");
|
|
|
|
+ }};
|
|
|
|
+
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 覆写构造方法
|
|
* 覆写构造方法
|
|
*
|
|
*
|
|
- * @param port
|
|
|
|
|
|
+ * @param port 端口
|
|
*/
|
|
*/
|
|
- public LocalHttpService(int port) {
|
|
|
|
|
|
+ public LocalHttpService(int port, String host, File wwwroot) {
|
|
super(port);
|
|
super(port);
|
|
|
|
+ this.rootDir = wwwroot;
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Response serve(IHTTPSession session) {
|
|
public Response serve(IHTTPSession session) {
|
|
- StringBuilder builder = new StringBuilder();
|
|
|
|
- builder.append("<!DOCTYPE html><html><body>");
|
|
|
|
- builder.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />");
|
|
|
|
- builder.append("\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n" +
|
|
|
|
- " \t<meta content=\"width=device-width,initial-scale=1.0\" name=\"viewport\">");
|
|
|
|
- builder.append("服务已经启动啦,可以正常使用啦!");
|
|
|
|
- builder.append("</body></html>\n");
|
|
|
|
- return newFixedLengthResponse(builder.toString());
|
|
|
|
|
|
+ return serveFile(session.getUri(), session.getHeaders());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ File getRootDir() {
|
|
|
|
+ return rootDir;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * url 和文件路径映射
|
|
|
|
+ *
|
|
|
|
+ * @param uri url
|
|
|
|
+ * @param header 请求头
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ Response serveFile(String uri, Map<String, String> header) {
|
|
|
|
+ Response res = null;
|
|
|
|
+ // Remove URL arguments
|
|
|
|
+ uri = uri.trim().replace(File.separatorChar, '/');
|
|
|
|
+ if (uri.indexOf('?') >= 0)
|
|
|
|
+ uri = uri.substring(0, uri.indexOf('?'));
|
|
|
|
+
|
|
|
|
+ // 禁止访问非法目录
|
|
|
|
+ if (uri.startsWith("src/main") || uri.endsWith("src/main") || uri.contains("../"))
|
|
|
|
+ return newFixedLengthResponse(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons.");
|
|
|
|
+
|
|
|
|
+ if (!uri.endsWith("/")) {
|
|
|
|
+ uri += "/";
|
|
|
|
+ res = newFixedLengthResponse(Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri + "</a></body></html>");
|
|
|
|
+ res.addHeader("Location", uri);
|
|
|
|
+ }
|
|
|
|
+ if (uri.equals("/")) {
|
|
|
|
+ try {
|
|
|
|
+ InputStream inputStream = App.mContext.getAssets().open("index.html");
|
|
|
|
+ return newFixedLengthResponse(Response.Status.OK, NanoHTTPD.MIME_HTML, inputStream, 1);
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "error: " + e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ InputStream inputStream = App.mContext.getAssets().open(uri);
|
|
|
|
+ if (uri.contains("StopServer")) {
|
|
|
|
+ stop();
|
|
|
|
+ return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 999, Server stopping.");
|
|
|
|
+ }
|
|
|
|
+ String mime = null;
|
|
|
|
+ int dotIndex = uri.lastIndexOf(".");
|
|
|
|
+ if (dotIndex >= 0) {
|
|
|
|
+ mime = MIME_TYPES.get(uri.substring(dotIndex + 1).toLowerCase());
|
|
|
|
+ } else {
|
|
|
|
+ mime = NanoHTTPD.MIME_PLAINTEXT;
|
|
|
|
+ }
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found." + e);
|
|
|
|
+ }
|
|
|
|
+ return res;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 列目录功能
|
|
|
|
+ */
|
|
|
|
+ private String listDirectory(String uri, File f) {
|
|
|
|
+ System.out.println("SWS listDir uri=" + uri + ", f=" + f);
|
|
|
|
+ String heading = "Directory " + uri;
|
|
|
|
+ String msg = "<html><head><title>" + heading + "</title><style><!--\n" +
|
|
|
|
+ "span.dirname { font-weight: bold; }\n" +
|
|
|
|
+ "span.filesize { font-size: 75%; }\n" +
|
|
|
|
+ "// -->\n" +
|
|
|
|
+ "</style>" +
|
|
|
|
+ "</head><body><h1>" + heading + "</h1>";
|
|
|
|
+
|
|
|
|
+ String up = null;
|
|
|
|
+ if (uri.length() > 1) {
|
|
|
|
+ String u = uri.substring(0, uri.length() - 1);
|
|
|
|
+ int slash = u.lastIndexOf('/');
|
|
|
|
+ if (slash >= 0 && slash < u.length()) {
|
|
|
|
+ up = uri.substring(0, slash + 1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ List<String> files = Arrays.asList(f.list(new FilenameFilter() {
|
|
|
|
+ @Override
|
|
|
|
+ public boolean accept(File dir, String name) {
|
|
|
|
+ return new File(dir, name).isFile();
|
|
|
|
+ }
|
|
|
|
+ }));
|
|
|
|
+ Collections.sort(files);
|
|
|
|
+ List<String> directories = Arrays.asList(f.list(new FilenameFilter() {
|
|
|
|
+ @Override
|
|
|
|
+ public boolean accept(File dir, String name) {
|
|
|
|
+ return new File(dir, name).isDirectory();
|
|
|
|
+ }
|
|
|
|
+ }));
|
|
|
|
+ Collections.sort(directories);
|
|
|
|
+ if (up != null || directories.size() + files.size() > 0) {
|
|
|
|
+ msg += "<ul>";
|
|
|
|
+ if (up != null || directories.size() > 0) {
|
|
|
|
+ msg += "<section class=\"directories\">";
|
|
|
|
+ if (up != null) {
|
|
|
|
+ msg += "<li><a rel=\"directory\" href=\"" + up + "\"><span class=\"dirname\">..</span></a></b></li>";
|
|
|
|
+ }
|
|
|
|
+ for (int i = 0; i < directories.size(); i++) {
|
|
|
|
+ String dir = directories.get(i) + "/";
|
|
|
|
+ msg += "<li><a rel=\"directory\" href=\"" + encodeUri(uri + dir) + "\"><span class=\"dirname\">" + dir + "</span></a></b></li>";
|
|
|
|
+ }
|
|
|
|
+ msg += "</section>";
|
|
|
|
+ }
|
|
|
|
+ if (files.size() > 0) {
|
|
|
|
+ msg += "<section class=\"files\">";
|
|
|
|
+ for (int i = 0; i < files.size(); i++) {
|
|
|
|
+ String file = files.get(i);
|
|
|
|
+
|
|
|
|
+ msg += "<li><a href=\"" + encodeUri(uri + file) + "\"><span class=\"filename\">" + file + "</span></a>";
|
|
|
|
+ File curFile = new File(f, file);
|
|
|
|
+ long len = curFile.length();
|
|
|
|
+ msg += " <span class=\"filesize\">(";
|
|
|
|
+ if (len < 1024)
|
|
|
|
+ msg += len + " bytes";
|
|
|
|
+ else if (len < 1024 * 1024)
|
|
|
|
+ msg += len / 1024 + "." + (len % 1024 / 10 % 100) + " KB";
|
|
|
|
+ else
|
|
|
|
+ msg += len / (1024 * 1024) + "." + len % (1024 * 1024) / 10 % 100 + " MB";
|
|
|
|
+ msg += ")</span></li>";
|
|
|
|
+ }
|
|
|
|
+ msg += "</section>";
|
|
|
|
+ }
|
|
|
|
+ msg += "</ul>";
|
|
|
|
+ }
|
|
|
|
+ msg += "</body></html>";
|
|
|
|
+ return msg;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String encodeUri(String uri) {
|
|
|
|
+ String newUri = "";
|
|
|
|
+ StringTokenizer st = new StringTokenizer(uri, "/ ", true);
|
|
|
|
+ while (st.hasMoreTokens()) {
|
|
|
|
+ String tok = st.nextToken();
|
|
|
|
+ if (tok.equals("/"))
|
|
|
|
+ newUri += "/";
|
|
|
|
+ else if (tok.equals(" "))
|
|
|
|
+ newUri += "%20";
|
|
|
|
+ else {
|
|
|
|
+ try {
|
|
|
|
+ newUri += URLEncoder.encode(tok, "UTF-8");
|
|
|
|
+ } catch (UnsupportedEncodingException ignored) {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return newUri;
|
|
}
|
|
}
|
|
}
|
|
}
|