package com.orange.atk.system; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import org.apache.log4j.Logger; import com.orange.atk.atkUI.corecli.Configuration; import com.orange.atk.platform.Platform; import fi.iki.elonen.NanoHTTPD; public class WebServer extends NanoHTTPD { private static final String TAG = NanoHTTPD.class.getName(); private static NanoHTTPD server = null; /** * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE */ private static final Map<String, String> MIME_TYPES; static { Map<String, String> mime = new HashMap<String, String>(); mime.put("css", "text/css"); mime.put("htm", "text/html"); mime.put("html", "text/html"); mime.put("xml", "text/xml"); mime.put("txt", "text/plain"); mime.put("asc", "text/plain"); mime.put("gif", "image/gif"); mime.put("jpg", "image/jpeg"); mime.put("jpeg", "image/jpeg"); mime.put("png", "image/png"); mime.put("mp3", "audio/mpeg"); mime.put("m3u", "audio/mpeg-url"); mime.put("mp4", "video/mp4"); mime.put("ogv", "video/ogg"); mime.put("flv", "video/x-flv"); mime.put("mov", "video/quicktime"); mime.put("swf", "application/x-shockwave-flash"); mime.put("js", "application/javascript"); mime.put("pdf", "application/pdf"); mime.put("doc", "application/msword"); mime.put("ogg", "application/x-ogg"); mime.put("zip", "application/octet-stream"); mime.put("exe", "application/octet-stream"); mime.put("class", "application/octet-stream"); MIME_TYPES = mime; } private File rootDir; public WebServer(String host, int port, File wwwroot) { super(host, port); this.rootDir = wwwroot; } public File getRootDir() { return rootDir; } /** * URL-encodes everything between "/"-characters. Encodes spaces as '%20' instead of '+'. */ 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; } /** * Serves file from homeDir and its' subdirectories (only). Uses only URI, ignores all headers and HTTP parameters. */ public Response serveFile(String uri, Map<String, String> header, File homeDir) { Response res = null; // Make sure we won't die of an exception later if (!homeDir.isDirectory()) res = new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); if (res == null) { // Remove URL arguments uri = uri.trim().replace(File.separatorChar, '/'); if (uri.indexOf('?') >= 0) uri = uri.substring(0, uri.indexOf('?')); // Prohibit getting out of current directory if (uri.startsWith("src/main") || uri.endsWith("src/main") || uri.contains("../")) res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons."); } File f = new File(homeDir, uri); if (res == null && !f.exists()) res = new Response(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found."); // List the directory, if necessary if (res == null && f.isDirectory()) { // Browsers get confused without '/' after the // directory, send a redirect. if (!uri.endsWith("/")) { uri += "/"; res = new Response(Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri + "</a></body></html>"); res.addHeader("Location", uri); } if (res == null) { // First try index.html and index.htm if (new File(f, "index.html").exists()) f = new File(homeDir, uri + "/index.html"); else if (new File(f, "index.htm").exists()) f = new File(homeDir, uri + "/index.htm"); // No index file, list the directory if it is readable else if (f.canRead()) { String[] files = f.list(); String msg = "<html><body><h1>Directory " + uri + "</h1><br/>"; if (uri.length() > 1) { String u = uri.substring(0, uri.length() - 1); int slash = u.lastIndexOf('/'); if (slash >= 0 && slash < u.length()) msg += "<b><a href=\"" + uri.substring(0, slash + 1) + "\">..</a></b><br/>"; } if (files != null) { for (int i = 0; i < files.length; ++i) { File curFile = new File(f, files[i]); boolean dir = curFile.isDirectory(); if (dir) { msg += "<b>"; files[i] += "/"; } msg += "<a href=\"" + encodeUri(uri + files[i]) + "\">" + files[i] + "</a>"; // Show file size if (curFile.isFile()) { long len = curFile.length(); msg += "  <font size=2>("; 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 += ")</font>"; } msg += "<br/>"; if (dir) msg += "</b>"; } } msg += "</body></html>"; res = new Response(msg); } else { res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: No directory listing."); } } } try { if (res == null) { // Get MIME type from file name extension, if possible String mime = null; int dot = f.getCanonicalPath().lastIndexOf('.'); if (dot >= 0) mime = MIME_TYPES.get(f.getCanonicalPath().substring(dot + 1).toLowerCase()); if (mime == null) mime = NanoHTTPD.MIME_DEFAULT_BINARY; // Calculate etag String etag = Integer.toHexString((f.getAbsolutePath() + f.lastModified() + "" + f.length()).hashCode()); // Support (simple) skipping: long startFrom = 0; long endAt = -1; String range = header.get("range"); if (range != null) { if (range.startsWith("bytes=")) { range = range.substring("bytes=".length()); int minus = range.indexOf('-'); try { if (minus > 0) { startFrom = Long.parseLong(range.substring(0, minus)); endAt = Long.parseLong(range.substring(minus + 1)); } } catch (NumberFormatException ignored) { } } } // Change return code and add Content-Range header when skipping is requested long fileLen = f.length(); if (range != null && startFrom >= 0) { if (startFrom >= fileLen) { res = new Response(Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, ""); res.addHeader("Content-Range", "bytes 0-0/" + fileLen); res.addHeader("ETag", etag); } else { if (endAt < 0) endAt = fileLen - 1; long newLen = endAt - startFrom + 1; if (newLen < 0) newLen = 0; final long dataLen = newLen; FileInputStream fis = new FileInputStream(f) { @Override public int available() throws IOException { return (int) dataLen; } }; fis.skip(startFrom); res = new Response(Response.Status.PARTIAL_CONTENT, mime, fis); res.addHeader("Content-Length", "" + dataLen); res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen); res.addHeader("ETag", etag); } } else { if (etag.equals(header.get("if-none-match"))) res = new Response(Response.Status.NOT_MODIFIED, mime, ""); else { res = new Response(Response.Status.OK, mime, new FileInputStream(f)); res.addHeader("Content-Length", "" + fileLen); res.addHeader("ETag", etag); } } } } catch (IOException ioe) { res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); } res.addHeader("Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requestes return res; } @Override public Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms, Map<String, String> files) { System.out.println(method + " '" + uri + "' "); Iterator<String> e = header.keySet().iterator(); while (e.hasNext()) { String value = e.next(); System.out.println(" HDR: '" + value + "' = '" + header.get(value) + "'"); } e = parms.keySet().iterator(); while (e.hasNext()) { String value = e.next(); System.out.println(" PRM: '" + value + "' = '" + parms.get(value) + "'"); } e = files.keySet().iterator(); while (e.hasNext()) { String value = e.next(); System.out.println(" UPLOADED: '" + value + "' = '" + files.get(value) + "'"); } return serveFile(uri, header, getRootDir()); } public static void run() { Logger.getLogger(TAG).info("NanoHTTPD 1.25 (C) 2001,2005-2011 Jarno Elonen and (C) 2010 Konstantinos Togias"); // Defaults int port = 8080; String host = "127.0.0.1"; File wwwroot = new File(Configuration.getProperty("outputDir")+Platform.FILE_SEPARATOR+"benchmark").getAbsoluteFile(); server = new WebServer(host, port, wwwroot); try { server.start(); Logger.getLogger(TAG).info("server running"); } catch (IOException e) { Logger.getLogger(TAG).info("server not running"); } } public static void kill() { server.stop(); } }