NanoHTTPD.java revision 6200253df7b934c807eb3436b51a3b039406457d
1269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkepackage fi.iki.elonen; 2269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 39788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawkeimport java.io.*; 4269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.ServerSocket; 5269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.Socket; 67b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat; 79788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawkeimport java.util.*; 8269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 10269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * A simple, tiny, nicely embeddable HTTP 1.0 (partially 1.1) server in Java 11d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 12d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 132b54eda5fee8430268a99d73b061e790362db544Tom Hermann * NanoHTTPD version 1.25, Copyright © 2001,2005-2012 Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/) and Copyright © 2010 142b54eda5fee8430268a99d73b061e790362db544Tom Hermann * Konstantinos Togias (info@ktogias.gr, http://ktogias.gr) 1501ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <p/> 1601ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * Uplifted to Java5 by Micah Hainline and Paul Hawke (paul.hawke@gmail.com). 17d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 18d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 19269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 20269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 21d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 22269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 2301ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li> 24269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 25269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li> 26269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li> 27269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 28269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 29269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 30269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 31269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 32269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 33269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 34269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 35269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 36269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 37269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 38269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 39269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 40269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Contains a built-in list of most common mime types</li> 41269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>All header names are converted lowercase so they don't vary between browsers/clients</li> 42d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 43269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 44d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 45d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 4601ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b> 47269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 48d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 4901ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li> 50d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 51269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 52d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 53269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * See the end of the source file for distribution license (Modified BSD licence) 54269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 55d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD { 56269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 57269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Common mime types for dynamic content 58269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 59269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_PLAINTEXT = "text/plain"; 60269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_HTML = "text/html"; 61269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public static final String MIME_DEFAULT_BINARY = "application/octet-stream"; 627b88819e82b89ac3476ce060903468076a73de8fPaul Hawke private final int myPort; 637b88819e82b89ac3476ce060903468076a73de8fPaul Hawke private ServerSocket myServerSocket; 647b88819e82b89ac3476ce060903468076a73de8fPaul Hawke private Thread myThread; 654e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private TempFileManagerFactory tempFileManagerFactory; 664e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private AsyncRunner asyncRunner; 67269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 68269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 69d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * Constructs an HTTP server on given port. 70269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 71df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke public NanoHTTPD(int port) { 727b88819e82b89ac3476ce060903468076a73de8fPaul Hawke this.myPort = port; 734e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke this.tempFileManagerFactory = new DefaultTempFileManagerFactory(); 744e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke this.asyncRunner = new DefaultAsyncRunner(); 753f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke } 763f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke 773f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke /** 783f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke * Starts the server 79d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 80d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * Throws an IOException if the socket is already in use 813f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke */ 827b88819e82b89ac3476ce060903468076a73de8fPaul Hawke public void start() throws IOException { 830cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke myServerSocket = new ServerSocket(myPort); 84269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread = new Thread(new Runnable() { 85269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke @Override 86269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public void run() { 876200253df7b934c807eb3436b51a3b039406457dPaul Hawke do { 886200253df7b934c807eb3436b51a3b039406457dPaul Hawke try { 896200253df7b934c807eb3436b51a3b039406457dPaul Hawke final Socket finalAccept = myServerSocket.accept(); 906200253df7b934c807eb3436b51a3b039406457dPaul Hawke InputStream inputStream = finalAccept.getInputStream(); 916200253df7b934c807eb3436b51a3b039406457dPaul Hawke OutputStream outputStream = finalAccept.getOutputStream(); 926200253df7b934c807eb3436b51a3b039406457dPaul Hawke TempFileManager tempFileManager = tempFileManagerFactory.create(); 936200253df7b934c807eb3436b51a3b039406457dPaul Hawke final HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream); 946200253df7b934c807eb3436b51a3b039406457dPaul Hawke asyncRunner.exec(new Runnable() { 956200253df7b934c807eb3436b51a3b039406457dPaul Hawke @Override 966200253df7b934c807eb3436b51a3b039406457dPaul Hawke public void run() { 976200253df7b934c807eb3436b51a3b039406457dPaul Hawke session.run(); 986200253df7b934c807eb3436b51a3b039406457dPaul Hawke if (finalAccept != null) { 996200253df7b934c807eb3436b51a3b039406457dPaul Hawke try { 1006200253df7b934c807eb3436b51a3b039406457dPaul Hawke finalAccept.close(); 1016200253df7b934c807eb3436b51a3b039406457dPaul Hawke } catch (IOException ignored) { 1026200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1036200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1046200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1056200253df7b934c807eb3436b51a3b039406457dPaul Hawke }); 1066200253df7b934c807eb3436b51a3b039406457dPaul Hawke } catch (IOException e) { 1076200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1086200253df7b934c807eb3436b51a3b039406457dPaul Hawke } while (true); 109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 110269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke }); 111269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread.setDaemon(true); 112269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread.start(); 113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 114d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 115269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 116269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Stops the server. 117269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 118269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public void stop() { 119269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 120269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myServerSocket.close(); 121269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke myThread.join(); 122269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 1237b88819e82b89ac3476ce060903468076a73de8fPaul Hawke ioe.printStackTrace(); 124269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (InterruptedException e) { 1257b88819e82b89ac3476ce060903468076a73de8fPaul Hawke e.printStackTrace(); 126269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 127269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 128269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1294e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { 1304e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke this.tempFileManagerFactory = tempFileManagerFactory; 1314e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 1324e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 1334e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke public void setAsyncRunner(AsyncRunner asyncRunner) { 1344e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke this.asyncRunner = asyncRunner; 1354e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 1364e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 137a6619fe65dd3a997960724faf7c51685de793f91Paul Hawke /** 138d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * Override this to customize the server. 139d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 140d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 141d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * (By default, this delegates to serveFile() and allows directory listing.) 1429788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * 1439788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * @param uri Percent-decoded URI without parameters, for example "/index.cgi" 1449788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * @param method "GET", "POST" etc. 1459788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data. 1469788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * @param header Header entries, percent decoded 147d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * @return HTTP response, see class Response for details 148269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 1492b54eda5fee8430268a99d73b061e790362db544Tom Hermann public abstract Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms, 1509788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke Map<String, String> files); 1519788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 1529788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public enum Method { 1539788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke GET, PUT, POST, DELETE; 1549788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 1559788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke static Method lookup(String method) { 1569788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke for (Method m : Method.values()) { 1579788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke if (m.toString().equalsIgnoreCase(method)) { 1589788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke return m; 1599788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 1609788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 1619788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke return null; 1629788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 1639788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 1649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 1656200253df7b934c807eb3436b51a3b039406457dPaul Hawke public interface AsyncRunner { 1666200253df7b934c807eb3436b51a3b039406457dPaul Hawke void exec(Runnable code); 1676200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1686200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1696200253df7b934c807eb3436b51a3b039406457dPaul Hawke public interface TempFileManagerFactory { 1706200253df7b934c807eb3436b51a3b039406457dPaul Hawke TempFileManager create(); 1716200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1726200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1736200253df7b934c807eb3436b51a3b039406457dPaul Hawke public interface TempFileManager { 1746200253df7b934c807eb3436b51a3b039406457dPaul Hawke TempFile createTempFile() throws Exception; 1756200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1766200253df7b934c807eb3436b51a3b039406457dPaul Hawke void clear(); 1776200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1786200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1796200253df7b934c807eb3436b51a3b039406457dPaul Hawke public interface TempFile { 1806200253df7b934c807eb3436b51a3b039406457dPaul Hawke OutputStream open() throws Exception; 1816200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1826200253df7b934c807eb3436b51a3b039406457dPaul Hawke void delete() throws Exception; 1836200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1846200253df7b934c807eb3436b51a3b039406457dPaul Hawke String getName(); 1856200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 1866200253df7b934c807eb3436b51a3b039406457dPaul Hawke 1879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 1889788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * HTTP response. Return one of these from serve(). 1899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 1909788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public static class Response { 1919788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 1929788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * HTTP status code after processing, e.g. "200 OK", HTTP_OK 1939788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 1949788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public Status status; 1959788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 1969788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * MIME type of content, e.g. "text/html" 1979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 1989788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public String mimeType; 1999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Data of the response, may be null. 2019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2029788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public InputStream data; 2039788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2049788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Headers for the HTTP response. Use addHeader() to add lines. 2059788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2069788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public Map<String, String> header = new HashMap<String, String>(); 2079788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2089788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2099788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message 2109788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2119788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public Response(String msg) { 2129788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this(Status.OK, MIME_HTML, msg); 2139788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2149788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2159788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2169788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Basic constructor. 2179788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2189788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public Response(Status status, String mimeType, InputStream data) { 2199788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.status = status; 2209788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.mimeType = mimeType; 2219788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.data = data; 2229788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2239788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2249788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2259788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Convenience method that makes an InputStream out of given text. 2269788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2279788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public Response(Status status, String mimeType, String txt) { 2289788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.status = status; 2299788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.mimeType = mimeType; 2309788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke try { 2319788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.data = new ByteArrayInputStream(txt.getBytes("UTF-8")); 2329788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } catch (java.io.UnsupportedEncodingException uee) { 2339788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke uee.printStackTrace(); 2349788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2359788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2369788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2379788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2389788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Adds given line to the header. 2399788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2409788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public void addHeader(String name, String value) { 2419788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke header.put(name, value); 2429788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2439788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2449788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 2459788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke * Some HTTP response status codes 2469788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 2479788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public enum Status { 2489788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke OK(200, "OK"), CREATED(201, "Created"), NO_CONTENT(204, "No Content"), PARTIAL_CONTENT(206, "Partial Content"), REDIRECT(301, 2499788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke "Moved Permanently"), NOT_MODIFIED(304, "Not Modified"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, 2509788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke "Unauthorized"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), RANGE_NOT_SATISFIABLE(416, 2519788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke "Requested Range Not Satisfiable"), INTERNAL_ERROR(500, "Internal Server Error"); 2529788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke private int requestStatus; 2539788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke private String descr; 2549788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2559788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke Status(int requestStatus, String descr) { 2569788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.requestStatus = requestStatus; 2579788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke this.descr = descr; 2589788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2599788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2609788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public int getRequestStatus() { 2619788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke return this.requestStatus; 2629788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2639788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 2649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke public String getDescription() { 2659788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke return "" + this.requestStatus + " " + descr; 2669788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2679788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 2689788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 269269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 2706200253df7b934c807eb3436b51a3b039406457dPaul Hawke public static class DefaultTempFile implements TempFile { 2716200253df7b934c807eb3436b51a3b039406457dPaul Hawke private File file; 2726200253df7b934c807eb3436b51a3b039406457dPaul Hawke private OutputStream fstream; 2736200253df7b934c807eb3436b51a3b039406457dPaul Hawke 2746200253df7b934c807eb3436b51a3b039406457dPaul Hawke public DefaultTempFile(String tempdir) throws IOException { 2756200253df7b934c807eb3436b51a3b039406457dPaul Hawke file = File.createTempFile("NanoHTTPD-", "", new File(tempdir)); 2766200253df7b934c807eb3436b51a3b039406457dPaul Hawke fstream = new FileOutputStream(file); 2776200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 2786200253df7b934c807eb3436b51a3b039406457dPaul Hawke 2796200253df7b934c807eb3436b51a3b039406457dPaul Hawke @Override 2806200253df7b934c807eb3436b51a3b039406457dPaul Hawke public OutputStream open() throws Exception { 2816200253df7b934c807eb3436b51a3b039406457dPaul Hawke return fstream; 2826200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 2836200253df7b934c807eb3436b51a3b039406457dPaul Hawke 2846200253df7b934c807eb3436b51a3b039406457dPaul Hawke @Override 2856200253df7b934c807eb3436b51a3b039406457dPaul Hawke public void delete() throws Exception { 2866200253df7b934c807eb3436b51a3b039406457dPaul Hawke file.delete(); 2876200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 2886200253df7b934c807eb3436b51a3b039406457dPaul Hawke 2896200253df7b934c807eb3436b51a3b039406457dPaul Hawke @Override 2906200253df7b934c807eb3436b51a3b039406457dPaul Hawke public String getName() { 2916200253df7b934c807eb3436b51a3b039406457dPaul Hawke return file.getAbsolutePath(); 2926200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 2936200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 2946200253df7b934c807eb3436b51a3b039406457dPaul Hawke 295d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke /** 296d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * Handles one session, i.e. parses the HTTP request and returns the response. 297d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 298d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke private class HTTPSession implements Runnable { 2994e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private final TempFileManager tempFileManager; 3006200253df7b934c807eb3436b51a3b039406457dPaul Hawke private InputStream is; 3016200253df7b934c807eb3436b51a3b039406457dPaul Hawke private OutputStream outputStream; 3029788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 3036200253df7b934c807eb3436b51a3b039406457dPaul Hawke public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { 3046200253df7b934c807eb3436b51a3b039406457dPaul Hawke this.tempFileManager = tempFileManager; 3056200253df7b934c807eb3436b51a3b039406457dPaul Hawke this.is = inputStream; 3066200253df7b934c807eb3436b51a3b039406457dPaul Hawke this.outputStream = outputStream; 307d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } 308d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 309d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke @Override 310d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke public void run() { 311d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke try { 3126200253df7b934c807eb3436b51a3b039406457dPaul Hawke if (is == null) { 313d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke return; 3146200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 315d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 316d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Read the first 8192 bytes. 317d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // The full header should fit in here. 318d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Apache's default header limit is 8KB. 319d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Do NOT assume that a single read will get the entire header at once! 320d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke final int bufsize = 8192; 321d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke byte[] buf = new byte[bufsize]; 322d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke int splitbyte = 0; 323d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke int rlen = 0; 324d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke { 325d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke int read = is.read(buf, 0, bufsize); 326d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke while (read > 0) { 327d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke rlen += read; 328d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke splitbyte = findHeaderEnd(buf, rlen); 329d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke if (splitbyte > 0) 330d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke break; 331d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke read = is.read(buf, rlen, bufsize - rlen); 332d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } 333d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } 334d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 335d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Create a BufferedReader for parsing the header. 336d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke ByteArrayInputStream hbis = new ByteArrayInputStream(buf, 0, rlen); 337d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke BufferedReader hin = new BufferedReader(new InputStreamReader(hbis)); 338d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke Properties pre = new Properties(); 339d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke Map<String, String> parms = new HashMap<String, String>(); 340d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke Map<String, String> header = new HashMap<String, String>(); 341d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke Map<String, String> files = new HashMap<String, String>(); 342d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 343d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Decode the header into parms and header java properties 344d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke decodeHeader(hin, pre, parms, header); 345df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke Method method = Method.lookup(pre.getProperty("method")); 3460cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (method == null) { 347df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); 3480cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 349d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke String uri = pre.getProperty("uri"); 350d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 351d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke long size = 0x7FFFFFFFFFFFFFFFl; 352d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke String contentLength = header.get("content-length"); 353d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke if (contentLength != null) { 354d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke try { 355d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke size = Integer.parseInt(contentLength); 356d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } catch (NumberFormatException ex) { 3577b88819e82b89ac3476ce060903468076a73de8fPaul Hawke ex.printStackTrace(); 358d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } 359d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } 360d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 361d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Write the part of body already read to ByteArrayOutputStream f 362d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke ByteArrayOutputStream f = new ByteArrayOutputStream(); 3630cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (splitbyte < rlen) { 364d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke f.write(buf, splitbyte, rlen - splitbyte); 3650cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 366d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 367d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // While Firefox sends on the first read all the data fitting 368d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // our buffer, Chrome and Opera send only the headers even if 369d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // there is data for the body. We do some magic here to find 370d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // out whether we have already consumed part of body, if we 371d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // have reached the end of the data to be sent or we should 372d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // expect the first byte of the body at the next read. 3730cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (splitbyte < rlen) { 374d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke size -= rlen - splitbyte + 1; 3750cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } else if (splitbyte == 0 || size == 0x7FFFFFFFFFFFFFFFl) { 376d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke size = 0; 3770cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 378d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 379d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Now read all the body and write it to f 380d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke buf = new byte[512]; 381d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke while (rlen >= 0 && size > 0) { 382269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke rlen = is.read(buf, 0, 512); 383269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke size -= rlen; 3840cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (rlen > 0) { 385269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke f.write(buf, 0, rlen); 3860cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 387269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 388269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 389269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Get the raw body as a byte [] 390269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke byte[] fbuf = f.toByteArray(); 391269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 392269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Create a BufferedReader for easily reading it as string. 393269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke ByteArrayInputStream bin = new ByteArrayInputStream(fbuf); 394269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke BufferedReader in = new BufferedReader(new InputStreamReader(bin)); 395269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 396269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 397269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 398df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke if (Method.POST.equals(method)) { 399269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 400269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentTypeHeader = header.get("content-type"); 4012b54eda5fee8430268a99d73b061e790362db544Tom Hermann 4022c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke StringTokenizer st = null; 4032c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (contentTypeHeader != null) { 4042c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke st = new StringTokenizer(contentTypeHeader, ",; "); 4052c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (st.hasMoreTokens()) { 4062c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke contentType = st.nextToken(); 4072c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke } 408269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 409269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 4100277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke if ("multipart/form-data".equalsIgnoreCase(contentType)) { 411269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 4120cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 413df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, 414269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 4150cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 4162b54eda5fee8430268a99d73b061e790362db544Tom Hermann 4174e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundaryStartString = "boundary="; 4182b54eda5fee8430268a99d73b061e790362db544Tom Hermann int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length(); 4194e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length()); 4204e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke if (boundary.startsWith("\"") && boundary.startsWith("\"")) { 4214e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke boundary = boundary.substring(1, boundary.length() - 1); 4224e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 423269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 424269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeMultipartData(boundary, fbuf, in, parms, files); 425269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 426269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle application/x-www-form-urlencoded 427269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String postLine = ""; 428269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke char pbuf[] = new char[512]; 429269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = in.read(pbuf); 430269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (read >= 0 && !postLine.endsWith("\r\n")) { 431269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke postLine += String.valueOf(pbuf, 0, read); 432269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke read = in.read(pbuf); 433269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 434269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke postLine = postLine.trim(); 435269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeParms(postLine, parms); 436269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 437269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 438269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 439df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke if (Method.PUT.equals(method)) 440269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke files.put("content", saveTmpFile(fbuf, 0, f.size())); 441269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 442269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Ok, now do the serve() 443269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Response r = serve(uri, method, header, parms, files); 4449788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke if (r == null) { 445df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 4469788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } else { 447269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendResponse(r.status, r.mimeType, r.header, r.data); 4489788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 449269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 450269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke in.close(); 451269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke is.close(); 452269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 453269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 454df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 4557b88819e82b89ac3476ce060903468076a73de8fPaul Hawke } catch (Throwable ignored) { 456269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 457269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (InterruptedException ie) { 458269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Thrown by sendError, ignore and exit the thread. 4594e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } finally { 4604e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke tempFileManager.clear(); 461269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 462269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 463269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 464269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 465269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes the sent headers and loads the data into java Properties' key - value pairs 466d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 467269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void decodeHeader(BufferedReader in, Properties pre, Map<String, String> parms, Map<String, String> header) 468269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke throws InterruptedException { 469269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 470269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Read the request line 471269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String inLine = in.readLine(); 4720cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (inLine == null) { 473269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return; 4740cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 4750cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke 476269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(inLine); 4770cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 478df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 4790cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 480269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 4810277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke pre.put("method", st.nextToken()); 482269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 4830cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 484df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 4850cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 486269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 487269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String uri = st.nextToken(); 488269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 489269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Decode parameters from the URI 490269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int qmi = uri.indexOf('?'); 491269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (qmi >= 0) { 492269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke decodeParms(uri.substring(qmi + 1), parms); 493269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri = decodePercent(uri.substring(0, qmi)); 4949788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } else { 495269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke uri = decodePercent(uri); 4969788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 497269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 498269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If there's another token, it's protocol version, 499269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // followed by HTTP headers. Ignore version but parse headers. 500269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // NOTE: this now forces header names lowercase since they are 501269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // case insensitive and vary by client. 502269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (st.hasMoreTokens()) { 503269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String line = in.readLine(); 504269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (line != null && line.trim().length() > 0) { 505269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int p = line.indexOf(':'); 506269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (p >= 0) 507269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim()); 508269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke line = in.readLine(); 509269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 510269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 511269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 512269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pre.put("uri", uri); 513269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 514df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 515269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 516269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 517269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 518269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 519269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes the Multipart Body data and put it into java Properties' key - value pairs. 520d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 521269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void decodeMultipartData(String boundary, byte[] fbuf, BufferedReader in, Map<String, String> parms, 5229788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke Map<String, String> files) throws InterruptedException { 523269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 524269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes()); 525269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int boundarycount = 1; 526269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String mpline = in.readLine(); 527269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (mpline != null) { 5287b88819e82b89ac3476ce060903468076a73de8fPaul Hawke if (!mpline.contains(boundary)) { 529df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, 530269baff79590de5abd70e8e05bf46547e4a28ee6Micah 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"); 5317b88819e82b89ac3476ce060903468076a73de8fPaul Hawke } 532269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke boundarycount++; 533269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> item = new HashMap<String, String>(); 534269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 535269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (mpline != null && mpline.trim().length() > 0) { 536269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int p = mpline.indexOf(':'); 537269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (p != -1) { 538269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke item.put(mpline.substring(0, p).trim().toLowerCase(), mpline.substring(p + 1).trim()); 539269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 540269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 541269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 542269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mpline != null) { 543269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentDisposition = item.get("content-disposition"); 544269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (contentDisposition == null) { 545df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, 546269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html"); 547269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 548269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(contentDisposition, "; "); 549269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke Map<String, String> disposition = new HashMap<String, String>(); 550269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (st.hasMoreTokens()) { 551269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String token = st.nextToken(); 552269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int p = token.indexOf('='); 553269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (p != -1) { 554269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke disposition.put(token.substring(0, p).trim().toLowerCase(), token.substring(p + 1).trim()); 555269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 556269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 557269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String pname = disposition.get("name"); 558269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pname = pname.substring(1, pname.length() - 1); 559269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 560269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = ""; 561269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (item.get("content-type") == null) { 5627b88819e82b89ac3476ce060903468076a73de8fPaul Hawke while (mpline != null && !mpline.contains(boundary)) { 563269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 564269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mpline != null) { 565269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int d = mpline.indexOf(boundary); 566269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (d == -1) { 567269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value += mpline; 568269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 569269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value += mpline.substring(0, d - 2); 570269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 571269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 572269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 573269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 574269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (boundarycount > bpositions.length) { 575df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.INTERNAL_ERROR, "Error processing request"); 576269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 577269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]); 578269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4); 579269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke files.put(pname, path); 580269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value = disposition.get("filename"); 581269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke value = value.substring(1, value.length() - 1); 582269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke do { 583269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke mpline = in.readLine(); 5847b88819e82b89ac3476ce060903468076a73de8fPaul Hawke } while (mpline != null && !mpline.contains(boundary)); 585269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 586269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke parms.put(pname, value); 587269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 588269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 589269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 590df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 591269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 592269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 593269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 594269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 595269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Find byte index separating header from body. It must be the last byte of the first two sequential new lines. 596d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 597269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private int findHeaderEnd(final byte[] buf, int rlen) { 598269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int splitbyte = 0; 599269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (splitbyte + 3 < rlen) { 600269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 601269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return splitbyte + 4; 602269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 603269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke splitbyte++; 604269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 605269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return 0; 606269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 607269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 608269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 609269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Find the byte positions where multipart boundaries start. 610d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 611269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke public int[] getBoundaryPositions(byte[] b, byte[] boundary) { 612269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int matchcount = 0; 613269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int matchbyte = -1; 614269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke List<Integer> matchbytes = new ArrayList<Integer>(); 615269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < b.length; i++) { 616269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (b[i] == boundary[matchcount]) { 617269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (matchcount == 0) 618269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbyte = i; 619269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchcount++; 620269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (matchcount == boundary.length) { 621269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbytes.add(matchbyte); 622269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchcount = 0; 623269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbyte = -1; 624269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 625269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 626269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke i -= matchcount; 627269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchcount = 0; 628269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke matchbyte = -1; 629269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 630269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 631269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int[] ret = new int[matchbytes.size()]; 632269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < ret.length; i++) { 6337b88819e82b89ac3476ce060903468076a73de8fPaul Hawke ret[i] = matchbytes.get(i); 634269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 635269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return ret; 636269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 637269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 638269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 639269baff79590de5abd70e8e05bf46547e4a28ee6Micah 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. 640d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 641269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private String saveTmpFile(byte[] b, int offset, int len) { 642269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String path = ""; 643269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (len > 0) { 644269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 6454e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke TempFile tempFile = tempFileManager.createTempFile(); 6464e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke OutputStream fstream = tempFile.open(); 647269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke fstream.write(b, offset, len); 648269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke fstream.close(); 6494e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke path = tempFile.getName(); 650269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Exception e) { // Catch exception if any 651d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke System.err.println("Error: " + e.getMessage()); 652269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 653269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 654269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return path; 655269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 656269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 657269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 658269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * It returns the offset separating multipart file headers from the file's data. 659269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 660269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private int stripMultipartHeaders(byte[] b, int offset) { 661269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int i; 662269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (i = offset; i < b.length; i++) { 663269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (b[i] == '\r' && b[++i] == '\n' && b[++i] == '\r' && b[++i] == '\n') { 664269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 665269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 666269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 667269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return i + 1; 668269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 669269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 670269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 671269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes the percent encoding scheme. <br/> 672269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * For example: "an+example%20string" -> "an example string" 673269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 674269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private String decodePercent(String str) throws InterruptedException { 675269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 6767b88819e82b89ac3476ce060903468076a73de8fPaul Hawke StringBuilder sb = new StringBuilder(); 677269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke for (int i = 0; i < str.length(); i++) { 678269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke char c = str.charAt(i); 679269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke switch (c) { 6809788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke case '+': 6819788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke sb.append(' '); 6829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke break; 6839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke case '%': 6849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke sb.append((char) Integer.parseInt(str.substring(i + 1, i + 3), 16)); 6859788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke i += 2; 6869788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke break; 6879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke default: 6889788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke sb.append(c); 6899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke break; 690269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 691269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 692269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return sb.toString(); 693269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Exception e) { 694df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke sendError(Response.Status.BAD_REQUEST, "BAD REQUEST: Bad percent-encoding."); 695269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return null; 696269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 697269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 698269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 699269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 700269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 701269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Properties. NOTE: this doesn't support multiple identical keys due to the simplicity of Properties -- if you need multiples, you 702269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * might want to replace the Properties with a Hashtable of Vectors or such. 703269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 704269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke private void decodeParms(String parms, Map<String, String> p) throws InterruptedException { 705269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (parms == null) 706269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return; 707269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 708269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke StringTokenizer st = new StringTokenizer(parms, "&"); 709269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (st.hasMoreTokens()) { 710269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String e = st.nextToken(); 711269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int sep = e.indexOf('='); 712269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (sep >= 0) { 7139788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke p.put(decodePercent(e.substring(0, sep)).trim(), 7149788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke decodePercent(e.substring(sep + 1))); 7159788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } else { 7169788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke p.put(decodePercent(e).trim(), ""); 717269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 718269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 719269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 720269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 721269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 722269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Returns an error message as a HTTP response and throws InterruptedException to stop further request processing. 723269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 724df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke private void sendError(Response.Status status, String msg) throws InterruptedException { 725269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke sendResponse(status, MIME_PLAINTEXT, null, new ByteArrayInputStream(msg.getBytes())); 726269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke throw new InterruptedException(); 727269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 728269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 729269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 730269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * Sends given response to the socket. 731269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 732df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke private void sendResponse(Response.Status status, String mime, Map<String, String> header, InputStream data) { 7337b88819e82b89ac3476ce060903468076a73de8fPaul Hawke SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 7347b88819e82b89ac3476ce060903468076a73de8fPaul Hawke gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 7357b88819e82b89ac3476ce060903468076a73de8fPaul Hawke 736269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 737269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (status == null) { 738269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke throw new Error("sendResponse(): Status can't be null."); 739269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 7406200253df7b934c807eb3436b51a3b039406457dPaul Hawke PrintWriter pw = new PrintWriter(outputStream); 7419f11fbed671ffe845a11044d7c0a14a0775cb5aePaul Hawke pw.print("HTTP/1.0 " + status.getDescription() + " \r\n"); 742269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 743269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (mime != null) { 744269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("Content-Type: " + mime + "\r\n"); 745269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 746269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 747269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (header == null || header.get("Date") == null) { 748269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 749269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 750269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 751269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (header != null) { 7527b88819e82b89ac3476ce060903468076a73de8fPaul Hawke for (String key : header.keySet()) { 753269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String value = header.get(key); 754269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print(key + ": " + value + "\r\n"); 755269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 756269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 757269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 758269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.print("\r\n"); 759269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pw.flush(); 760269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 761269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (data != null) { 762269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int pending = data.available(); // This is to support partial sends, see serveFile() 763d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke int BUFFER_SIZE = 16 * 1024; 764269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke byte[] buff = new byte[BUFFER_SIZE]; 765269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (pending > 0) { 766269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending)); 767269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (read <= 0) { 768269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke break; 769269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 7706200253df7b934c807eb3436b51a3b039406457dPaul Hawke outputStream.write(buff, 0, read); 771269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke pending -= read; 772269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 773269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 7746200253df7b934c807eb3436b51a3b039406457dPaul Hawke outputStream.flush(); 7756200253df7b934c807eb3436b51a3b039406457dPaul Hawke outputStream.close(); 776269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (data != null) 777269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke data.close(); 778269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (IOException ioe) { 779269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Couldn't write? No can do. 780269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 781269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 782269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 7834e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 7844e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private class DefaultTempFileManagerFactory implements TempFileManagerFactory { 7854e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke @Override 7864e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke public TempFileManager create() { 7874e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke return new DefaultTempFileManager(); 7884e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 7894e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 7904e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 7914e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private class DefaultTempFileManager implements TempFileManager { 7924e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private final String tmpdir; 7934e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private final List<TempFile> tempFiles; 7944e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 7954e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private DefaultTempFileManager() { 7964e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke tmpdir = System.getProperty("java.io.tmpdir"); 7974e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke tempFiles = new ArrayList<TempFile>(); 7984e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 7994e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 8004e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke @Override 8014e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke public TempFile createTempFile() throws Exception { 8024e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke DefaultTempFile tempFile = new DefaultTempFile(tmpdir); 8034e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke tempFiles.add(tempFile); 8044e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke return tempFile; 8054e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 8064e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 8074e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke @Override 8084e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke public void clear() { 8094e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke for (TempFile file : tempFiles) { 8104e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke try { 8114e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke file.delete(); 8126200253df7b934c807eb3436b51a3b039406457dPaul Hawke } catch (Exception ignored) { 8136200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 8144e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 8154e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke tempFiles.clear(); 8164e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 8174e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 8184e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke 8194e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke private class DefaultAsyncRunner implements AsyncRunner { 8204e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke @Override 8214e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke public void exec(Runnable code) { 8224e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke Thread t = new Thread(code); 8234e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke t.setDaemon(true); 8244e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke t.start(); 8254e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 8264e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 827269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 828