NanoHTTPD.java revision 9d9bd98292f8f38cbd3450d1abe94009b281b6e7
1269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkepackage fi.iki.elonen;
2269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
39788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawkeimport java.io.*;
4f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawkeimport java.net.InetSocketAddress;
5269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.ServerSocket;
6269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkeimport java.net.Socket;
7dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.ByteBuffer;
8dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.channels.FileChannel;
97b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat;
109788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawkeimport java.util.*;
11269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
12269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/**
13269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * A simple, tiny, nicely embeddable HTTP 1.0 (partially 1.1) server in Java
14d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
15d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
16b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke * NanoHTTPD
17b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke *   -- version 1.25, Copyright &copy; 2001,2005-2012
18b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke *      Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/) and
1950b70cd2a7250fd85394edc863b219a3bfb738e6Paul Hawke *      Copyright &copy; 2010 Konstantinos Togias (info@ktogias.gr, http://ktogias.gr)
2001ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <p/>
2150b70cd2a7250fd85394edc863b219a3bfb738e6Paul Hawke *   -- version 6 and above, Copyright &copy; 2012-
2250b70cd2a7250fd85394edc863b219a3bfb738e6Paul Hawke *      Uplifted to Java6 by Paul Hawke (paul.hawke@gmail.com) and Micah Hainline.
23d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
24d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
25269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b>
26269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul>
27d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
28269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li>
2901ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li>
30269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li>
31269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li>
32269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li>
33269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li>
34269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li>
35269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li>
36269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li>
37269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li>
38269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li>
39269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li>
40269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li>
41269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li>
42269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li>
43269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li>
44269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li>
45269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li>
46269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Contains a built-in list of most common mime types</li>
47269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>All header names are converted lowercase so they don't vary between browsers/clients</li>
48d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
49269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul>
50d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
51d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
5201ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b>
53269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul>
54d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
5501ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li>
56d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
57269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul>
58d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
5950b70cd2a7250fd85394edc863b219a3bfb738e6Paul Hawke * See the separate "LICENSE.md" file for the distribution license (Modified BSD licence)
60269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */
61d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD {
62269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    /**
63269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     * Common mime types for dynamic content
64269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
65269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public static final String MIME_PLAINTEXT = "text/plain";
66269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public static final String MIME_HTML = "text/html";
67269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public static final String MIME_DEFAULT_BINARY = "application/octet-stream";
68f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke    private final String hostname;
697b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    private final int myPort;
707b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    private ServerSocket myServerSocket;
717b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    private Thread myThread;
729d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke    /**
739d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke     * Pluggable strategy for creating and cleaning up temporary files.
749d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke     */
754e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private TempFileManagerFactory tempFileManagerFactory;
769d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke    /**
779d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke     * Pluggable strategy for asynchronously executing requests.
789d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke     */
794e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private AsyncRunner asyncRunner;
809d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke    /**
819d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke     * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
829d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke     */
839d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke    private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
84269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
85269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    /**
86d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Constructs an HTTP server on given port.
87269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
88df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke    public NanoHTTPD(int port) {
89f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        this(null, port);
90f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke    }
91f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke
92f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke    public NanoHTTPD(String hostname, int port) {
93f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        this.hostname = hostname;
947b88819e82b89ac3476ce060903468076a73de8fPaul Hawke        this.myPort = port;
954e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.tempFileManagerFactory = new DefaultTempFileManagerFactory();
964e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.asyncRunner = new DefaultAsyncRunner();
973f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke    }
983f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke
993f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke    /**
1003f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke     * Starts the server
101d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * <p/>
102d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Throws an IOException if the socket is already in use
1033f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke     */
1047b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    public void start() throws IOException {
105f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        myServerSocket = new ServerSocket();
106f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
107f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke
108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        myThread = new Thread(new Runnable() {
109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            @Override
110269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            public void run() {
1116200253df7b934c807eb3436b51a3b039406457dPaul Hawke                do {
1126200253df7b934c807eb3436b51a3b039406457dPaul Hawke                    try {
1136200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        final Socket finalAccept = myServerSocket.accept();
1146200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        InputStream inputStream = finalAccept.getInputStream();
1156200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        OutputStream outputStream = finalAccept.getOutputStream();
1166200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        TempFileManager tempFileManager = tempFileManagerFactory.create();
1176200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        final HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream);
1186200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        asyncRunner.exec(new Runnable() {
1196200253df7b934c807eb3436b51a3b039406457dPaul Hawke                            @Override
1206200253df7b934c807eb3436b51a3b039406457dPaul Hawke                            public void run() {
1216200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                session.run();
1229d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke                                try {
1239d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke                                    finalAccept.close();
1249d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke                                } catch (IOException ignored) {
1256200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                }
1266200253df7b934c807eb3436b51a3b039406457dPaul Hawke                            }
1276200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        });
1286200253df7b934c807eb3436b51a3b039406457dPaul Hawke                    } catch (IOException e) {
1296200253df7b934c807eb3436b51a3b039406457dPaul Hawke                    }
1302dc83f968760f835a138f0320db9eca60f8e0ecaBjörn Heinrichs                } while (!myServerSocket.isClosed());
131269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
132269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        });
133269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        myThread.setDaemon(true);
1341ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke        myThread.setName("NanoHttpd Main Listener");
135269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        myThread.start();
136269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    }
137d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
138269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    /**
139269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     * Stops the server.
140269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
141269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public void stop() {
142269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        try {
143269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            myServerSocket.close();
144269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            myThread.join();
145269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        } catch (IOException ioe) {
1467b88819e82b89ac3476ce060903468076a73de8fPaul Hawke            ioe.printStackTrace();
147269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        } catch (InterruptedException e) {
1487b88819e82b89ac3476ce060903468076a73de8fPaul Hawke            e.printStackTrace();
149269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
150269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    }
151269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
1524e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
1534e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.tempFileManagerFactory = tempFileManagerFactory;
1544e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
1554e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
1564e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    public void setAsyncRunner(AsyncRunner asyncRunner) {
1574e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.asyncRunner = asyncRunner;
1584e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
1594e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
160a6619fe65dd3a997960724faf7c51685de793f91Paul Hawke    /**
161d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Override this to customize the server.
162d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * <p/>
163d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * <p/>
164d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * (By default, this delegates to serveFile() and allows directory listing.)
1659788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     *
1669788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param uri    Percent-decoded URI without parameters, for example "/index.cgi"
1679788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param method "GET", "POST" etc.
1689788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param parms  Parsed, percent decoded parameters from URI and, in case of POST, data.
1699788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param header Header entries, percent decoded
170d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * @return HTTP response, see class Response for details
171269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
1722b54eda5fee8430268a99d73b061e790362db544Tom Hermann    public abstract Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms,
1739788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                                   Map<String, String> files);
1749788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
175f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    /**
176f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     * Decodes the percent encoding scheme. <br/>
177f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     * For example: "an+example%20string" -> "an example string"
178f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     */
179b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke    protected String decodePercent(String str) {
180b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke        StringBuilder sb = new StringBuilder();
181b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke        for (int i = 0; i < str.length(); i++) {
182b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke            char c = str.charAt(i);
183b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke            switch (c) {
184b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                case '+':
185b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    sb.append(' ');
186b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    break;
187b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                case '%':
188b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    sb.append((char) Integer.parseInt(str.substring(i + 1, i + 3), 16));
189b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    i += 2;
190b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    break;
191b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                default:
192b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    sb.append(c);
193b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    break;
194f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            }
195f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        }
196b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke        return sb.toString();
197f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    }
198f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke
199f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    protected Map<String, List<String>> decodeParameters(Map<String, String> parms) {
200f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        return this.decodeParameters(parms.get(QUERY_STRING_PARAMETER));
201f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    }
202f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke
203f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    protected Map<String, List<String>> decodeParameters(String queryString) {
204f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        Map<String, List<String>> parms = new HashMap<String, List<String>>();
205f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        if (queryString != null) {
206f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            StringTokenizer st = new StringTokenizer(queryString, "&");
207f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            while (st.hasMoreTokens()) {
208f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                String e = st.nextToken();
209f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                int sep = e.indexOf('=');
210b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
211b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                if (!parms.containsKey(propertyName)) {
212b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    parms.put(propertyName, new ArrayList<String>());
213b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                }
214b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null;
215b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                if (propertyValue != null) {
216b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    parms.get(propertyName).add(propertyValue);
217f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                }
218f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            }
219f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        }
220f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        return parms;
221f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    }
222f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke
2239788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    public enum Method {
224b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke        GET, PUT, POST, DELETE, HEAD;
2259788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2269788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        static Method lookup(String method) {
2279788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            for (Method m : Method.values()) {
2289788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                if (m.toString().equalsIgnoreCase(method)) {
2299788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    return m;
2309788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                }
2319788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
2329788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            return null;
2339788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
2349788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    }
2359788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2366200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface AsyncRunner {
2376200253df7b934c807eb3436b51a3b039406457dPaul Hawke        void exec(Runnable code);
2386200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2396200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2406200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface TempFileManagerFactory {
2416200253df7b934c807eb3436b51a3b039406457dPaul Hawke        TempFileManager create();
2426200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2436200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2446200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface TempFileManager {
2456200253df7b934c807eb3436b51a3b039406457dPaul Hawke        TempFile createTempFile() throws Exception;
2466200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2476200253df7b934c807eb3436b51a3b039406457dPaul Hawke        void clear();
2486200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2496200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2506200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface TempFile {
2516200253df7b934c807eb3436b51a3b039406457dPaul Hawke        OutputStream open() throws Exception;
2526200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2536200253df7b934c807eb3436b51a3b039406457dPaul Hawke        void delete() throws Exception;
2546200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2556200253df7b934c807eb3436b51a3b039406457dPaul Hawke        String getName();
2566200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2576200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2589788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    /**
2599788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * HTTP response. Return one of these from serve().
2609788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     */
2619788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    public static class Response {
2629788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2639788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * HTTP status code after processing, e.g. "200 OK", HTTP_OK
2649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2659d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private final Status status;
2669788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2679788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * MIME type of content, e.g. "text/html"
2689788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2699d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private final String mimeType;
2709788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2719788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Data of the response, may be null.
2729788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2739d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private InputStream data;
2749788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2759788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Headers for the HTTP response. Use addHeader() to add lines.
2769788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2779d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private final Map<String, String> header = new HashMap<String, String>();
2789d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        /**
2799d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke         * The request method that spawned this response.
2809d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke         */
2819d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private Method requestMethod;
2829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message
2849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2859788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Response(String msg) {
2869788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this(Status.OK, MIME_HTML, msg);
2879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
2889788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2909788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Basic constructor.
2919788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2929788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Response(Status status, String mimeType, InputStream data) {
2939788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.status = status;
2949788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.mimeType = mimeType;
2959788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.data = data;
2969788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
2979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2989788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Convenience method that makes an InputStream out of given text.
3009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
3019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Response(Status status, String mimeType, String txt) {
3029788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.status = status;
3039788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.mimeType = mimeType;
3049788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            try {
3059788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                this.data = new ByteArrayInputStream(txt.getBytes("UTF-8"));
3069788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            } catch (java.io.UnsupportedEncodingException uee) {
3079788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                uee.printStackTrace();
3089788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
3099788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
3109788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
311f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        public static void error(OutputStream outputStream, Status error, String message) {
312f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            new Response(error, MIME_PLAINTEXT, message).send(outputStream);
313f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        }
314f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke
315b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke        public void setRequestMethod(Method requestMethod) {
316b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke            this.requestMethod = requestMethod;
317b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke        }
318b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke
3199788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
3209788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Adds given line to the header.
3219788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
3229788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public void addHeader(String name, String value) {
3239788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            header.put(name, value);
3249788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
3259788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3269788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
3277f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Sends given response to the socket.
3287f0727787957c2f093412c01d165846842ec2425Paul Hawke         */
3297f0727787957c2f093412c01d165846842ec2425Paul Hawke        private void send(OutputStream outputStream) {
3307f0727787957c2f093412c01d165846842ec2425Paul Hawke            String mime = mimeType;
3317f0727787957c2f093412c01d165846842ec2425Paul Hawke            SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
3327f0727787957c2f093412c01d165846842ec2425Paul Hawke            gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
3337f0727787957c2f093412c01d165846842ec2425Paul Hawke
3347f0727787957c2f093412c01d165846842ec2425Paul Hawke            try {
3357f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (status == null) {
3367f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new Error("sendResponse(): Status can't be null.");
3377f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3387f0727787957c2f093412c01d165846842ec2425Paul Hawke                PrintWriter pw = new PrintWriter(outputStream);
3397f0727787957c2f093412c01d165846842ec2425Paul Hawke                pw.print("HTTP/1.0 " + status.getDescription() + " \r\n");
3407f0727787957c2f093412c01d165846842ec2425Paul Hawke
3417f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (mime != null) {
3427f0727787957c2f093412c01d165846842ec2425Paul Hawke                    pw.print("Content-Type: " + mime + "\r\n");
3437f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3447f0727787957c2f093412c01d165846842ec2425Paul Hawke
3457f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (header == null || header.get("Date") == null) {
3467f0727787957c2f093412c01d165846842ec2425Paul Hawke                    pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n");
3477f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3487f0727787957c2f093412c01d165846842ec2425Paul Hawke
3497f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (header != null) {
3507f0727787957c2f093412c01d165846842ec2425Paul Hawke                    for (String key : header.keySet()) {
3517f0727787957c2f093412c01d165846842ec2425Paul Hawke                        String value = header.get(key);
3527f0727787957c2f093412c01d165846842ec2425Paul Hawke                        pw.print(key + ": " + value + "\r\n");
3537f0727787957c2f093412c01d165846842ec2425Paul Hawke                    }
3547f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3557f0727787957c2f093412c01d165846842ec2425Paul Hawke
3567f0727787957c2f093412c01d165846842ec2425Paul Hawke                pw.print("\r\n");
3577f0727787957c2f093412c01d165846842ec2425Paul Hawke                pw.flush();
3587f0727787957c2f093412c01d165846842ec2425Paul Hawke
359b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                if (requestMethod != Method.HEAD && data != null) {
3607f0727787957c2f093412c01d165846842ec2425Paul Hawke                    int pending = data.available(); // This is to support partial sends, see serveFile()
3617f0727787957c2f093412c01d165846842ec2425Paul Hawke                    int BUFFER_SIZE = 16 * 1024;
3627f0727787957c2f093412c01d165846842ec2425Paul Hawke                    byte[] buff = new byte[BUFFER_SIZE];
3637f0727787957c2f093412c01d165846842ec2425Paul Hawke                    while (pending > 0) {
3647f0727787957c2f093412c01d165846842ec2425Paul Hawke                        int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending));
3657f0727787957c2f093412c01d165846842ec2425Paul Hawke                        if (read <= 0) {
3667f0727787957c2f093412c01d165846842ec2425Paul Hawke                            break;
3677f0727787957c2f093412c01d165846842ec2425Paul Hawke                        }
3687f0727787957c2f093412c01d165846842ec2425Paul Hawke                        outputStream.write(buff, 0, read);
369b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke
3707f0727787957c2f093412c01d165846842ec2425Paul Hawke                        pending -= read;
3717f0727787957c2f093412c01d165846842ec2425Paul Hawke                    }
3727f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3737f0727787957c2f093412c01d165846842ec2425Paul Hawke                outputStream.flush();
3747f0727787957c2f093412c01d165846842ec2425Paul Hawke                outputStream.close();
3757f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (data != null)
3767f0727787957c2f093412c01d165846842ec2425Paul Hawke                    data.close();
3777f0727787957c2f093412c01d165846842ec2425Paul Hawke            } catch (IOException ioe) {
3787f0727787957c2f093412c01d165846842ec2425Paul Hawke                // Couldn't write? No can do.
3797f0727787957c2f093412c01d165846842ec2425Paul Hawke            }
3807f0727787957c2f093412c01d165846842ec2425Paul Hawke        }
3817f0727787957c2f093412c01d165846842ec2425Paul Hawke
3827f0727787957c2f093412c01d165846842ec2425Paul Hawke        /**
3839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Some HTTP response status codes
3849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
3859788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public enum Status {
3869788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            OK(200, "OK"), CREATED(201, "Created"), NO_CONTENT(204, "No Content"), PARTIAL_CONTENT(206, "Partial Content"), REDIRECT(301,
3879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    "Moved Permanently"), NOT_MODIFIED(304, "Not Modified"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401,
3889788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    "Unauthorized"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), RANGE_NOT_SATISFIABLE(416,
3899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    "Requested Range Not Satisfiable"), INTERNAL_ERROR(500, "Internal Server Error");
3909d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke            private final int requestStatus;
3919d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke            private final String description;
3929788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3939d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke            Status(int requestStatus, String description) {
3949788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                this.requestStatus = requestStatus;
3959d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke                this.description = description;
3969788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
3979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3989788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            public int getRequestStatus() {
3999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                return this.requestStatus;
4009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
4019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
4029788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            public String getDescription() {
4039d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke                return "" + this.requestStatus + " " + description;
4049788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
4059788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
4069788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    }
407269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
4086200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public static class DefaultTempFile implements TempFile {
4096200253df7b934c807eb3436b51a3b039406457dPaul Hawke        private File file;
4106200253df7b934c807eb3436b51a3b039406457dPaul Hawke        private OutputStream fstream;
4116200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4126200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public DefaultTempFile(String tempdir) throws IOException {
4136200253df7b934c807eb3436b51a3b039406457dPaul Hawke            file = File.createTempFile("NanoHTTPD-", "", new File(tempdir));
4146200253df7b934c807eb3436b51a3b039406457dPaul Hawke            fstream = new FileOutputStream(file);
4156200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4166200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4176200253df7b934c807eb3436b51a3b039406457dPaul Hawke        @Override
4186200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public OutputStream open() throws Exception {
4196200253df7b934c807eb3436b51a3b039406457dPaul Hawke            return fstream;
4206200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4216200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4226200253df7b934c807eb3436b51a3b039406457dPaul Hawke        @Override
4236200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public void delete() throws Exception {
4246200253df7b934c807eb3436b51a3b039406457dPaul Hawke            file.delete();
4256200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4266200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4276200253df7b934c807eb3436b51a3b039406457dPaul Hawke        @Override
4286200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public String getName() {
4296200253df7b934c807eb3436b51a3b039406457dPaul Hawke            return file.getAbsolutePath();
4306200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4316200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
4326200253df7b934c807eb3436b51a3b039406457dPaul Hawke
433d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke    /**
434d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Handles one session, i.e. parses the HTTP request and returns the response.
435d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     */
436f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    protected class HTTPSession implements Runnable {
437f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        public static final int BUFSIZE = 8192;
4384e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        private final TempFileManager tempFileManager;
4399d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private final InputStream inputStream;
4409d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private final OutputStream outputStream;
4419788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
4426200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
4436200253df7b934c807eb3436b51a3b039406457dPaul Hawke            this.tempFileManager = tempFileManager;
4447f0727787957c2f093412c01d165846842ec2425Paul Hawke            this.inputStream = inputStream;
4456200253df7b934c807eb3436b51a3b039406457dPaul Hawke            this.outputStream = outputStream;
446d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke        }
447d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
448d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke        @Override
449d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke        public void run() {
450d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke            try {
4517f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (inputStream == null) {
452d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    return;
4536200253df7b934c807eb3436b51a3b039406457dPaul Hawke                }
454d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
455d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Read the first 8192 bytes.
456d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // The full header should fit in here.
457d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Apache's default header limit is 8KB.
458d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Do NOT assume that a single read will get the entire header at once!
4597f0727787957c2f093412c01d165846842ec2425Paul Hawke                byte[] buf = new byte[BUFSIZE];
460d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                int splitbyte = 0;
461d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                int rlen = 0;
462d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                {
4637f0727787957c2f093412c01d165846842ec2425Paul Hawke                    int read = inputStream.read(buf, 0, BUFSIZE);
464d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    while (read > 0) {
465d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                        rlen += read;
466d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                        splitbyte = findHeaderEnd(buf, rlen);
467d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                        if (splitbyte > 0)
468d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                            break;
4697f0727787957c2f093412c01d165846842ec2425Paul Hawke                        read = inputStream.read(buf, rlen, BUFSIZE - rlen);
470d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    }
471d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                }
472d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
473d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Create a BufferedReader for parsing the header.
4747f0727787957c2f093412c01d165846842ec2425Paul Hawke                BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
4757f0727787957c2f093412c01d165846842ec2425Paul Hawke                Map<String, String> pre = new HashMap<String, String>();
476d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                Map<String, String> parms = new HashMap<String, String>();
477d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                Map<String, String> header = new HashMap<String, String>();
478d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                Map<String, String> files = new HashMap<String, String>();
479d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
480d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Decode the header into parms and header java properties
481d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                decodeHeader(hin, pre, parms, header);
4827f0727787957c2f093412c01d165846842ec2425Paul Hawke                Method method = Method.lookup(pre.get("method"));
4830cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (method == null) {
4847f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
4857f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
486d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                }
4877f0727787957c2f093412c01d165846842ec2425Paul Hawke                String uri = pre.get("uri");
4887f0727787957c2f093412c01d165846842ec2425Paul Hawke                long size = extractContentLength(header);
489d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
490d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Write the part of body already read to ByteArrayOutputStream f
491dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                RandomAccessFile f = getTmpBucket();
4920cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (splitbyte < rlen) {
493d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    f.write(buf, splitbyte, rlen - splitbyte);
4940cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
495d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
496d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // While Firefox sends on the first read all the data fitting
497d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // our buffer, Chrome and Opera send only the headers even if
498d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // there is data for the body. We do some magic here to find
499d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // out whether we have already consumed part of body, if we
500d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // have reached the end of the data to be sent or we should
501d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // expect the first byte of the body at the next read.
5020cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (splitbyte < rlen) {
503d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    size -= rlen - splitbyte + 1;
5040cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                } else if (splitbyte == 0 || size == 0x7FFFFFFFFFFFFFFFl) {
505d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    size = 0;
5060cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
507d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
508d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Now read all the body and write it to f
509d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                buf = new byte[512];
510d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                while (rlen >= 0 && size > 0) {
5117f0727787957c2f093412c01d165846842ec2425Paul Hawke                    rlen = inputStream.read(buf, 0, 512);
512269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    size -= rlen;
5130cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                    if (rlen > 0) {
514269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        f.write(buf, 0, rlen);
5150cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                    }
516269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
517269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
518269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Get the raw body as a byte []
519dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                ByteBuffer fbuf = f.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f.length());
520dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                f.seek(0);
521269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
522269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Create a BufferedReader for easily reading it as string.
523dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                InputStream bin = new FileInputStream(f.getFD());
524269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                BufferedReader in = new BufferedReader(new InputStreamReader(bin));
525269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
526269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // If the method is POST, there may be parameters
527269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // in data section, too, read it:
528df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke                if (Method.POST.equals(method)) {
529269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    String contentType = "";
530269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    String contentTypeHeader = header.get("content-type");
5312b54eda5fee8430268a99d73b061e790362db544Tom Hermann
5322c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                    StringTokenizer st = null;
5332c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                    if (contentTypeHeader != null) {
5342c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                        st = new StringTokenizer(contentTypeHeader, ",; ");
5352c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                        if (st.hasMoreTokens()) {
5362c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                            contentType = st.nextToken();
5372c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                        }
538269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
539269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
5400277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke                    if ("multipart/form-data".equalsIgnoreCase(contentType)) {
541269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        // Handle multipart/form-data
5420cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                        if (!st.hasMoreTokens()) {
5437f0727787957c2f093412c01d165846842ec2425Paul Hawke                            Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
5447f0727787957c2f093412c01d165846842ec2425Paul Hawke                            throw new InterruptedException();
5450cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                        }
5462b54eda5fee8430268a99d73b061e790362db544Tom Hermann
5474e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        String boundaryStartString = "boundary=";
5482b54eda5fee8430268a99d73b061e790362db544Tom Hermann                        int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length();
5494e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length());
5504e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        if (boundary.startsWith("\"") && boundary.startsWith("\"")) {
5514e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                            boundary = boundary.substring(1, boundary.length() - 1);
5524e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        }
553269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
554269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        decodeMultipartData(boundary, fbuf, in, parms, files);
555269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    } else {
556269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        // Handle application/x-www-form-urlencoded
557269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String postLine = "";
558269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        char pbuf[] = new char[512];
559269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        int read = in.read(pbuf);
560269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        while (read >= 0 && !postLine.endsWith("\r\n")) {
561269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            postLine += String.valueOf(pbuf, 0, read);
562269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            read = in.read(pbuf);
563269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
564269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        postLine = postLine.trim();
565269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        decodeParms(postLine, parms);
566269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
567b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                } else if (Method.PUT.equals(method)) {
568dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    files.put("content", saveTmpFile(fbuf, 0, fbuf.limit()));
569b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                }
570269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
571269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Ok, now do the serve()
572269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                Response r = serve(uri, method, header, parms, files);
5739788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                if (r == null) {
5747f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
5757f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
5769788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                } else {
577b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    r.setRequestMethod(method);
5787f0727787957c2f093412c01d165846842ec2425Paul Hawke                    r.send(outputStream);
5799788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                }
580269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
581269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                in.close();
5827f0727787957c2f093412c01d165846842ec2425Paul Hawke                inputStream.close();
583269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (IOException ioe) {
584269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                try {
5857f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
5867f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
5877b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                } catch (Throwable ignored) {
588269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
589269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (InterruptedException ie) {
590269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Thrown by sendError, ignore and exit the thread.
5914e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            } finally {
5924e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                tempFileManager.clear();
593269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
594269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
595269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
5967f0727787957c2f093412c01d165846842ec2425Paul Hawke        private long extractContentLength(Map<String, String> header) {
5977f0727787957c2f093412c01d165846842ec2425Paul Hawke            long size = 0x7FFFFFFFFFFFFFFFl;
5987f0727787957c2f093412c01d165846842ec2425Paul Hawke            String contentLength = header.get("content-length");
5997f0727787957c2f093412c01d165846842ec2425Paul Hawke            if (contentLength != null) {
6007f0727787957c2f093412c01d165846842ec2425Paul Hawke                try {
6017f0727787957c2f093412c01d165846842ec2425Paul Hawke                    size = Integer.parseInt(contentLength);
6027f0727787957c2f093412c01d165846842ec2425Paul Hawke                } catch (NumberFormatException ex) {
6037f0727787957c2f093412c01d165846842ec2425Paul Hawke                    ex.printStackTrace();
6047f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
6057f0727787957c2f093412c01d165846842ec2425Paul Hawke            }
6067f0727787957c2f093412c01d165846842ec2425Paul Hawke            return size;
6077f0727787957c2f093412c01d165846842ec2425Paul Hawke        }
6087f0727787957c2f093412c01d165846842ec2425Paul Hawke
609269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
6107f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Decodes the sent headers and loads the data into Key/value pairs
611d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
6127f0727787957c2f093412c01d165846842ec2425Paul Hawke        private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> header)
613269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                throws InterruptedException {
614269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            try {
615269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Read the request line
616269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                String inLine = in.readLine();
6170cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (inLine == null) {
618269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    return;
6190cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
6200cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke
621269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                StringTokenizer st = new StringTokenizer(inLine);
6220cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (!st.hasMoreTokens()) {
6237f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
6247f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
6250cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
626269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
6270277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke                pre.put("method", st.nextToken());
628269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
6290cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (!st.hasMoreTokens()) {
6307f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
6317f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
6320cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
633269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
634269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                String uri = st.nextToken();
635269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
636269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Decode parameters from the URI
637269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                int qmi = uri.indexOf('?');
638269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                if (qmi >= 0) {
639269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    decodeParms(uri.substring(qmi + 1), parms);
640269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    uri = decodePercent(uri.substring(0, qmi));
6419788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                } else {
642269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    uri = decodePercent(uri);
6439788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                }
644269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
645269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // If there's another token, it's protocol version,
646269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // followed by HTTP headers. Ignore version but parse headers.
647269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // NOTE: this now forces header names lowercase since they are
648269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // case insensitive and vary by client.
649269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                if (st.hasMoreTokens()) {
650269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    String line = in.readLine();
651269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    while (line != null && line.trim().length() > 0) {
652269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        int p = line.indexOf(':');
653269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (p >= 0)
654269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim());
655269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        line = in.readLine();
656269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
657269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
658269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
659269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                pre.put("uri", uri);
660269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (IOException ioe) {
6617f0727787957c2f093412c01d165846842ec2425Paul Hawke                Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
6627f0727787957c2f093412c01d165846842ec2425Paul Hawke                throw new InterruptedException();
663269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
664269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
665269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
666269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
6677f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Decodes the Multipart Body data and put it into Key/Value pairs.
668d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
669dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
6709788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                                         Map<String, String> files) throws InterruptedException {
671269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            try {
672269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
673269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                int boundarycount = 1;
674269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                String mpline = in.readLine();
675269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                while (mpline != null) {
6767b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                    if (!mpline.contains(boundary)) {
6777f0727787957c2f093412c01d165846842ec2425Paul Hawke                        Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
6787f0727787957c2f093412c01d165846842ec2425Paul Hawke                        throw new InterruptedException();
6797b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                    }
680269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    boundarycount++;
681269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    Map<String, String> item = new HashMap<String, String>();
682269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    mpline = in.readLine();
683269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    while (mpline != null && mpline.trim().length() > 0) {
684269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        int p = mpline.indexOf(':');
685269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (p != -1) {
686269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            item.put(mpline.substring(0, p).trim().toLowerCase(), mpline.substring(p + 1).trim());
687269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
688269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        mpline = in.readLine();
689269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
690269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    if (mpline != null) {
691269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String contentDisposition = item.get("content-disposition");
692269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (contentDisposition == null) {
6937f0727787957c2f093412c01d165846842ec2425Paul Hawke                            Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
6947f0727787957c2f093412c01d165846842ec2425Paul Hawke                            throw new InterruptedException();
695269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
696269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        StringTokenizer st = new StringTokenizer(contentDisposition, "; ");
697269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        Map<String, String> disposition = new HashMap<String, String>();
698269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        while (st.hasMoreTokens()) {
699269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            String token = st.nextToken();
700269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            int p = token.indexOf('=');
701269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            if (p != -1) {
702269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                disposition.put(token.substring(0, p).trim().toLowerCase(), token.substring(p + 1).trim());
703269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            }
704269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
705269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String pname = disposition.get("name");
706269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        pname = pname.substring(1, pname.length() - 1);
707269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
708269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String value = "";
709269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (item.get("content-type") == null) {
7107b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                            while (mpline != null && !mpline.contains(boundary)) {
711269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                mpline = in.readLine();
712269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                if (mpline != null) {
713269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    int d = mpline.indexOf(boundary);
714269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    if (d == -1) {
715269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                        value += mpline;
716269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    } else {
717269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                        value += mpline.substring(0, d - 2);
718269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    }
719269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                }
720269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            }
721269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        } else {
722269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            if (boundarycount > bpositions.length) {
7237f0727787957c2f093412c01d165846842ec2425Paul Hawke                                Response.error(outputStream, Response.Status.INTERNAL_ERROR, "Error processing request");
7247f0727787957c2f093412c01d165846842ec2425Paul Hawke                                throw new InterruptedException();
725269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            }
726269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
727269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
728269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            files.put(pname, path);
729269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            value = disposition.get("filename");
730269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            value = value.substring(1, value.length() - 1);
731269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            do {
732269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                mpline = in.readLine();
7337b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                            } while (mpline != null && !mpline.contains(boundary));
734269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
735269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        parms.put(pname, value);
736269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
737269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
738269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (IOException ioe) {
7397f0727787957c2f093412c01d165846842ec2425Paul Hawke                Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
7407f0727787957c2f093412c01d165846842ec2425Paul Hawke                throw new InterruptedException();
741269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
742269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
743269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
744269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
745269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         * Find byte index separating header from body. It must be the last byte of the first two sequential new lines.
746d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
747269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        private int findHeaderEnd(final byte[] buf, int rlen) {
748269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int splitbyte = 0;
749269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            while (splitbyte + 3 < rlen) {
750269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') {
751269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    return splitbyte + 4;
752269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
753269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                splitbyte++;
754269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
755269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return 0;
756269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
757269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
758269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
759269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         * Find the byte positions where multipart boundaries start.
760d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
761dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        public int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
762269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int matchcount = 0;
763269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int matchbyte = -1;
764269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            List<Integer> matchbytes = new ArrayList<Integer>();
765dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            for (int i=0; i<b.limit(); i++) {
766dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                if (b.get(i) == boundary[matchcount]) {
767269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    if (matchcount == 0)
768269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchbyte = i;
769269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    matchcount++;
770269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    if (matchcount == boundary.length) {
771269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchbytes.add(matchbyte);
772269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchcount = 0;
773269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchbyte = -1;
774269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
775269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                } else {
776269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    i -= matchcount;
777269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    matchcount = 0;
778269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    matchbyte = -1;
779269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
780269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
781269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int[] ret = new int[matchbytes.size()];
782269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            for (int i = 0; i < ret.length; i++) {
7837b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                ret[i] = matchbytes.get(i);
784269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
785269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return ret;
786269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
787269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
788269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
789269baff79590de5abd70e8e05bf46547e4a28ee6Micah 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.
790d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
791dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private String saveTmpFile(ByteBuffer  b, int offset, int len) {
792269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            String path = "";
793269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            if (len > 0) {
794269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                try {
7954e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                    TempFile tempFile = tempFileManager.createTempFile();
796dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    ByteBuffer src = b.duplicate();
797dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    FileChannel dest = new FileOutputStream(tempFile.getName()).getChannel();
798dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    src.position(offset).limit(offset + len);
799dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    dest.write(src.slice());
8004e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                    path = tempFile.getName();
801269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                } catch (Exception e) { // Catch exception if any
802d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    System.err.println("Error: " + e.getMessage());
803269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
804269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
805269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return path;
806269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
807269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
8089d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private RandomAccessFile getTmpBucket() {
809dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            try {
810dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                TempFile tempFile = tempFileManager.createTempFile();
811dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                return new RandomAccessFile(tempFile.getName(), "rw");
812dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            } catch (Exception e) {
813dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                System.err.println("Error: " + e.getMessage());
814dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            }
815dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            return null;
816dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        }
817dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke
818269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
819269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         * It returns the offset separating multipart file headers from the file's data.
820269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         */
821dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private int stripMultipartHeaders(ByteBuffer b, int offset) {
822269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int i;
823dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            for (i=offset; i<b.limit(); i++) {
824dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                if (b.get(i) == '\r' && b.get(++i) == '\n' && b.get(++i) == '\r' && b.get(++i) == '\n') {
825269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    break;
826269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
827269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
828269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return i + 1;
829269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
830269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
831269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
8327f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
8337f0727787957c2f093412c01d165846842ec2425Paul Hawke         * adds them to given Map. NOTE: this doesn't support multiple identical keys due to the simplicity of Map.
834269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         */
8359d9bd98292f8f38cbd3450d1abe94009b281b6e7Paul Hawke        private void decodeParms(String parms, Map<String, String> p) {
836f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            if (parms == null) {
837f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                p.put(QUERY_STRING_PARAMETER, "");
838269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                return;
839f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            }
840269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
841f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            p.put(QUERY_STRING_PARAMETER, parms);
842269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            StringTokenizer st = new StringTokenizer(parms, "&");
843b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke            while (st.hasMoreTokens()) {
844b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                String e = st.nextToken();
845b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                int sep = e.indexOf('=');
846b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                if (sep >= 0) {
847b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    p.put(decodePercent(e.substring(0, sep)).trim(),
848b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                            decodePercent(e.substring(sep + 1)));
849b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                } else {
850b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke                    p.put(decodePercent(e).trim(), "");
851269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
852269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
853269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
854269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    }
8554e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8564e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
8574e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8584e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public TempFileManager create() {
8594e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            return new DefaultTempFileManager();
8604e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8614e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
8624e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
863dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke    public static class DefaultTempFileManager implements TempFileManager {
8644e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        private final String tmpdir;
8654e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        private final List<TempFile> tempFiles;
8664e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
867dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        public DefaultTempFileManager() {
8684e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tmpdir = System.getProperty("java.io.tmpdir");
8694e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tempFiles = new ArrayList<TempFile>();
8704e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8714e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8724e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8734e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public TempFile createTempFile() throws Exception {
8744e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            DefaultTempFile tempFile = new DefaultTempFile(tmpdir);
8754e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tempFiles.add(tempFile);
8764e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            return tempFile;
8774e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8784e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8794e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8804e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public void clear() {
8814e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            for (TempFile file : tempFiles) {
8824e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                try {
8834e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                    file.delete();
8846200253df7b934c807eb3436b51a3b039406457dPaul Hawke                } catch (Exception ignored) {
8856200253df7b934c807eb3436b51a3b039406457dPaul Hawke                }
8864e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            }
8874e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tempFiles.clear();
8884e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8894e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
8904e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8914e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private class DefaultAsyncRunner implements AsyncRunner {
8921ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke        private long requestCount;
8934e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8944e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public void exec(Runnable code) {
8951ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke            ++requestCount;
8964e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            Thread t = new Thread(code);
8974e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            t.setDaemon(true);
8981ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke            t.setName("NanoHttpd Request Processor (#" + requestCount + ")");
8994e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            t.start();
9004e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
9014e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
902269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke}
903