NanoHTTPD.java revision 1ca771092a5a39cfea9c4cf843ba7f7c90a1dbcc
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/>
162b54eda5fee8430268a99d73b061e790362db544Tom Hermann * NanoHTTPD version 1.25, Copyright &copy; 2001,2005-2012 Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/) and Copyright &copy; 2010
172b54eda5fee8430268a99d73b061e790362db544Tom Hermann * Konstantinos Togias (info@ktogias.gr, http://ktogias.gr)
1801ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <p/>
1901ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * Uplifted to Java5 by Micah Hainline and Paul Hawke (paul.hawke@gmail.com).
20d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
21d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
22269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b>
23269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul>
24d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
25269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li>
2601ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li>
27269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li>
28269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li>
29269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li>
30269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li>
31269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li>
32269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li>
33269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li>
34269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li>
35269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li>
36269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li>
37269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li>
38269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li>
39269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li>
40269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li>
41269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li>
42269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li>
43269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Contains a built-in list of most common mime types</li>
44269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>All header names are converted lowercase so they don't vary between browsers/clients</li>
45d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
46269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul>
47d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
48d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
4901ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b>
50269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul>
51d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
5201ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li>
53d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
54269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul>
55d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/>
56269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * See the end of the source file for distribution license (Modified BSD licence)
57269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */
58d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD {
59f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    /*
60f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
61f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     */
62f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    public static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
63269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    /**
64269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     * Common mime types for dynamic content
65269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
66269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public static final String MIME_PLAINTEXT = "text/plain";
67269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public static final String MIME_HTML = "text/html";
68269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public static final String MIME_DEFAULT_BINARY = "application/octet-stream";
69f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke    private final String hostname;
707b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    private final int myPort;
717b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    private ServerSocket myServerSocket;
727b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    private Thread myThread;
734e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private TempFileManagerFactory tempFileManagerFactory;
744e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private AsyncRunner asyncRunner;
75269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
76269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    /**
77d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Constructs an HTTP server on given port.
78269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
79df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke    public NanoHTTPD(int port) {
80f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        this(null, port);
81f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke    }
82f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke
83f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke    public NanoHTTPD(String hostname, int port) {
84f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        this.hostname = hostname;
857b88819e82b89ac3476ce060903468076a73de8fPaul Hawke        this.myPort = port;
864e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.tempFileManagerFactory = new DefaultTempFileManagerFactory();
874e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.asyncRunner = new DefaultAsyncRunner();
883f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke    }
893f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke
903f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke    /**
913f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke     * Starts the server
92d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * <p/>
93d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Throws an IOException if the socket is already in use
943f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke     */
957b88819e82b89ac3476ce060903468076a73de8fPaul Hawke    public void start() throws IOException {
96f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        myServerSocket = new ServerSocket();
97f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke        myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
98f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke
99269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        myThread = new Thread(new Runnable() {
100269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            @Override
101269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            public void run() {
1026200253df7b934c807eb3436b51a3b039406457dPaul Hawke                do {
1036200253df7b934c807eb3436b51a3b039406457dPaul Hawke                    try {
1046200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        final Socket finalAccept = myServerSocket.accept();
1056200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        InputStream inputStream = finalAccept.getInputStream();
1066200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        OutputStream outputStream = finalAccept.getOutputStream();
1076200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        TempFileManager tempFileManager = tempFileManagerFactory.create();
1086200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        final HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream);
1096200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        asyncRunner.exec(new Runnable() {
1106200253df7b934c807eb3436b51a3b039406457dPaul Hawke                            @Override
1116200253df7b934c807eb3436b51a3b039406457dPaul Hawke                            public void run() {
1126200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                session.run();
1136200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                if (finalAccept != null) {
1146200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                    try {
1156200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                        finalAccept.close();
1166200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                    } catch (IOException ignored) {
1176200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                    }
1186200253df7b934c807eb3436b51a3b039406457dPaul Hawke                                }
1196200253df7b934c807eb3436b51a3b039406457dPaul Hawke                            }
1206200253df7b934c807eb3436b51a3b039406457dPaul Hawke                        });
1216200253df7b934c807eb3436b51a3b039406457dPaul Hawke                    } catch (IOException e) {
1226200253df7b934c807eb3436b51a3b039406457dPaul Hawke                    }
1236200253df7b934c807eb3436b51a3b039406457dPaul Hawke                } while (true);
124269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
125269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        });
126269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        myThread.setDaemon(true);
1271ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke        myThread.setName("NanoHttpd Main Listener");
128269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        myThread.start();
129269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    }
130d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
131269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    /**
132269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     * Stops the server.
133269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
134269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    public void stop() {
135269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        try {
136269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            myServerSocket.close();
137269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            myThread.join();
138269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        } catch (IOException ioe) {
1397b88819e82b89ac3476ce060903468076a73de8fPaul Hawke            ioe.printStackTrace();
140269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        } catch (InterruptedException e) {
1417b88819e82b89ac3476ce060903468076a73de8fPaul Hawke            e.printStackTrace();
142269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
143269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    }
144269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
1454e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
1464e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.tempFileManagerFactory = tempFileManagerFactory;
1474e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
1484e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
1494e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    public void setAsyncRunner(AsyncRunner asyncRunner) {
1504e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        this.asyncRunner = asyncRunner;
1514e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
1524e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
153a6619fe65dd3a997960724faf7c51685de793f91Paul Hawke    /**
154d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Override this to customize the server.
155d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * <p/>
156d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * <p/>
157d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * (By default, this delegates to serveFile() and allows directory listing.)
1589788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     *
1599788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param uri    Percent-decoded URI without parameters, for example "/index.cgi"
1609788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param method "GET", "POST" etc.
1619788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param parms  Parsed, percent decoded parameters from URI and, in case of POST, data.
1629788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * @param header Header entries, percent decoded
163d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * @return HTTP response, see class Response for details
164269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke     */
1652b54eda5fee8430268a99d73b061e790362db544Tom Hermann    public abstract Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms,
1669788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                                   Map<String, String> files);
1679788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
168f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    /**
169f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     * Decodes the percent encoding scheme. <br/>
170f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     * For example: "an+example%20string" -> "an example string"
171f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke     */
172bcb535ea1fc2167a1e55ee5a6d8a0e7492b81605Paul Hawke    protected String decodePercent(String str) throws InterruptedException {
173f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        try {
174f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            StringBuilder sb = new StringBuilder();
175f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            for (int i = 0; i < str.length(); i++) {
176f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                char c = str.charAt(i);
177f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                switch (c) {
178f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    case '+':
179f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        sb.append(' ');
180f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        break;
181f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    case '%':
182f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        sb.append((char) Integer.parseInt(str.substring(i + 1, i + 3), 16));
183f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        i += 2;
184f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        break;
185f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    default:
186f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        sb.append(c);
187f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        break;
188f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                }
189f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            }
190f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            return sb.toString();
191f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        } catch (Exception e) {
192f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            throw new InterruptedException();
193f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        }
194f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    }
195f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke
196f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    protected Map<String, List<String>> decodeParameters(Map<String, String> parms) {
197f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        return this.decodeParameters(parms.get(QUERY_STRING_PARAMETER));
198f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    }
199f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke
200f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    protected Map<String, List<String>> decodeParameters(String queryString) {
201f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        Map<String, List<String>> parms = new HashMap<String, List<String>>();
202f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        if (queryString != null) {
203f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            StringTokenizer st = new StringTokenizer(queryString, "&");
204f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            while (st.hasMoreTokens()) {
205f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                String e = st.nextToken();
206f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                int sep = e.indexOf('=');
207f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                try {
208f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
209f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    if (!parms.containsKey(propertyName)) {
210f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        parms.put(propertyName, new ArrayList<String>());
211f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    }
212f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null;
213f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    if (propertyValue != null) {
214f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        parms.get(propertyName).add(propertyValue);
215f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    }
216f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                } catch (InterruptedException e1) {
217f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    e1.printStackTrace();
218f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                }
219f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            }
220f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        }
221f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        return parms;
222f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    }
223f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke
2249788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    public enum Method {
2259788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        GET, PUT, POST, DELETE;
2269788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2279788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        static Method lookup(String method) {
2289788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            for (Method m : Method.values()) {
2299788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                if (m.toString().equalsIgnoreCase(method)) {
2309788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    return m;
2319788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                }
2329788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
2339788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            return null;
2349788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
2359788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    }
2369788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2376200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface AsyncRunner {
2386200253df7b934c807eb3436b51a3b039406457dPaul Hawke        void exec(Runnable code);
2396200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2406200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2416200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface TempFileManagerFactory {
2426200253df7b934c807eb3436b51a3b039406457dPaul Hawke        TempFileManager create();
2436200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2446200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2456200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface TempFileManager {
2466200253df7b934c807eb3436b51a3b039406457dPaul Hawke        TempFile createTempFile() throws Exception;
2476200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2486200253df7b934c807eb3436b51a3b039406457dPaul Hawke        void clear();
2496200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2506200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2516200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public interface TempFile {
2526200253df7b934c807eb3436b51a3b039406457dPaul Hawke        OutputStream open() throws Exception;
2536200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2546200253df7b934c807eb3436b51a3b039406457dPaul Hawke        void delete() throws Exception;
2556200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2566200253df7b934c807eb3436b51a3b039406457dPaul Hawke        String getName();
2576200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
2586200253df7b934c807eb3436b51a3b039406457dPaul Hawke
2599788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    /**
2609788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     * HTTP response. Return one of these from serve().
2619788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke     */
2629788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    public static class Response {
2639788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * HTTP status code after processing, e.g. "200 OK", HTTP_OK
2659788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2669788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Status status;
2679788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2689788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * MIME type of content, e.g. "text/html"
2699788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2709788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public String mimeType;
2719788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2729788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Data of the response, may be null.
2739788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2749788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public InputStream data;
2759788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2769788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Headers for the HTTP response. Use addHeader() to add lines.
2779788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2789788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Map<String, String> header = new HashMap<String, String>();
2799788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2809788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2819788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message
2829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Response(String msg) {
2849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this(Status.OK, MIME_HTML, msg);
2859788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
2869788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2889788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Basic constructor.
2899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2909788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Response(Status status, String mimeType, InputStream data) {
2919788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.status = status;
2929788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.mimeType = mimeType;
2939788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.data = data;
2949788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
2959788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
2969788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
2979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Convenience method that makes an InputStream out of given text.
2989788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
2999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public Response(Status status, String mimeType, String txt) {
3009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.status = status;
3019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            this.mimeType = mimeType;
3029788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            try {
3039788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                this.data = new ByteArrayInputStream(txt.getBytes("UTF-8"));
3049788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            } catch (java.io.UnsupportedEncodingException uee) {
3059788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                uee.printStackTrace();
3069788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
3079788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
3089788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
309f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        public static void error(OutputStream outputStream, Status error, String message) {
310f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            new Response(error, MIME_PLAINTEXT, message).send(outputStream);
311f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        }
312f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke
3139788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
3149788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Adds given line to the header.
3159788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
3169788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public void addHeader(String name, String value) {
3179788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            header.put(name, value);
3189788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
3199788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3209788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        /**
3217f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Sends given response to the socket.
3227f0727787957c2f093412c01d165846842ec2425Paul Hawke         */
3237f0727787957c2f093412c01d165846842ec2425Paul Hawke        private void send(OutputStream outputStream) {
3247f0727787957c2f093412c01d165846842ec2425Paul Hawke            String mime = mimeType;
3257f0727787957c2f093412c01d165846842ec2425Paul Hawke            SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
3267f0727787957c2f093412c01d165846842ec2425Paul Hawke            gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
3277f0727787957c2f093412c01d165846842ec2425Paul Hawke
3287f0727787957c2f093412c01d165846842ec2425Paul Hawke            try {
3297f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (status == null) {
3307f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new Error("sendResponse(): Status can't be null.");
3317f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3327f0727787957c2f093412c01d165846842ec2425Paul Hawke                PrintWriter pw = new PrintWriter(outputStream);
3337f0727787957c2f093412c01d165846842ec2425Paul Hawke                pw.print("HTTP/1.0 " + status.getDescription() + " \r\n");
3347f0727787957c2f093412c01d165846842ec2425Paul Hawke
3357f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (mime != null) {
3367f0727787957c2f093412c01d165846842ec2425Paul Hawke                    pw.print("Content-Type: " + mime + "\r\n");
3377f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3387f0727787957c2f093412c01d165846842ec2425Paul Hawke
3397f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (header == null || header.get("Date") == null) {
3407f0727787957c2f093412c01d165846842ec2425Paul Hawke                    pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n");
3417f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3427f0727787957c2f093412c01d165846842ec2425Paul Hawke
3437f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (header != null) {
3447f0727787957c2f093412c01d165846842ec2425Paul Hawke                    for (String key : header.keySet()) {
3457f0727787957c2f093412c01d165846842ec2425Paul Hawke                        String value = header.get(key);
3467f0727787957c2f093412c01d165846842ec2425Paul Hawke                        pw.print(key + ": " + value + "\r\n");
3477f0727787957c2f093412c01d165846842ec2425Paul Hawke                    }
3487f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3497f0727787957c2f093412c01d165846842ec2425Paul Hawke
3507f0727787957c2f093412c01d165846842ec2425Paul Hawke                pw.print("\r\n");
3517f0727787957c2f093412c01d165846842ec2425Paul Hawke                pw.flush();
3527f0727787957c2f093412c01d165846842ec2425Paul Hawke
3537f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (data != null) {
3547f0727787957c2f093412c01d165846842ec2425Paul Hawke                    int pending = data.available(); // This is to support partial sends, see serveFile()
3557f0727787957c2f093412c01d165846842ec2425Paul Hawke                    int BUFFER_SIZE = 16 * 1024;
3567f0727787957c2f093412c01d165846842ec2425Paul Hawke                    byte[] buff = new byte[BUFFER_SIZE];
3577f0727787957c2f093412c01d165846842ec2425Paul Hawke                    while (pending > 0) {
3587f0727787957c2f093412c01d165846842ec2425Paul Hawke                        int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending));
3597f0727787957c2f093412c01d165846842ec2425Paul Hawke                        if (read <= 0) {
3607f0727787957c2f093412c01d165846842ec2425Paul Hawke                            break;
3617f0727787957c2f093412c01d165846842ec2425Paul Hawke                        }
3627f0727787957c2f093412c01d165846842ec2425Paul Hawke                        outputStream.write(buff, 0, read);
3637f0727787957c2f093412c01d165846842ec2425Paul Hawke                        pending -= read;
3647f0727787957c2f093412c01d165846842ec2425Paul Hawke                    }
3657f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
3667f0727787957c2f093412c01d165846842ec2425Paul Hawke                outputStream.flush();
3677f0727787957c2f093412c01d165846842ec2425Paul Hawke                outputStream.close();
3687f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (data != null)
3697f0727787957c2f093412c01d165846842ec2425Paul Hawke                    data.close();
3707f0727787957c2f093412c01d165846842ec2425Paul Hawke            } catch (IOException ioe) {
3717f0727787957c2f093412c01d165846842ec2425Paul Hawke                // Couldn't write? No can do.
3727f0727787957c2f093412c01d165846842ec2425Paul Hawke            }
3737f0727787957c2f093412c01d165846842ec2425Paul Hawke        }
3747f0727787957c2f093412c01d165846842ec2425Paul Hawke
3757f0727787957c2f093412c01d165846842ec2425Paul Hawke        /**
3769788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         * Some HTTP response status codes
3779788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke         */
3789788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        public enum Status {
3799788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            OK(200, "OK"), CREATED(201, "Created"), NO_CONTENT(204, "No Content"), PARTIAL_CONTENT(206, "Partial Content"), REDIRECT(301,
3809788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    "Moved Permanently"), NOT_MODIFIED(304, "Not Modified"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401,
3819788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    "Unauthorized"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), RANGE_NOT_SATISFIABLE(416,
3829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                    "Requested Range Not Satisfiable"), INTERNAL_ERROR(500, "Internal Server Error");
3839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            private int requestStatus;
3849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            private String descr;
3859788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3869788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            Status(int requestStatus, String descr) {
3879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                this.requestStatus = requestStatus;
3889788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                this.descr = descr;
3899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
3909788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3919788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            public int getRequestStatus() {
3929788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                return this.requestStatus;
3939788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
3949788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
3959788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            public String getDescription() {
3969788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                return "" + this.requestStatus + " " + descr;
3979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke            }
3989788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke        }
3999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke    }
400269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
4016200253df7b934c807eb3436b51a3b039406457dPaul Hawke    public static class DefaultTempFile implements TempFile {
4026200253df7b934c807eb3436b51a3b039406457dPaul Hawke        private File file;
4036200253df7b934c807eb3436b51a3b039406457dPaul Hawke        private OutputStream fstream;
4046200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4056200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public DefaultTempFile(String tempdir) throws IOException {
4066200253df7b934c807eb3436b51a3b039406457dPaul Hawke            file = File.createTempFile("NanoHTTPD-", "", new File(tempdir));
4076200253df7b934c807eb3436b51a3b039406457dPaul Hawke            fstream = new FileOutputStream(file);
4086200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4096200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4106200253df7b934c807eb3436b51a3b039406457dPaul Hawke        @Override
4116200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public OutputStream open() throws Exception {
4126200253df7b934c807eb3436b51a3b039406457dPaul Hawke            return fstream;
4136200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4146200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4156200253df7b934c807eb3436b51a3b039406457dPaul Hawke        @Override
4166200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public void delete() throws Exception {
4176200253df7b934c807eb3436b51a3b039406457dPaul Hawke            file.delete();
4186200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4196200253df7b934c807eb3436b51a3b039406457dPaul Hawke
4206200253df7b934c807eb3436b51a3b039406457dPaul Hawke        @Override
4216200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public String getName() {
4226200253df7b934c807eb3436b51a3b039406457dPaul Hawke            return file.getAbsolutePath();
4236200253df7b934c807eb3436b51a3b039406457dPaul Hawke        }
4246200253df7b934c807eb3436b51a3b039406457dPaul Hawke    }
4256200253df7b934c807eb3436b51a3b039406457dPaul Hawke
426d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke    /**
427d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     * Handles one session, i.e. parses the HTTP request and returns the response.
428d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke     */
429f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke    protected class HTTPSession implements Runnable {
430f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke        public static final int BUFSIZE = 8192;
4314e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        private final TempFileManager tempFileManager;
4327f0727787957c2f093412c01d165846842ec2425Paul Hawke        private InputStream inputStream;
4336200253df7b934c807eb3436b51a3b039406457dPaul Hawke        private OutputStream outputStream;
4349788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke
4356200253df7b934c807eb3436b51a3b039406457dPaul Hawke        public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
4366200253df7b934c807eb3436b51a3b039406457dPaul Hawke            this.tempFileManager = tempFileManager;
4377f0727787957c2f093412c01d165846842ec2425Paul Hawke            this.inputStream = inputStream;
4386200253df7b934c807eb3436b51a3b039406457dPaul Hawke            this.outputStream = outputStream;
439d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke        }
440d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
441d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke        @Override
442d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke        public void run() {
443d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke            try {
4447f0727787957c2f093412c01d165846842ec2425Paul Hawke                if (inputStream == null) {
445d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    return;
4466200253df7b934c807eb3436b51a3b039406457dPaul Hawke                }
447d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
448d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Read the first 8192 bytes.
449d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // The full header should fit in here.
450d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Apache's default header limit is 8KB.
451d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Do NOT assume that a single read will get the entire header at once!
4527f0727787957c2f093412c01d165846842ec2425Paul Hawke                byte[] buf = new byte[BUFSIZE];
453d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                int splitbyte = 0;
454d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                int rlen = 0;
455d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                {
4567f0727787957c2f093412c01d165846842ec2425Paul Hawke                    int read = inputStream.read(buf, 0, BUFSIZE);
457d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    while (read > 0) {
458d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                        rlen += read;
459d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                        splitbyte = findHeaderEnd(buf, rlen);
460d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                        if (splitbyte > 0)
461d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                            break;
4627f0727787957c2f093412c01d165846842ec2425Paul Hawke                        read = inputStream.read(buf, rlen, BUFSIZE - rlen);
463d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    }
464d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                }
465d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
466d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Create a BufferedReader for parsing the header.
4677f0727787957c2f093412c01d165846842ec2425Paul Hawke                BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
4687f0727787957c2f093412c01d165846842ec2425Paul Hawke                Map<String, String> pre = new HashMap<String, String>();
469d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                Map<String, String> parms = new HashMap<String, String>();
470d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                Map<String, String> header = new HashMap<String, String>();
471d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                Map<String, String> files = new HashMap<String, String>();
472d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
473d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Decode the header into parms and header java properties
474d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                decodeHeader(hin, pre, parms, header);
4757f0727787957c2f093412c01d165846842ec2425Paul Hawke                Method method = Method.lookup(pre.get("method"));
4760cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (method == null) {
4777f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
4787f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
479d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                }
4807f0727787957c2f093412c01d165846842ec2425Paul Hawke                String uri = pre.get("uri");
4817f0727787957c2f093412c01d165846842ec2425Paul Hawke                long size = extractContentLength(header);
482d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
483d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Write the part of body already read to ByteArrayOutputStream f
484dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                RandomAccessFile f = getTmpBucket();
4850cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (splitbyte < rlen) {
486d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    f.write(buf, splitbyte, rlen - splitbyte);
4870cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
488d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
489d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // While Firefox sends on the first read all the data fitting
490d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // our buffer, Chrome and Opera send only the headers even if
491d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // there is data for the body. We do some magic here to find
492d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // out whether we have already consumed part of body, if we
493d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // have reached the end of the data to be sent or we should
494d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // expect the first byte of the body at the next read.
4950cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (splitbyte < rlen) {
496d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    size -= rlen - splitbyte + 1;
4970cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                } else if (splitbyte == 0 || size == 0x7FFFFFFFFFFFFFFFl) {
498d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    size = 0;
4990cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
500d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke
501d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                // Now read all the body and write it to f
502d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                buf = new byte[512];
503d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                while (rlen >= 0 && size > 0) {
5047f0727787957c2f093412c01d165846842ec2425Paul Hawke                    rlen = inputStream.read(buf, 0, 512);
505269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    size -= rlen;
5060cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                    if (rlen > 0) {
507269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        f.write(buf, 0, rlen);
5080cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                    }
509269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
510269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
511269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Get the raw body as a byte []
512dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                ByteBuffer fbuf = f.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f.length());
513dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                f.seek(0);
514269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
515269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Create a BufferedReader for easily reading it as string.
516dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                InputStream bin = new FileInputStream(f.getFD());
517269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                BufferedReader in = new BufferedReader(new InputStreamReader(bin));
518269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
519269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // If the method is POST, there may be parameters
520269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // in data section, too, read it:
521df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke                if (Method.POST.equals(method)) {
522269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    String contentType = "";
523269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    String contentTypeHeader = header.get("content-type");
5242b54eda5fee8430268a99d73b061e790362db544Tom Hermann
5252c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                    StringTokenizer st = null;
5262c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                    if (contentTypeHeader != null) {
5272c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                        st = new StringTokenizer(contentTypeHeader, ",; ");
5282c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                        if (st.hasMoreTokens()) {
5292c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                            contentType = st.nextToken();
5302c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke                        }
531269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
532269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
5330277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke                    if ("multipart/form-data".equalsIgnoreCase(contentType)) {
534269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        // Handle multipart/form-data
5350cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                        if (!st.hasMoreTokens()) {
5367f0727787957c2f093412c01d165846842ec2425Paul Hawke                            Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
5377f0727787957c2f093412c01d165846842ec2425Paul Hawke                            throw new InterruptedException();
5380cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                        }
5392b54eda5fee8430268a99d73b061e790362db544Tom Hermann
5404e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        String boundaryStartString = "boundary=";
5412b54eda5fee8430268a99d73b061e790362db544Tom Hermann                        int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length();
5424e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length());
5434e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        if (boundary.startsWith("\"") && boundary.startsWith("\"")) {
5444e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                            boundary = boundary.substring(1, boundary.length() - 1);
5454e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                        }
546269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
547269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        decodeMultipartData(boundary, fbuf, in, parms, files);
548269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    } else {
549269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        // Handle application/x-www-form-urlencoded
550269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String postLine = "";
551269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        char pbuf[] = new char[512];
552269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        int read = in.read(pbuf);
553269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        while (read >= 0 && !postLine.endsWith("\r\n")) {
554269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            postLine += String.valueOf(pbuf, 0, read);
555269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            read = in.read(pbuf);
556269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
557269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        postLine = postLine.trim();
558269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        decodeParms(postLine, parms);
559269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
560269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
561269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
562df674ca7f11cee6336b66271c50476f389e0ff4bPaul Hawke                if (Method.PUT.equals(method))
563dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    files.put("content", saveTmpFile(fbuf, 0, fbuf.limit()));
564269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
565269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Ok, now do the serve()
566269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                Response r = serve(uri, method, header, parms, files);
5679788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                if (r == null) {
5687f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
5697f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
5709788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                } else {
5717f0727787957c2f093412c01d165846842ec2425Paul Hawke                    r.send(outputStream);
5729788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                }
573269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
574269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                in.close();
5757f0727787957c2f093412c01d165846842ec2425Paul Hawke                inputStream.close();
576269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (IOException ioe) {
577269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                try {
5787f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
5797f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
5807b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                } catch (Throwable ignored) {
581269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
582269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (InterruptedException ie) {
583269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Thrown by sendError, ignore and exit the thread.
5844e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            } finally {
5854e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                tempFileManager.clear();
586269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
587269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
588269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
5897f0727787957c2f093412c01d165846842ec2425Paul Hawke        private long extractContentLength(Map<String, String> header) {
5907f0727787957c2f093412c01d165846842ec2425Paul Hawke            long size = 0x7FFFFFFFFFFFFFFFl;
5917f0727787957c2f093412c01d165846842ec2425Paul Hawke            String contentLength = header.get("content-length");
5927f0727787957c2f093412c01d165846842ec2425Paul Hawke            if (contentLength != null) {
5937f0727787957c2f093412c01d165846842ec2425Paul Hawke                try {
5947f0727787957c2f093412c01d165846842ec2425Paul Hawke                    size = Integer.parseInt(contentLength);
5957f0727787957c2f093412c01d165846842ec2425Paul Hawke                } catch (NumberFormatException ex) {
5967f0727787957c2f093412c01d165846842ec2425Paul Hawke                    ex.printStackTrace();
5977f0727787957c2f093412c01d165846842ec2425Paul Hawke                }
5987f0727787957c2f093412c01d165846842ec2425Paul Hawke            }
5997f0727787957c2f093412c01d165846842ec2425Paul Hawke            return size;
6007f0727787957c2f093412c01d165846842ec2425Paul Hawke        }
6017f0727787957c2f093412c01d165846842ec2425Paul Hawke
602269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
6037f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Decodes the sent headers and loads the data into Key/value pairs
604d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
6057f0727787957c2f093412c01d165846842ec2425Paul Hawke        private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> header)
606269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                throws InterruptedException {
607269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            try {
608269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Read the request line
609269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                String inLine = in.readLine();
6100cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (inLine == null) {
611269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    return;
6120cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
6130cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke
614269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                StringTokenizer st = new StringTokenizer(inLine);
6150cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (!st.hasMoreTokens()) {
6167f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
6177f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
6180cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
619269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
6200277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke                pre.put("method", st.nextToken());
621269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
6220cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                if (!st.hasMoreTokens()) {
6237f0727787957c2f093412c01d165846842ec2425Paul Hawke                    Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
6247f0727787957c2f093412c01d165846842ec2425Paul Hawke                    throw new InterruptedException();
6250cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke                }
626269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
627269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                String uri = st.nextToken();
628269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
629269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // Decode parameters from the URI
630269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                int qmi = uri.indexOf('?');
631269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                if (qmi >= 0) {
632269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    decodeParms(uri.substring(qmi + 1), parms);
633269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    uri = decodePercent(uri.substring(0, qmi));
6349788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                } else {
635269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    uri = decodePercent(uri);
6369788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                }
637269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
638269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // If there's another token, it's protocol version,
639269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // followed by HTTP headers. Ignore version but parse headers.
640269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // NOTE: this now forces header names lowercase since they are
641269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                // case insensitive and vary by client.
642269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                if (st.hasMoreTokens()) {
643269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    String line = in.readLine();
644269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    while (line != null && line.trim().length() > 0) {
645269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        int p = line.indexOf(':');
646269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (p >= 0)
647269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim());
648269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        line = in.readLine();
649269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
650269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
651269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
652269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                pre.put("uri", uri);
653269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (IOException ioe) {
6547f0727787957c2f093412c01d165846842ec2425Paul Hawke                Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
6557f0727787957c2f093412c01d165846842ec2425Paul Hawke                throw new InterruptedException();
656269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
657269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
658269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
659269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
6607f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Decodes the Multipart Body data and put it into Key/Value pairs.
661d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
662dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
6639788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke                                         Map<String, String> files) throws InterruptedException {
664269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            try {
665269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
666269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                int boundarycount = 1;
667269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                String mpline = in.readLine();
668269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                while (mpline != null) {
6697b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                    if (!mpline.contains(boundary)) {
6707f0727787957c2f093412c01d165846842ec2425Paul 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");
6717f0727787957c2f093412c01d165846842ec2425Paul Hawke                        throw new InterruptedException();
6727b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                    }
673269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    boundarycount++;
674269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    Map<String, String> item = new HashMap<String, String>();
675269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    mpline = in.readLine();
676269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    while (mpline != null && mpline.trim().length() > 0) {
677269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        int p = mpline.indexOf(':');
678269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (p != -1) {
679269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            item.put(mpline.substring(0, p).trim().toLowerCase(), mpline.substring(p + 1).trim());
680269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
681269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        mpline = in.readLine();
682269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
683269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    if (mpline != null) {
684269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String contentDisposition = item.get("content-disposition");
685269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (contentDisposition == null) {
6867f0727787957c2f093412c01d165846842ec2425Paul 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");
6877f0727787957c2f093412c01d165846842ec2425Paul Hawke                            throw new InterruptedException();
688269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
689269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        StringTokenizer st = new StringTokenizer(contentDisposition, "; ");
690269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        Map<String, String> disposition = new HashMap<String, String>();
691269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        while (st.hasMoreTokens()) {
692269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            String token = st.nextToken();
693269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            int p = token.indexOf('=');
694269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            if (p != -1) {
695269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                disposition.put(token.substring(0, p).trim().toLowerCase(), token.substring(p + 1).trim());
696269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            }
697269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
698269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String pname = disposition.get("name");
699269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        pname = pname.substring(1, pname.length() - 1);
700269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
701269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        String value = "";
702269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        if (item.get("content-type") == null) {
7037b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                            while (mpline != null && !mpline.contains(boundary)) {
704269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                mpline = in.readLine();
705269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                if (mpline != null) {
706269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    int d = mpline.indexOf(boundary);
707269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    if (d == -1) {
708269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                        value += mpline;
709269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    } else {
710269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                        value += mpline.substring(0, d - 2);
711269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                    }
712269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                }
713269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            }
714269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        } else {
715269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            if (boundarycount > bpositions.length) {
7167f0727787957c2f093412c01d165846842ec2425Paul Hawke                                Response.error(outputStream, Response.Status.INTERNAL_ERROR, "Error processing request");
7177f0727787957c2f093412c01d165846842ec2425Paul Hawke                                throw new InterruptedException();
718269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            }
719269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
720269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
721269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            files.put(pname, path);
722269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            value = disposition.get("filename");
723269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            value = value.substring(1, value.length() - 1);
724269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                            do {
725269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                                mpline = in.readLine();
7267b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                            } while (mpline != null && !mpline.contains(boundary));
727269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        }
728269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        parms.put(pname, value);
729269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
730269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
731269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            } catch (IOException ioe) {
7327f0727787957c2f093412c01d165846842ec2425Paul Hawke                Response.error(outputStream, Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
7337f0727787957c2f093412c01d165846842ec2425Paul Hawke                throw new InterruptedException();
734269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
735269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
736269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
737269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
738269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         * Find byte index separating header from body. It must be the last byte of the first two sequential new lines.
739d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
740269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        private int findHeaderEnd(final byte[] buf, int rlen) {
741269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int splitbyte = 0;
742269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            while (splitbyte + 3 < rlen) {
743269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') {
744269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    return splitbyte + 4;
745269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
746269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                splitbyte++;
747269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
748269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return 0;
749269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
750269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
751269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
752269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         * Find the byte positions where multipart boundaries start.
753d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
754dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        public int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
755269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int matchcount = 0;
756269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int matchbyte = -1;
757269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            List<Integer> matchbytes = new ArrayList<Integer>();
758dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            for (int i=0; i<b.limit(); i++) {
759dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                if (b.get(i) == boundary[matchcount]) {
760269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    if (matchcount == 0)
761269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchbyte = i;
762269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    matchcount++;
763269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    if (matchcount == boundary.length) {
764269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchbytes.add(matchbyte);
765269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchcount = 0;
766269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                        matchbyte = -1;
767269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    }
768269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                } else {
769269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    i -= matchcount;
770269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    matchcount = 0;
771269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    matchbyte = -1;
772269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
773269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
774269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int[] ret = new int[matchbytes.size()];
775269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            for (int i = 0; i < ret.length; i++) {
7767b88819e82b89ac3476ce060903468076a73de8fPaul Hawke                ret[i] = matchbytes.get(i);
777269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
778269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return ret;
779269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
780269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
781269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
782269baff79590de5abd70e8e05bf46547e4a28ee6Micah 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.
783d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke         */
784dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private String saveTmpFile(ByteBuffer  b, int offset, int len) {
785269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            String path = "";
786269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            if (len > 0) {
787269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                try {
7884e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                    TempFile tempFile = tempFileManager.createTempFile();
789dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    ByteBuffer src = b.duplicate();
790dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    FileChannel dest = new FileOutputStream(tempFile.getName()).getChannel();
791dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    src.position(offset).limit(offset + len);
792dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                    dest.write(src.slice());
7934e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                    path = tempFile.getName();
794269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                } catch (Exception e) { // Catch exception if any
795d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke                    System.err.println("Error: " + e.getMessage());
796269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
797269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
798269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return path;
799269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
800269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
801dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private RandomAccessFile getTmpBucket() throws IOException {
802dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            try {
803dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                TempFile tempFile = tempFileManager.createTempFile();
804dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                return new RandomAccessFile(tempFile.getName(), "rw");
805dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            } catch (Exception e) {
806dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                System.err.println("Error: " + e.getMessage());
807dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            }
808dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            return null;
809dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        }
810dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke
811269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
812269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         * It returns the offset separating multipart file headers from the file's data.
813269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         */
814dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        private int stripMultipartHeaders(ByteBuffer b, int offset) {
815269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            int i;
816dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke            for (i=offset; i<b.limit(); i++) {
817dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke                if (b.get(i) == '\r' && b.get(++i) == '\n' && b.get(++i) == '\r' && b.get(++i) == '\n') {
818269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                    break;
819269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
820269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
821269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            return i + 1;
822269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
823269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
824269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        /**
8257f0727787957c2f093412c01d165846842ec2425Paul Hawke         * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
8267f0727787957c2f093412c01d165846842ec2425Paul Hawke         * adds them to given Map. NOTE: this doesn't support multiple identical keys due to the simplicity of Map.
827269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke         */
828269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        private void decodeParms(String parms, Map<String, String> p) throws InterruptedException {
829f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            if (parms == null) {
830f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                p.put(QUERY_STRING_PARAMETER, "");
831269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                return;
832f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            }
833269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke
834f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            p.put(QUERY_STRING_PARAMETER, parms);
835269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            StringTokenizer st = new StringTokenizer(parms, "&");
836f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            try {
837f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                while (st.hasMoreTokens()) {
838f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    String e = st.nextToken();
839f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    int sep = e.indexOf('=');
840f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    if (sep >= 0) {
841f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        p.put(decodePercent(e.substring(0, sep)).trim(),
842f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                                decodePercent(e.substring(sep + 1)));
843f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    } else {
844f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                        p.put(decodePercent(e).trim(), "");
845f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                    }
846269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke                }
847f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke            } catch (InterruptedException e) {
848f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke                Response.error(outputStream, Response.Status.BAD_REQUEST, "BAD REQUEST: Bad percent-encoding.");
849269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke            }
850269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke        }
851269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke    }
8524e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8534e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
8544e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8554e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public TempFileManager create() {
8564e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            return new DefaultTempFileManager();
8574e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8584e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
8594e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
860dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke    public static class DefaultTempFileManager implements TempFileManager {
8614e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        private final String tmpdir;
8624e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        private final List<TempFile> tempFiles;
8634e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
864dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke        public DefaultTempFileManager() {
8654e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tmpdir = System.getProperty("java.io.tmpdir");
8664e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tempFiles = new ArrayList<TempFile>();
8674e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8684e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8694e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8704e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public TempFile createTempFile() throws Exception {
8714e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            DefaultTempFile tempFile = new DefaultTempFile(tmpdir);
8724e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tempFiles.add(tempFile);
8734e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            return tempFile;
8744e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8754e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8764e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8774e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public void clear() {
8784e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            for (TempFile file : tempFiles) {
8794e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                try {
8804e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke                    file.delete();
8816200253df7b934c807eb3436b51a3b039406457dPaul Hawke                } catch (Exception ignored) {
8826200253df7b934c807eb3436b51a3b039406457dPaul Hawke                }
8834e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            }
8844e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            tempFiles.clear();
8854e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8864e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
8874e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke
8884e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    private class DefaultAsyncRunner implements AsyncRunner {
8891ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke        private long requestCount;
8904e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        @Override
8914e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        public void exec(Runnable code) {
8921ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke            ++requestCount;
8934e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            Thread t = new Thread(code);
8944e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            t.setDaemon(true);
8951ca771092a5a39cfea9c4cf843ba7f7c90a1dbccPaul Hawke            t.setName("NanoHttpd Request Processor (#" + requestCount + ")");
8964e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke            t.start();
8974e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke        }
8984e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke    }
899269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke}
900