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 &copy; 2001,2005-2012 Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/) and Copyright &copy; 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