NanoHTTPD.java revision 269baff79590de5abd70e8e05bf46547e4a28ee6
1269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkepackage fi.iki.elonen; 2269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 3269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.BufferedReader; 4269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.ByteArrayInputStream; 5269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.ByteArrayOutputStream; 6269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.File; 7269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.FileInputStream; 8269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.FileOutputStream; 9269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.IOException; 10269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.InputStream; 11269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.InputStreamReader; 12269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.OutputStream; 13269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.PrintStream; 14269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.io.PrintWriter; 15269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.ServerSocket; 16269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.Socket; 17269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.URLEncoder; 18269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.ArrayList; 19269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.Date; 20269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.HashMap; 21269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.Iterator; 22269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.List; 23269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.Locale; 24269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.Map; 25269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.Properties; 26269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.StringTokenizer; 27269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.util.TimeZone; 28269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 29269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 30269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * A simple, tiny, nicely embeddable HTTP 1.0 (partially 1.1) server in Java 31269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 32269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <p> 33269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * NanoHTTPD version 1.25, Copyright © 2001,2005-2012 Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/) and Copyright © 2010 34269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Konstantinos Togias (info@ktogias.gr, http://ktogias.gr) 35269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 36269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <p> 37269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 38269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 39269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 40269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 41269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Java 1.1 compatible</li> 42269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 43269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li> 44269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li> 45269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 46269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 47269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 48269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 49269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 50269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 51269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 52269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 53269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 54269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 55269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 56269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 57269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 58269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Contains a built-in list of most common mime types</li> 59269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>All header names are converted lowercase so they don't vary between browsers/clients</li> 60269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 61269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 62269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 63269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <p> 64269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Ways to use: </b> 65269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 66269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 67269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Run as a standalone app, serves files and shows requests</li> 68269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Subclass serve() and embed to your own program</li> 69269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Call serveFile() from serve() with your own base directory</li> 70269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 71269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 72269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 73269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * See the end of the source file for distribution license (Modified BSD licence) 74269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 75269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkepublic class NanoHTTPD { 76269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // ================================================== 77269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // API parts 78269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // ================================================== 79269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 80269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 81269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Override this to customize the server. 82269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <p> 83269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 84269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * (By default, this delegates to serveFile() and allows directory listing.) 85269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * 86269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * @param uri 87269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Percent-decoded URI without parameters, for example "/index.cgi" 88269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * @param method 89269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * "GET", "POST" etc. 90269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * @param parms 91269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Parsed, percent decoded parameters from URI and, in case of POST, data. 92269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * @param header 93269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Header entries, percent decoded 94269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * @return HTTP response, see class Response for details 95269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 96269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public Response serve(String uri, String method, Map<String, String> header, Map<String, String> parms, Map<String, String> files) { 97269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println(method + " '" + uri + "' "); 98269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 99269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Iterator<String> e = header.keySet().iterator(); 100269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (e.hasNext()) { 101269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = e.next(); 102269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println(" HDR: '" + value + "' = '" + header.get(value) + "'"); 103269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 104269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke e = parms.keySet().iterator(); 105269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (e.hasNext()) { 106269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = e.next(); 107269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println(" PRM: '" + value + "' = '" + parms.get(value) + "'"); 108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke e = files.keySet().iterator(); 110269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (e.hasNext()) { 111269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = e.next(); 112269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println(" UPLOADED: '" + value + "' = '" + files.get(value) + "'"); 113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 114269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 115269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return serveFile(uri, header, myRootDir, true); 116269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 117269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 118269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 119269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * HTTP response. Return one of these from serve(). 120269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 121269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public class Response { 122269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 123269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Default constructor: response = HTTP_OK, data = mime = 'null' 124269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 125269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public Response() { 126269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.status = HTTP_OK; 127269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 128269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 129269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 130269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Basic constructor. 131269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 132269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public Response(String status, String mimeType, InputStream data) { 133269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.status = status; 134269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.mimeType = mimeType; 135269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.data = data; 136269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 137269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 138269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 139269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Convenience method that makes an InputStream out of given text. 140269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 141269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public Response(String status, String mimeType, String txt) { 142269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.status = status; 143269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.mimeType = mimeType; 144269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 145269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.data = new ByteArrayInputStream(txt.getBytes("UTF-8")); 146269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (java.io.UnsupportedEncodingException uee) { 147269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uee.printStackTrace(); 148269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 149269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 150269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 151269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 152269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Adds given line to the header. 153269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 154269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public void addHeader(String name, String value) { 155269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke header.put(name, value); 156269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 157269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 158269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 159269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * HTTP status code after processing, e.g. "200 OK", HTTP_OK 160269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 161269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public String status; 162269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 163269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 164269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * MIME type of content, e.g. "text/html" 165269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 166269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public String mimeType; 167269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 168269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 169269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Data of the response, may be null. 170269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 171269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public InputStream data; 172269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 173269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 174269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Headers for the HTTP response. Use addHeader() to add lines. 175269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 176269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public Map<String, String> header = new HashMap<String, String>(); 177269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 178269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 179269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 180269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Some HTTP response status codes 181269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 182269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_OK = "200 OK"; 183269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_PARTIALCONTENT = "206 Partial Content"; 184269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_RANGE_NOT_SATISFIABLE = "416 Requested Range Not Satisfiable"; 185269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_REDIRECT = "301 Moved Permanently"; 186269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_NOTMODIFIED = "304 Not Modified"; 187269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_FORBIDDEN = "403 Forbidden"; 188269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_NOTFOUND = "404 Not Found"; 189269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_BADREQUEST = "400 Bad Request"; 190269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_INTERNALERROR = "500 Internal Server Error"; 191269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String HTTP_NOTIMPLEMENTED = "501 Not Implemented"; 192269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 193269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 194269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Common mime types for dynamic content 195269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 196269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_PLAINTEXT = "text/plain"; 197269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_HTML = "text/html"; 198269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_DEFAULT_BINARY = "application/octet-stream"; 199269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_XML = "text/xml"; 200269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 201269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // ================================================== 202269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Socket & server code 203269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // ================================================== 204269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 205269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 206269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Starts a HTTP server to given port. 207269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <p> 208269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Throws an IOException if the socket is already in use 209269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 210269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public NanoHTTPD(int port, File wwwroot) throws IOException { 211269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myTcpPort = port; 212269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke this.myRootDir = wwwroot; 213269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myServerSocket = new ServerSocket(myTcpPort); 214269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread = new Thread(new Runnable() { 215269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke @Override 216269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public void run() { 217269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 218269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (true) { 219269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke new HTTPSession(myServerSocket.accept()); 220269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 221269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 222269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 223269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 224269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke }); 225269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread.setDaemon(true); 226269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread.start(); 227269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 228269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 229269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 230269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Stops the server. 231269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 232269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public void stop() { 233269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 234269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myServerSocket.close(); 235269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread.join(); 236269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 237269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (InterruptedException e) { 238269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 239269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 240269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 241269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 242269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Starts as a standalone file server and waits for Enter. 243269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 244269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static void main(String[] args) { 245269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println("NanoHTTPD 1.25 (C) 2001,2005-2011 Jarno Elonen and (C) 2010 Konstantinos Togias\n" 246269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "(Command line options: [-p port] [-d root-dir] [--licence])\n"); 247269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 248269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Defaults 249269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int port = 80; 250269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke File wwwroot = new File(".").getAbsoluteFile(); 251269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 252269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Show licence if requested 253269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < args.length; ++i) 254269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (args[i].equalsIgnoreCase("-p")) 255269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke port = Integer.parseInt(args[i + 1]); 256269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (args[i].equalsIgnoreCase("-d")) 257269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke wwwroot = new File(args[i + 1]).getAbsoluteFile(); 258269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (args[i].toLowerCase().endsWith("licence")) { 259269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println(LICENCE + "\n"); 260269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 261269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 262269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 263269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 264269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke new NanoHTTPD(port, wwwroot); 265269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 266269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myErr.println("Couldn't start server:\n" + ioe); 267269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke System.exit(-1); 268269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 269269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 270269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println("Now serving files in port " + port + " from \"" + wwwroot + "\""); 271269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myOut.println("Hit Enter to stop.\n"); 272269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 273269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 274269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke System.in.read(); 275269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Throwable t) { 276269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 277269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 278269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 279269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 280269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Handles one session, i.e. parses the HTTP request and returns the response. 281269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 282269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private class HTTPSession implements Runnable { 283269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public HTTPSession(Socket s) { 284269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mySocket = s; 285269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Thread t = new Thread(this); 286269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke t.setDaemon(true); 287269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke t.start(); 288269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 289269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 290269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke @Override 291269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public void run() { 292269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 293269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke InputStream is = mySocket.getInputStream(); 294269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (is == null) 295269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return; 296269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 297269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Read the first 8192 bytes. 298269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // The full header should fit in here. 299269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Apache's default header limit is 8KB. 300269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Do NOT assume that a single read will get the entire header at once! 301269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke final int bufsize = 8192; 302269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke byte[] buf = new byte[bufsize]; 303269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int splitbyte = 0; 304269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int rlen = 0; 305269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke { 306269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = is.read(buf, 0, bufsize); 307269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (read > 0) { 308269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke rlen += read; 309269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke splitbyte = findHeaderEnd(buf, rlen); 310269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (splitbyte > 0) 311269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 312269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke read = is.read(buf, rlen, bufsize - rlen); 313269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 314269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 315269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 316269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Create a BufferedReader for parsing the header. 317269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke ByteArrayInputStream hbis = new ByteArrayInputStream(buf, 0, rlen); 318269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke BufferedReader hin = new BufferedReader(new InputStreamReader(hbis)); 319269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Properties pre = new Properties(); 320269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> parms = new HashMap<String, String>(); 321269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> header = new HashMap<String, String>(); 322269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> files = new HashMap<String, String>(); 323269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 324269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Decode the header into parms and header java properties 325269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeHeader(hin, pre, parms, header); 326269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String method = pre.getProperty("method"); 327269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String uri = pre.getProperty("uri"); 328269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 329269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke long size = 0x7FFFFFFFFFFFFFFFl; 330269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentLength = header.get("content-length"); 331269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (contentLength != null) { 332269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 333269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke size = Integer.parseInt(contentLength); 334269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (NumberFormatException ex) { 335269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 336269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 337269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 338269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Write the part of body already read to ByteArrayOutputStream f 339269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke ByteArrayOutputStream f = new ByteArrayOutputStream(); 340269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (splitbyte < rlen) 341269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke f.write(buf, splitbyte, rlen - splitbyte); 342269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 343269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // While Firefox sends on the first read all the data fitting 344269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // our buffer, Chrome and Opera send only the headers even if 345269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // there is data for the body. We do some magic here to find 346269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // out whether we have already consumed part of body, if we 347269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // have reached the end of the data to be sent or we should 348269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // expect the first byte of the body at the next read. 349269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (splitbyte < rlen) 350269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke size -= rlen - splitbyte + 1; 351269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (splitbyte == 0 || size == 0x7FFFFFFFFFFFFFFFl) 352269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke size = 0; 353269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 354269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Now read all the body and write it to f 355269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke buf = new byte[512]; 356269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (rlen >= 0 && size > 0) { 357269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke rlen = is.read(buf, 0, 512); 358269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke size -= rlen; 359269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (rlen > 0) 360269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke f.write(buf, 0, rlen); 361269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 362269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 363269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Get the raw body as a byte [] 364269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke byte[] fbuf = f.toByteArray(); 365269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 366269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Create a BufferedReader for easily reading it as string. 367269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke ByteArrayInputStream bin = new ByteArrayInputStream(fbuf); 368269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke BufferedReader in = new BufferedReader(new InputStreamReader(bin)); 369269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 370269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 371269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 372269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (method.equalsIgnoreCase("POST")) { 373269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 374269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentTypeHeader = header.get("content-type"); 375269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(contentTypeHeader, "; "); 376269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (st.hasMoreTokens()) { 377269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke contentType = st.nextToken(); 378269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 379269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 380269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (contentType.equalsIgnoreCase("multipart/form-data")) { 381269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 382269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (!st.hasMoreTokens()) 383269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, 384269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 385269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String boundaryExp = st.nextToken(); 386269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke st = new StringTokenizer(boundaryExp, "="); 387269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (st.countTokens() != 2) 388269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, 389269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke "BAD REQUEST: Content type is multipart/form-data but boundary syntax error. Usage: GET /example/file.html"); 390269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke st.nextToken(); 391269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String boundary = st.nextToken(); 392269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 393269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeMultipartData(boundary, fbuf, in, parms, files); 394269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 395269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle application/x-www-form-urlencoded 396269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String postLine = ""; 397269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke char pbuf[] = new char[512]; 398269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = in.read(pbuf); 399269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (read >= 0 && !postLine.endsWith("\r\n")) { 400269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke postLine += String.valueOf(pbuf, 0, read); 401269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke read = in.read(pbuf); 402269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 403269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke postLine = postLine.trim(); 404269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeParms(postLine, parms); 405269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 406269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 407269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 408269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (method.equalsIgnoreCase("PUT")) 409269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke files.put("content", saveTmpFile(fbuf, 0, f.size())); 410269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 411269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Ok, now do the serve() 412269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Response r = serve(uri, method, header, parms, files); 413269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (r == null) 414269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 415269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else 416269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendResponse(r.status, r.mimeType, r.header, r.data); 417269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 418269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke in.close(); 419269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke is.close(); 420269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 421269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 422269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 423269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Throwable t) { 424269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 425269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (InterruptedException ie) { 426269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Thrown by sendError, ignore and exit the thread. 427269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 428269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 429269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 430269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 431269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes the sent headers and loads the data into java Properties' key - value pairs 432269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke **/ 433269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void decodeHeader(BufferedReader in, Properties pre, Map<String, String> parms, Map<String, String> header) 434269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke throws InterruptedException { 435269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 436269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Read the request line 437269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String inLine = in.readLine(); 438269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (inLine == null) 439269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return; 440269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(inLine); 441269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (!st.hasMoreTokens()) 442269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 443269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 444269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String method = st.nextToken(); 445269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pre.put("method", method); 446269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 447269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (!st.hasMoreTokens()) 448269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 449269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 450269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String uri = st.nextToken(); 451269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 452269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Decode parameters from the URI 453269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int qmi = uri.indexOf('?'); 454269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (qmi >= 0) { 455269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeParms(uri.substring(qmi + 1), parms); 456269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri = decodePercent(uri.substring(0, qmi)); 457269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else 458269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri = decodePercent(uri); 459269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 460269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If there's another token, it's protocol version, 461269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // followed by HTTP headers. Ignore version but parse headers. 462269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // NOTE: this now forces header names lowercase since they are 463269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // case insensitive and vary by client. 464269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (st.hasMoreTokens()) { 465269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String line = in.readLine(); 466269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (line != null && line.trim().length() > 0) { 467269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int p = line.indexOf(':'); 468269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (p >= 0) 469269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim()); 470269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke line = in.readLine(); 471269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 472269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 473269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 474269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pre.put("uri", uri); 475269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 476269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 477269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 478269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 479269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 480269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 481269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes the Multipart Body data and put it into java Properties' key - value pairs. 482269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke **/ 483269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void decodeMultipartData(String boundary, byte[] fbuf, BufferedReader in, Map<String, String> parms, 484269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> files) throws InterruptedException { 485269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 486269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes()); 487269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int boundarycount = 1; 488269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String mpline = in.readLine(); 489269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (mpline != null) { 490269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mpline.indexOf(boundary) == -1) 491269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, 492269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html"); 493269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke boundarycount++; 494269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> item = new HashMap<String, String>(); 495269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 496269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (mpline != null && mpline.trim().length() > 0) { 497269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int p = mpline.indexOf(':'); 498269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (p != -1) { 499269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke item.put(mpline.substring(0, p).trim().toLowerCase(), mpline.substring(p + 1).trim()); 500269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 501269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 502269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 503269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mpline != null) { 504269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentDisposition = item.get("content-disposition"); 505269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (contentDisposition == null) { 506269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, 507269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html"); 508269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 509269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(contentDisposition, "; "); 510269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> disposition = new HashMap<String, String>(); 511269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (st.hasMoreTokens()) { 512269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String token = st.nextToken(); 513269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int p = token.indexOf('='); 514269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (p != -1) { 515269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke disposition.put(token.substring(0, p).trim().toLowerCase(), token.substring(p + 1).trim()); 516269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 517269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 518269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String pname = disposition.get("name"); 519269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pname = pname.substring(1, pname.length() - 1); 520269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 521269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = ""; 522269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (item.get("content-type") == null) { 523269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (mpline != null && mpline.indexOf(boundary) == -1) { 524269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 525269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mpline != null) { 526269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int d = mpline.indexOf(boundary); 527269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (d == -1) { 528269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value += mpline; 529269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 530269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value += mpline.substring(0, d - 2); 531269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 532269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 533269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 534269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 535269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (boundarycount > bpositions.length) { 536269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_INTERNALERROR, "Error processing request"); 537269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 538269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]); 539269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4); 540269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke files.put(pname, path); 541269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value = disposition.get("filename"); 542269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value = value.substring(1, value.length() - 1); 543269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke do { 544269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 545269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } while (mpline != null && mpline.indexOf(boundary) == -1); 546269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 547269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke parms.put(pname, value); 548269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 549269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 550269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 551269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 552269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 553269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 554269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 555269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 556269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Find byte index separating header from body. It must be the last byte of the first two sequential new lines. 557269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke **/ 558269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private int findHeaderEnd(final byte[] buf, int rlen) { 559269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int splitbyte = 0; 560269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (splitbyte + 3 < rlen) { 561269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 562269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return splitbyte + 4; 563269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 564269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke splitbyte++; 565269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 566269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return 0; 567269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 568269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 569269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 570269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Find the byte positions where multipart boundaries start. 571269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke **/ 572269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public int[] getBoundaryPositions(byte[] b, byte[] boundary) { 573269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int matchcount = 0; 574269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int matchbyte = -1; 575269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke List<Integer> matchbytes = new ArrayList<Integer>(); 576269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < b.length; i++) { 577269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (b[i] == boundary[matchcount]) { 578269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (matchcount == 0) 579269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbyte = i; 580269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchcount++; 581269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (matchcount == boundary.length) { 582269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbytes.add(matchbyte); 583269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchcount = 0; 584269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbyte = -1; 585269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 586269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 587269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke i -= matchcount; 588269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchcount = 0; 589269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbyte = -1; 590269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 591269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 592269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int[] ret = new int[matchbytes.size()]; 593269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < ret.length; i++) { 594269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke ret[i] = (matchbytes.get(i)).intValue(); 595269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 596269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return ret; 597269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 598269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 599269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 600269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Retrieves the content of a sent file and saves it to a temporary file. The full path to the saved file is returned. 601269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke **/ 602269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private String saveTmpFile(byte[] b, int offset, int len) { 603269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String path = ""; 604269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (len > 0) { 605269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String tmpdir = System.getProperty("java.io.tmpdir"); 606269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 607269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke File temp = File.createTempFile("NanoHTTPD", "", new File(tmpdir)); 608269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke OutputStream fstream = new FileOutputStream(temp); 609269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke fstream.write(b, offset, len); 610269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke fstream.close(); 611269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke path = temp.getAbsolutePath(); 612269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Exception e) { // Catch exception if any 613269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myErr.println("Error: " + e.getMessage()); 614269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 615269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 616269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return path; 617269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 618269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 619269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 620269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * It returns the offset separating multipart file headers from the file's data. 621269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 622269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private int stripMultipartHeaders(byte[] b, int offset) { 623269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int i; 624269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (i = offset; i < b.length; i++) { 625269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (b[i] == '\r' && b[++i] == '\n' && b[++i] == '\r' && b[++i] == '\n') { 626269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 627269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 628269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 629269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return i + 1; 630269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 631269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 632269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 633269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes the percent encoding scheme. <br/> 634269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * For example: "an+example%20string" -> "an example string" 635269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 636269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private String decodePercent(String str) throws InterruptedException { 637269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 638269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringBuffer sb = new StringBuffer(); 639269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < str.length(); i++) { 640269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke char c = str.charAt(i); 641269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke switch (c) { 642269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke case '+': 643269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sb.append(' '); 644269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 645269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke case '%': 646269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sb.append((char) Integer.parseInt(str.substring(i + 1, i + 3), 16)); 647269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke i += 2; 648269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 649269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke default: 650269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sb.append(c); 651269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 652269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 653269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 654269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return sb.toString(); 655269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Exception e) { 656269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendError(HTTP_BADREQUEST, "BAD REQUEST: Bad percent-encoding."); 657269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return null; 658269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 659269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 660269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 661269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 662269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 663269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Properties. NOTE: this doesn't support multiple identical keys due to the simplicity of Properties -- if you need multiples, you 664269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * might want to replace the Properties with a Hashtable of Vectors or such. 665269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 666269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void decodeParms(String parms, Map<String, String> p) throws InterruptedException { 667269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (parms == null) 668269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return; 669269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 670269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(parms, "&"); 671269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (st.hasMoreTokens()) { 672269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String e = st.nextToken(); 673269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int sep = e.indexOf('='); 674269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (sep >= 0) { 675269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); 676269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 677269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 678269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 679269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 680269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 681269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Returns an error message as a HTTP response and throws InterruptedException to stop further request processing. 682269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 683269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void sendError(String status, String msg) throws InterruptedException { 684269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendResponse(status, MIME_PLAINTEXT, null, new ByteArrayInputStream(msg.getBytes())); 685269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke throw new InterruptedException(); 686269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 687269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 688269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 689269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Sends given response to the socket. 690269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 691269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void sendResponse(String status, String mime, Map<String, String> header, InputStream data) { 692269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 693269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (status == null) { 694269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke throw new Error("sendResponse(): Status can't be null."); 695269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 696269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke OutputStream out = mySocket.getOutputStream(); 697269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke PrintWriter pw = new PrintWriter(out); 698269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("HTTP/1.0 " + status + " \r\n"); 699269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 700269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mime != null) { 701269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("Content-Type: " + mime + "\r\n"); 702269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 703269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 704269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (header == null || header.get("Date") == null) { 705269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 706269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 707269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 708269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (header != null) { 709269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Iterator<String> e = header.keySet().iterator(); 710269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (e.hasNext()) { 711269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String key = e.next(); 712269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = header.get(key); 713269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print(key + ": " + value + "\r\n"); 714269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 715269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 716269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 717269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("\r\n"); 718269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.flush(); 719269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 720269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (data != null) { 721269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int pending = data.available(); // This is to support partial sends, see serveFile() 722269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke byte[] buff = new byte[BUFFER_SIZE]; 723269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (pending > 0) { 724269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending)); 725269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (read <= 0) { 726269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 727269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 728269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke out.write(buff, 0, read); 729269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pending -= read; 730269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 731269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 732269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke out.flush(); 733269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke out.close(); 734269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (data != null) 735269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke data.close(); 736269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 737269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Couldn't write? No can do. 738269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 739269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mySocket.close(); 740269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Throwable t) { 741269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 742269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 743269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 744269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 745269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private final Socket mySocket; 746269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 747269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 748269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 749269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * URL-encodes everything between "/"-characters. Encodes spaces as '%20' instead of '+'. 750269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 751269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private String encodeUri(String uri) { 752269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String newUri = ""; 753269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(uri, "/ ", true); 754269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (st.hasMoreTokens()) { 755269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String tok = st.nextToken(); 756269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (tok.equals("/")) 757269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke newUri += "/"; 758269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (tok.equals(" ")) 759269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke newUri += "%20"; 760269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else { 761269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke newUri += URLEncoder.encode(tok); 762269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // For Java 1.4 you'll want to use this instead: 763269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // try { newUri += URLEncoder.encode( tok, "UTF-8" ); } catch ( java.io.UnsupportedEncodingException uee ) {} 764269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 765269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 766269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return newUri; 767269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 768269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 769269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private final int myTcpPort; 770269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private final ServerSocket myServerSocket; 771269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private final Thread myThread; 772269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private final File myRootDir; 773269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 774269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // ================================================== 775269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // File server code 776269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // ================================================== 777269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 778269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 779269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Serves file from homeDir and its' subdirectories (only). Uses only URI, ignores all headers and HTTP parameters. 780269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 781269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public Response serveFile(String uri, Map<String, String> header, File homeDir, boolean allowDirectoryListing) { 782269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Response res = null; 783269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 784269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Make sure we won't die of an exception later 785269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (!homeDir.isDirectory()) 786269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_INTERNALERROR, MIME_PLAINTEXT, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); 787269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 788269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (res == null) { 789269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Remove URL arguments 790269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri = uri.trim().replace(File.separatorChar, '/'); 791269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (uri.indexOf('?') >= 0) 792269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri = uri.substring(0, uri.indexOf('?')); 793269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 794269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Prohibit getting out of current directory 795269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (uri.startsWith("..") || uri.endsWith("..") || uri.indexOf("../") >= 0) 796269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons."); 797269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 798269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 799269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke File f = new File(homeDir, uri); 800269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (res == null && !f.exists()) 801269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_NOTFOUND, MIME_PLAINTEXT, "Error 404, file not found."); 802269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 803269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // List the directory, if necessary 804269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (res == null && f.isDirectory()) { 805269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Browsers get confused without '/' after the 806269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // directory, send a redirect. 807269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (!uri.endsWith("/")) { 808269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri += "/"; 809269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_REDIRECT, MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri 810269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "</a></body></html>"); 811269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("Location", uri); 812269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 813269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 814269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (res == null) { 815269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // First try index.html and index.htm 816269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (new File(f, "index.html").exists()) 817269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke f = new File(homeDir, uri + "/index.html"); 818269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (new File(f, "index.htm").exists()) 819269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke f = new File(homeDir, uri + "/index.htm"); 820269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // No index file, list the directory if it is readable 821269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (allowDirectoryListing && f.canRead()) { 822269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String[] files = f.list(); 823269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String msg = "<html><body><h1>Directory " + uri + "</h1><br/>"; 824269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 825269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (uri.length() > 1) { 826269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String u = uri.substring(0, uri.length() - 1); 827269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int slash = u.lastIndexOf('/'); 828269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (slash >= 0 && slash < u.length()) 829269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += "<b><a href=\"" + uri.substring(0, slash + 1) + "\">..</a></b><br/>"; 830269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 831269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 832269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (files != null) { 833269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < files.length; ++i) { 834269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke File curFile = new File(f, files[i]); 835269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke boolean dir = curFile.isDirectory(); 836269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (dir) { 837269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += "<b>"; 838269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke files[i] += "/"; 839269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 840269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 841269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += "<a href=\"" + encodeUri(uri + files[i]) + "\">" + files[i] + "</a>"; 842269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 843269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Show file size 844269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (curFile.isFile()) { 845269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke long len = curFile.length(); 846269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += " <font size=2>("; 847269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (len < 1024) 848269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += len + " bytes"; 849269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else if (len < 1024 * 1024) 850269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += len / 1024 + "." + (len % 1024 / 10 % 100) + " KB"; 851269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else 852269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += len / (1024 * 1024) + "." + len % (1024 * 1024) / 10 % 100 + " MB"; 853269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 854269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += ")</font>"; 855269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 856269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += "<br/>"; 857269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (dir) 858269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += "</b>"; 859269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 860269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 861269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke msg += "</body></html>"; 862269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_OK, MIME_HTML, msg); 863269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 864269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: No directory listing."); 865269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 866269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 867269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 868269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 869269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 870269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (res == null) { 871269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Get MIME type from file name extension, if possible 872269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String mime = null; 873269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int dot = f.getCanonicalPath().lastIndexOf('.'); 874269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (dot >= 0) 875269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime = MIME_TYPES.get(f.getCanonicalPath().substring(dot + 1).toLowerCase()); 876269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mime == null) 877269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime = MIME_DEFAULT_BINARY; 878269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 879269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Calculate etag 880269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String etag = Integer.toHexString((f.getAbsolutePath() + f.lastModified() + "" + f.length()).hashCode()); 881269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 882269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Support (simple) skipping: 883269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke long startFrom = 0; 884269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke long endAt = -1; 885269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String range = header.get("range"); 886269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (range != null) { 887269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (range.startsWith("bytes=")) { 888269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke range = range.substring("bytes=".length()); 889269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int minus = range.indexOf('-'); 890269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 891269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (minus > 0) { 892269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke startFrom = Long.parseLong(range.substring(0, minus)); 893269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke endAt = Long.parseLong(range.substring(minus + 1)); 894269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 895269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (NumberFormatException nfe) { 896269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 897269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 898269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 899269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 900269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Change return code and add Content-Range header when skipping is requested 901269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke long fileLen = f.length(); 902269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (range != null && startFrom >= 0) { 903269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (startFrom >= fileLen) { 904269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, ""); 905269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("Content-Range", "bytes 0-0/" + fileLen); 906269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("ETag", etag); 907269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 908269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (endAt < 0) 909269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke endAt = fileLen - 1; 910269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke long newLen = endAt - startFrom + 1; 911269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (newLen < 0) 912269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke newLen = 0; 913269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 914269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke final long dataLen = newLen; 915269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke FileInputStream fis = new FileInputStream(f) { 916269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke @Override 917269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public int available() throws IOException { 918269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return (int) dataLen; 919269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 920269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke }; 921269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke fis.skip(startFrom); 922269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 923269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_PARTIALCONTENT, mime, fis); 924269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("Content-Length", "" + dataLen); 925269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen); 926269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("ETag", etag); 927269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 928269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 929269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (etag.equals(header.get("if-none-match"))) 930269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_NOTMODIFIED, mime, ""); 931269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke else { 932269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_OK, mime, new FileInputStream(f)); 933269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("Content-Length", "" + fileLen); 934269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("ETag", etag); 935269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 936269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 937269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 938269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 939269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res = new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); 940269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 941269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 942269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke res.addHeader("Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requestes 943269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return res; 944269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 945269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 946269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 947269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE 948269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 949269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private static final Map<String, String> MIME_TYPES; 950269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke static { 951269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> mime = new HashMap<String, String>(); 952269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("css", "text/css"); 953269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("htm", "text/html"); 954269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("html", "text/html"); 955269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("xml", "text/xml"); 956269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("txt", "text/plain"); 957269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("asc", "text/plain"); 958269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("gif", "image/gif"); 959269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("jpg", "image/jpeg"); 960269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("jpeg", "image/jpeg"); 961269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("png", "image/png"); 962269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("mp3", "audio/mpeg"); 963269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("m3u", "audio/mpeg-url"); 964269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("mp4", "video/mp4"); 965269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("ogv", "video/ogg"); 966269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("flv", "video/x-flv"); 967269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("mov", "video/quicktime"); 968269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("swf", "application/x-shockwave-flash"); 969269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("js", "application/javascript"); 970269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("pdf", "application/pdf"); 971269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("doc", "application/msword"); 972269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("ogg", "application/x-ogg"); 973269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("zip", "application/octet-stream"); 974269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("exe", "application/octet-stream"); 975269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mime.put("class", "application/octet-stream"); 976269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke MIME_TYPES = mime; 977269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 978269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 979269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private static int BUFFER_SIZE = 16 * 1024; 980269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 981269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Change these if you want to log to somewhere else than stdout 982269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke protected static PrintStream myOut = System.out; 983269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke protected static PrintStream myErr = System.err; 984269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 985269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 986269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * GMT date formatter 987269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 988269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private static java.text.SimpleDateFormat gmtFrmt; 989269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke static { 990269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke gmtFrmt = new java.text.SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 991269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 992269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 993269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 994269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 995269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * The distribution licence 996269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 997269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private static final String LICENCE = "Copyright (C) 2001,2005-2011 by Jarno Elonen <elonen@iki.fi>\n" 998269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "and Copyright (C) 2010 by Konstantinos Togias <info@ktogias.gr>\n" + "\n" 999269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "Redistribution and use in source and binary forms, with or without\n" 1000269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "modification, are permitted provided that the following conditions\n" + "are met:\n" + "\n" 1001269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "Redistributions of source code must retain the above copyright notice,\n" 1002269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "this list of conditions and the following disclaimer. Redistributions in\n" 1003269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "binary form must reproduce the above copyright notice, this list of\n" 1004269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "conditions and the following disclaimer in the documentation and/or other\n" 1005269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "materials provided with the distribution. The name of the author may not\n" 1006269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "be used to endorse or promote products derived from this software without\n" + "specific prior written permission. \n" 1007269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + " \n" + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" 1008269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" 1009269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" 1010269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" 1011269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n" 1012269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" 1013269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" 1014269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" 1015269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" 1016269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; 1017269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 1018