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