NanoHTTPD.java revision de2bb1bc9fa3959846741e1fd14076971e17b90c
1269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkepackage fi.iki.elonen; 2269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 38b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie/* 48b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * #%L 58b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * NanoHttpd-Core 68b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * %% 78b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * Copyright (C) 2012 - 2015 nanohttpd 88b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * %% 98b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * Redistribution and use in source and binary forms, with or without modification, 108b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * are permitted provided that the following conditions are met: 119e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 128b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 1. Redistributions of source code must retain the above copyright notice, this 138b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * list of conditions and the following disclaimer. 149e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 158b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 2. Redistributions in binary form must reproduce the above copyright notice, 168b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * this list of conditions and the following disclaimer in the documentation 178b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * and/or other materials provided with the distribution. 189e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 198b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 3. Neither the name of the nanohttpd nor the names of its contributors 208b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * may be used to endorse or promote products derived from this software without 218b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * specific prior written permission. 229e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 238b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 248b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 258b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 268b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 278b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 288b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 298b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 308b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 318b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 328b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * OF THE POSSIBILITY OF SUCH DAMAGE. 338b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * #L% 348b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie */ 358b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie 363526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.BufferedReader; 375e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport java.io.BufferedWriter; 383526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.ByteArrayInputStream; 393526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.Closeable; 403526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.File; 413526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.FileInputStream; 423526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.FileOutputStream; 433526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.IOException; 443526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.InputStream; 453526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.InputStreamReader; 463526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.OutputStream; 475e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport java.io.OutputStreamWriter; 483526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.PrintWriter; 493526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.PushbackInputStream; 503526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.RandomAccessFile; 513526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.UnsupportedEncodingException; 52b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetAddress; 53b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetSocketAddress; 54b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.ServerSocket; 55b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.Socket; 56b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.SocketException; 5713736e18ec88e3df74d055e061fb324e04778ad6Paul Hawkeimport java.net.SocketTimeoutException; 58b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.URLDecoder; 59dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.ByteBuffer; 60dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.channels.FileChannel; 6130fb85f55cbd8df3005e652da3781f51294baf90ritchieimport java.security.KeyStore; 627b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat; 63b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.ArrayList; 64b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Calendar; 65abcf1089ce1de49278970f088883cb32acb4f225ritchieimport java.util.Collections; 66b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Date; 67b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.HashMap; 68b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Iterator; 69b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.List; 70b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Locale; 71b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Map; 72b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.StringTokenizer; 73b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.TimeZone; 743526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Level; 753526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Logger; 76269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 7730fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManager; 7830fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManagerFactory; 7930fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLContext; 8030fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocket; 8130fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocketFactory; 8230fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.TrustManagerFactory; 83f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 845e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport fi.iki.elonen.NanoHTTPD.Response.IStatus; 855e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport fi.iki.elonen.NanoHTTPD.Response.Status; 865e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 87269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 885b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke * A simple, tiny, nicely embeddable HTTP server in Java 89d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 90d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 91b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke * NanoHTTPD 929058464950a9734da0a7ff2dc47f3081bbb5117critchie * <p> 939058464950a9734da0a7ff2dc47f3081bbb5117critchie * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 949058464950a9734da0a7ff2dc47f3081bbb5117critchie * 2010 by Konstantinos Togias 959058464950a9734da0a7ff2dc47f3081bbb5117critchie * </p> 96d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 97d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 98269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 99269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 100d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 101269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 10201ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li> 103269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 1049058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>No fixed config files, logging, authorization etc. (Implement yourself if 1059058464950a9734da0a7ff2dc47f3081bbb5117critchie * you need them.)</li> 1069058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT 1079058464950a9734da0a7ff2dc47f3081bbb5117critchie * support in 1.25)</li> 108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 110269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 111269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 112269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 114269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 115269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 116269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 117269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 118269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 119269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 120269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 121cf230611ca85378ae2b0e8130dace5766edfd295Philipp Wiesemann * <li>Contains a built-in list of most common MIME types</li> 1229058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>All header names are converted to lower case so they don't vary between 1239058464950a9734da0a7ff2dc47f3081bbb5117critchie * browsers/clients</li> 124d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 125269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 126d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 127d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 12801ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b> 129269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 130d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 13101ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li> 132d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 133269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 134d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 1359058464950a9734da0a7ff2dc47f3081bbb5117critchie * See the separate "LICENSE.md" file for the distribution license (Modified BSD 1369058464950a9734da0a7ff2dc47f3081bbb5117critchie * licence) 137269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 138d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD { 1399058464950a9734da0a7ff2dc47f3081bbb5117critchie 140269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 14130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 142293e04675f3d4f427c125a26e831d70d5011c79bhflicka */ 14330fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface AsyncRunner { 1449058464950a9734da0a7ff2dc47f3081bbb5117critchie 145abcf1089ce1de49278970f088883cb32acb4f225ritchie void closeAll(); 146abcf1089ce1de49278970f088883cb32acb4f225ritchie 147abcf1089ce1de49278970f088883cb32acb4f225ritchie void closed(ClientHandler clientHandler); 148abcf1089ce1de49278970f088883cb32acb4f225ritchie 149abcf1089ce1de49278970f088883cb32acb4f225ritchie void exec(ClientHandler code); 150abcf1089ce1de49278970f088883cb32acb4f225ritchie } 151abcf1089ce1de49278970f088883cb32acb4f225ritchie 152abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 153abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for every new client connection. 154abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 155abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ClientHandler implements Runnable { 156abcf1089ce1de49278970f088883cb32acb4f225ritchie 157abcf1089ce1de49278970f088883cb32acb4f225ritchie private final InputStream inputStream; 158abcf1089ce1de49278970f088883cb32acb4f225ritchie 159abcf1089ce1de49278970f088883cb32acb4f225ritchie private final Socket acceptSocket; 160abcf1089ce1de49278970f088883cb32acb4f225ritchie 161abcf1089ce1de49278970f088883cb32acb4f225ritchie private ClientHandler(InputStream inputStream, Socket acceptSocket) { 162abcf1089ce1de49278970f088883cb32acb4f225ritchie this.inputStream = inputStream; 163abcf1089ce1de49278970f088883cb32acb4f225ritchie this.acceptSocket = acceptSocket; 164abcf1089ce1de49278970f088883cb32acb4f225ritchie } 165abcf1089ce1de49278970f088883cb32acb4f225ritchie 166abcf1089ce1de49278970f088883cb32acb4f225ritchie public void close() { 167abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 168abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 169abcf1089ce1de49278970f088883cb32acb4f225ritchie } 170abcf1089ce1de49278970f088883cb32acb4f225ritchie 171abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 172abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 173abcf1089ce1de49278970f088883cb32acb4f225ritchie OutputStream outputStream = null; 174abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 175abcf1089ce1de49278970f088883cb32acb4f225ritchie outputStream = this.acceptSocket.getOutputStream(); 176abcf1089ce1de49278970f088883cb32acb4f225ritchie TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create(); 177abcf1089ce1de49278970f088883cb32acb4f225ritchie HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress()); 178abcf1089ce1de49278970f088883cb32acb4f225ritchie while (!this.acceptSocket.isClosed()) { 179abcf1089ce1de49278970f088883cb32acb4f225ritchie session.execute(); 180abcf1089ce1de49278970f088883cb32acb4f225ritchie } 181abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (Exception e) { 182abcf1089ce1de49278970f088883cb32acb4f225ritchie // When the socket is closed by the client, 183abcf1089ce1de49278970f088883cb32acb4f225ritchie // we throw our own SocketException 184abcf1089ce1de49278970f088883cb32acb4f225ritchie // to break the "keep alive" loop above. If 185abcf1089ce1de49278970f088883cb32acb4f225ritchie // the exception was anything other 186abcf1089ce1de49278970f088883cb32acb4f225ritchie // than the expected SocketException OR a 187abcf1089ce1de49278970f088883cb32acb4f225ritchie // SocketTimeoutException, print the 188abcf1089ce1de49278970f088883cb32acb4f225ritchie // stacktrace 189abcf1089ce1de49278970f088883cb32acb4f225ritchie if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) { 190abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 191abcf1089ce1de49278970f088883cb32acb4f225ritchie } 192abcf1089ce1de49278970f088883cb32acb4f225ritchie } finally { 193abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(outputStream); 194abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 195abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 196abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.closed(this); 197abcf1089ce1de49278970f088883cb32acb4f225ritchie } 198abcf1089ce1de49278970f088883cb32acb4f225ritchie } 19930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2009058464950a9734da0a7ff2dc47f3081bbb5117critchie 20130fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Cookie { 2029058464950a9734da0a7ff2dc47f3081bbb5117critchie 20330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static String getHTTPTime(int days) { 20430fb85f55cbd8df3005e652da3781f51294baf90ritchie Calendar calendar = Calendar.getInstance(); 20530fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 20630fb85f55cbd8df3005e652da3781f51294baf90ritchie dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 20730fb85f55cbd8df3005e652da3781f51294baf90ritchie calendar.add(Calendar.DAY_OF_MONTH, days); 20830fb85f55cbd8df3005e652da3781f51294baf90ritchie return dateFormat.format(calendar.getTime()); 20930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21030fb85f55cbd8df3005e652da3781f51294baf90ritchie 21130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String n, v, e; 21230fb85f55cbd8df3005e652da3781f51294baf90ritchie 21330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value) { 21430fb85f55cbd8df3005e652da3781f51294baf90ritchie this(name, value, 30); 21530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21630fb85f55cbd8df3005e652da3781f51294baf90ritchie 21730fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, int numDays) { 21830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 21930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 22030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = getHTTPTime(numDays); 22130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22230fb85f55cbd8df3005e652da3781f51294baf90ritchie 22330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, String expires) { 22430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 22530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 22630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = expires; 22730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22830fb85f55cbd8df3005e652da3781f51294baf90ritchie 22930fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHTTPHeader() { 23030fb85f55cbd8df3005e652da3781f51294baf90ritchie String fmt = "%s=%s; expires=%s"; 23130fb85f55cbd8df3005e652da3781f51294baf90ritchie return String.format(fmt, this.n, this.v, this.e); 23230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 23330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2349058464950a9734da0a7ff2dc47f3081bbb5117critchie 2359058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 23630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Provides rudimentary support for cookies. Doesn't support 'path', 23730fb85f55cbd8df3005e652da3781f51294baf90ritchie * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported 23830fb85f55cbd8df3005e652da3781f51294baf90ritchie * features. 2399e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 24030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @author LordFokas 2419058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 24230fb85f55cbd8df3005e652da3781f51294baf90ritchie public class CookieHandler implements Iterable<String> { 2439058464950a9734da0a7ff2dc47f3081bbb5117critchie 24430fb85f55cbd8df3005e652da3781f51294baf90ritchie private final HashMap<String, String> cookies = new HashMap<String, String>(); 2459058464950a9734da0a7ff2dc47f3081bbb5117critchie 24630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final ArrayList<Cookie> queue = new ArrayList<Cookie>(); 2479058464950a9734da0a7ff2dc47f3081bbb5117critchie 24830fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler(Map<String, String> httpHeaders) { 24930fb85f55cbd8df3005e652da3781f51294baf90ritchie String raw = httpHeaders.get("cookie"); 25030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (raw != null) { 25130fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] tokens = raw.split(";"); 25230fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String token : tokens) { 25330fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] data = token.trim().split("="); 25430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (data.length == 2) { 25530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.put(data[0], data[1]); 25630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 25730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 25830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 25930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2609058464950a9734da0a7ff2dc47f3081bbb5117critchie 26130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 26230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Set a cookie with an expiration date from a month ago, effectively 26330fb85f55cbd8df3005e652da3781f51294baf90ritchie * deleting it on the client side. 2649e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 26530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 26630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie name. 26730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 26830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete(String name) { 26930fb85f55cbd8df3005e652da3781f51294baf90ritchie set(name, "-delete-", -30); 27030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2719058464950a9734da0a7ff2dc47f3081bbb5117critchie 27230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 27330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Iterator<String> iterator() { 27430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.keySet().iterator(); 27530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2769058464950a9734da0a7ff2dc47f3081bbb5117critchie 27730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 27830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Read a cookie from the HTTP Headers. 2799e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 28030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 28130fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 28230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return The cookie's value if it exists, null otherwise. 28330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 28430fb85f55cbd8df3005e652da3781f51294baf90ritchie public String read(String name) { 28530fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.get(name); 28630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2879058464950a9734da0a7ff2dc47f3081bbb5117critchie 28830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(Cookie cookie) { 28930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(cookie); 29030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2919058464950a9734da0a7ff2dc47f3081bbb5117critchie 29230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 29330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sets a cookie. 2949e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 29530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 29630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 29730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param value 29830fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's value. 29930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param expires 30030fb85f55cbd8df3005e652da3781f51294baf90ritchie * How many days until the cookie expires. 30130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 30230fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(String name, String value, int expires) { 30330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); 30430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 305269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 30630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 30730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Internally used by the webserver to add all queued cookies into the 30830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Response's HTTP Headers. 3099e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 31030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param response 31130fb85f55cbd8df3005e652da3781f51294baf90ritchie * The Response object to which headers the queued cookies 31230fb85f55cbd8df3005e652da3781f51294baf90ritchie * will be added. 31330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 31430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void unloadQueue(Response response) { 31530fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Cookie cookie : this.queue) { 31630fb85f55cbd8df3005e652da3781f51294baf90ritchie response.addHeader("Set-Cookie", cookie.getHTTPHeader()); 31730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 31830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 319f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke } 320f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 3215b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 32230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default threading strategy for NanoHTTPD. 32330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 32430fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 32530fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, the server spawns a new Thread for every incoming request. 32630fb85f55cbd8df3005e652da3781f51294baf90ritchie * These are set to <i>daemon</i> status, and named according to the request 32730fb85f55cbd8df3005e652da3781f51294baf90ritchie * number. The name is useful when profiling the application. 32830fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3295b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 33030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultAsyncRunner implements AsyncRunner { 3313f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke 33230fb85f55cbd8df3005e652da3781f51294baf90ritchie private long requestCount; 33330fb85f55cbd8df3005e652da3781f51294baf90ritchie 334abcf1089ce1de49278970f088883cb32acb4f225ritchie private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>()); 335abcf1089ce1de49278970f088883cb32acb4f225ritchie 336d685218eacc23e69f685a76113665f50cc560edfritchie /** 337d685218eacc23e69f685a76113665f50cc560edfritchie * @return a list with currently running clients. 338d685218eacc23e69f685a76113665f50cc560edfritchie */ 339d685218eacc23e69f685a76113665f50cc560edfritchie public List<ClientHandler> getRunning() { 340d685218eacc23e69f685a76113665f50cc560edfritchie return running; 341d685218eacc23e69f685a76113665f50cc560edfritchie } 342d685218eacc23e69f685a76113665f50cc560edfritchie 34330fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 344abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closeAll() { 345abcf1089ce1de49278970f088883cb32acb4f225ritchie // copy of the list for concurrency 346abcf1089ce1de49278970f088883cb32acb4f225ritchie for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) { 347abcf1089ce1de49278970f088883cb32acb4f225ritchie clientHandler.close(); 348abcf1089ce1de49278970f088883cb32acb4f225ritchie } 349abcf1089ce1de49278970f088883cb32acb4f225ritchie } 350abcf1089ce1de49278970f088883cb32acb4f225ritchie 351abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 352abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closed(ClientHandler clientHandler) { 353abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.remove(clientHandler); 354abcf1089ce1de49278970f088883cb32acb4f225ritchie } 355abcf1089ce1de49278970f088883cb32acb4f225ritchie 356abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 357abcf1089ce1de49278970f088883cb32acb4f225ritchie public void exec(ClientHandler clientHandler) { 35830fb85f55cbd8df3005e652da3781f51294baf90ritchie ++this.requestCount; 359abcf1089ce1de49278970f088883cb32acb4f225ritchie Thread t = new Thread(clientHandler); 36030fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setDaemon(true); 36130fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")"); 362abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.add(clientHandler); 36330fb85f55cbd8df3005e652da3781f51294baf90ritchie t.start(); 364c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 365c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 366c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke 3679058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 36830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 36930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 37030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 37130fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, files are created by <code>File.createTempFile()</code> in 37230fb85f55cbd8df3005e652da3781f51294baf90ritchie * the directory specified. 37330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3749058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 37530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFile implements TempFile { 37630fb85f55cbd8df3005e652da3781f51294baf90ritchie 37730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final File file; 37830fb85f55cbd8df3005e652da3781f51294baf90ritchie 37930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream fstream; 38030fb85f55cbd8df3005e652da3781f51294baf90ritchie 38130fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFile(String tempdir) throws IOException { 38230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.file = File.createTempFile("NanoHTTPD-", "", new File(tempdir)); 38330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.fstream = new FileOutputStream(this.file); 3849058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3859058464950a9734da0a7ff2dc47f3081bbb5117critchie 38630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete() throws Exception { 38830fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.fstream); 3896034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann if (!this.file.delete()) { 3906034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann throw new Exception("could not delete temporary file"); 3916034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann } 3929058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3939058464950a9734da0a7ff2dc47f3081bbb5117critchie 39430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 39530fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getName() { 39630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.file.getAbsolutePath(); 3979058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3989058464950a9734da0a7ff2dc47f3081bbb5117critchie 39930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 40030fb85f55cbd8df3005e652da3781f51294baf90ritchie public OutputStream open() throws Exception { 40130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.fstream; 40230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 4039058464950a9734da0a7ff2dc47f3081bbb5117critchie } 404f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 4053f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke /** 40630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 40730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 40830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 40930fb85f55cbd8df3005e652da3781f51294baf90ritchie * This class stores its files in the standard location (that is, wherever 41030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>java.io.tmpdir</code> points to). Files are added to an internal 41130fb85f55cbd8df3005e652da3781f51294baf90ritchie * list, and deleted when no longer needed (that is, when 41230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>clear()</code> is invoked at the end of processing a request). 41330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 4143f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke */ 41530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFileManager implements TempFileManager { 416f39ce1dd765bb79b0a83a8e6dd921f0d3986ebf6Aaron Davidson 41730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String tmpdir; 4189058464950a9734da0a7ff2dc47f3081bbb5117critchie 41930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final List<TempFile> tempFiles; 4209058464950a9734da0a7ff2dc47f3081bbb5117critchie 42130fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFileManager() { 42230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tmpdir = System.getProperty("java.io.tmpdir"); 42330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles = new ArrayList<TempFile>(); 42430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 425d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 42630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 42730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void clear() { 42830fb85f55cbd8df3005e652da3781f51294baf90ritchie for (TempFile file : this.tempFiles) { 42930fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 43030fb85f55cbd8df3005e652da3781f51294baf90ritchie file.delete(); 43130fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception ignored) { 43230fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); 43330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 434fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 43530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.clear(); 436269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 437269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 43830fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 43930fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFile createTempFile() throws Exception { 44030fb85f55cbd8df3005e652da3781f51294baf90ritchie DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); 44130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.add(tempFile); 44230fb85f55cbd8df3005e652da3781f51294baf90ritchie return tempFile; 44330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 4440a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4450a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 4460a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander /** 44730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 4480a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander */ 44930fb85f55cbd8df3005e652da3781f51294baf90ritchie private class DefaultTempFileManagerFactory implements TempFileManagerFactory { 4500a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 45130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 45230fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFileManager create() { 45330fb85f55cbd8df3005e652da3781f51294baf90ritchie return new DefaultTempFileManager(); 4540a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4550a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4568dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 45730fb85f55cbd8df3005e652da3781f51294baf90ritchie protected class HTTPSession implements IHTTPSession { 4588dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 45930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int BUFSIZE = 8192; 4608dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final TempFileManager tempFileManager; 4628dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream outputStream; 4649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 46530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final PushbackInputStream inputStream; 46693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 46730fb85f55cbd8df3005e652da3781f51294baf90ritchie private int splitbyte; 46893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 46930fb85f55cbd8df3005e652da3781f51294baf90ritchie private int rlen; 470f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 47130fb85f55cbd8df3005e652da3781f51294baf90ritchie private String uri; 472f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 47330fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method method; 474f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 47530fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> parms; 4765b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 47730fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> headers; 4785b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 47930fb85f55cbd8df3005e652da3781f51294baf90ritchie private CookieHandler cookies; 4805b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48130fb85f55cbd8df3005e652da3781f51294baf90ritchie private String queryParameterString; 4825b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48330fb85f55cbd8df3005e652da3781f51294baf90ritchie private String remoteIp; 4848dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 48530fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { 48630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 48730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 48830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 4898dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 4908dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 49130fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) { 49230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 49330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 49430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 49530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString(); 49630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 4979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 4989788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 4999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 50030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the sent headers and loads the data into Key/value pairs 5019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 50230fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException { 5039788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke try { 50430fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the request line 50530fb85f55cbd8df3005e652da3781f51294baf90ritchie String inLine = in.readLine(); 50630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (inLine == null) { 50730fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 50830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 509fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 51030fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(inLine); 51130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 51230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 51330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 5149788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 51530fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("method", st.nextToken()); 5167f0727787957c2f093412c01d165846842ec2425Paul Hawke 51730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 51830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 5197f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5207f0727787957c2f093412c01d165846842ec2425Paul Hawke 52130fb85f55cbd8df3005e652da3781f51294baf90ritchie String uri = st.nextToken(); 5227f0727787957c2f093412c01d165846842ec2425Paul Hawke 52330fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode parameters from the URI 52430fb85f55cbd8df3005e652da3781f51294baf90ritchie int qmi = uri.indexOf('?'); 52530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (qmi >= 0) { 52630fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(uri.substring(qmi + 1), parms); 52730fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri.substring(0, qmi)); 52830fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 52930fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri); 5307f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5317f0727787957c2f093412c01d165846842ec2425Paul Hawke 53230fb85f55cbd8df3005e652da3781f51294baf90ritchie // If there's another token, its protocol version, 53330fb85f55cbd8df3005e652da3781f51294baf90ritchie // followed by HTTP headers. Ignore version but parse headers. 53430fb85f55cbd8df3005e652da3781f51294baf90ritchie // NOTE: this now forces header names lower case since they are 53530fb85f55cbd8df3005e652da3781f51294baf90ritchie // case insensitive and vary by client. 536de2bb1bc9fa3959846741e1fd14076971e17b90cElonen if (st.hasMoreTokens()) { 537de2bb1bc9fa3959846741e1fd14076971e17b90cElonen if (!st.nextToken().equals("HTTP/1.1")) { 538de2bb1bc9fa3959846741e1fd14076971e17b90cElonen throw new ResponseException(Response.Status.UNSUPPORTED_HTTP_VERSION, "Only HTTP/1.1 is supported."); 539de2bb1bc9fa3959846741e1fd14076971e17b90cElonen } 540de2bb1bc9fa3959846741e1fd14076971e17b90cElonen } else { 54130fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange.."); 54230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 54330fb85f55cbd8df3005e652da3781f51294baf90ritchie String line = in.readLine(); 54430fb85f55cbd8df3005e652da3781f51294baf90ritchie while (line != null && line.trim().length() > 0) { 54530fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = line.indexOf(':'); 54630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p >= 0) { 54730fb85f55cbd8df3005e652da3781f51294baf90ritchie headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim()); 5487f0727787957c2f093412c01d165846842ec2425Paul Hawke } 54930fb85f55cbd8df3005e652da3781f51294baf90ritchie line = in.readLine(); 5507f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5517f0727787957c2f093412c01d165846842ec2425Paul Hawke 55230fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("uri", uri); 5537f0727787957c2f093412c01d165846842ec2425Paul Hawke } catch (IOException ioe) { 55430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 5557f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5567f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5577f0727787957c2f093412c01d165846842ec2425Paul Hawke 55830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 55930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the Multipart Body data and put it into Key/Value pairs. 56030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 56130fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms, Map<String, String> files) throws ResponseException { 56230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 56330fb85f55cbd8df3005e652da3781f51294baf90ritchie int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes()); 56430fb85f55cbd8df3005e652da3781f51294baf90ritchie int boundarycount = 1; 56530fb85f55cbd8df3005e652da3781f51294baf90ritchie String mpline = in.readLine(); 56630fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null) { 56730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!mpline.contains(boundary)) { 56830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, 56930fb85f55cbd8df3005e652da3781f51294baf90ritchie "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html"); 57030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 57130fb85f55cbd8df3005e652da3781f51294baf90ritchie boundarycount++; 57230fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> item = new HashMap<String, String>(); 57330fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 57430fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && mpline.trim().length() > 0) { 57530fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = mpline.indexOf(':'); 57630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p != -1) { 57730fb85f55cbd8df3005e652da3781f51294baf90ritchie item.put(mpline.substring(0, p).trim().toLowerCase(Locale.US), mpline.substring(p + 1).trim()); 57830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 57930fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 58030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 58130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mpline != null) { 58230fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentDisposition = item.get("content-disposition"); 58330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (contentDisposition == null) { 58430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, 58530fb85f55cbd8df3005e652da3781f51294baf90ritchie "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html"); 58630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 58730fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(contentDisposition, ";"); 58830fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> disposition = new HashMap<String, String>(); 58930fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 59030fb85f55cbd8df3005e652da3781f51294baf90ritchie String token = st.nextToken().trim(); 59130fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = token.indexOf('='); 59230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p != -1) { 59330fb85f55cbd8df3005e652da3781f51294baf90ritchie disposition.put(token.substring(0, p).trim().toLowerCase(Locale.US), token.substring(p + 1).trim()); 59430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59630fb85f55cbd8df3005e652da3781f51294baf90ritchie String pname = disposition.get("name"); 59730fb85f55cbd8df3005e652da3781f51294baf90ritchie pname = pname.substring(1, pname.length() - 1); 59830fb85f55cbd8df3005e652da3781f51294baf90ritchie 59930fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = ""; 60030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (item.get("content-type") == null) { 60130fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && !mpline.contains(boundary)) { 60230fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 60330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mpline != null) { 60430fb85f55cbd8df3005e652da3781f51294baf90ritchie int d = mpline.indexOf(boundary); 60530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (d == -1) { 60630fb85f55cbd8df3005e652da3781f51294baf90ritchie value += mpline; 60730fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 60830fb85f55cbd8df3005e652da3781f51294baf90ritchie value += mpline.substring(0, d - 2); 60930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 61030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 61130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 61230fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 61330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (boundarycount > bpositions.length) { 61430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request"); 61530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 61630fb85f55cbd8df3005e652da3781f51294baf90ritchie int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]); 61730fb85f55cbd8df3005e652da3781f51294baf90ritchie String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4); 61830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!files.containsKey(pname)) { 61930fb85f55cbd8df3005e652da3781f51294baf90ritchie files.put(pname, path); 62030fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 62130fb85f55cbd8df3005e652da3781f51294baf90ritchie int count = 2; 62230fb85f55cbd8df3005e652da3781f51294baf90ritchie while (files.containsKey(pname + count)) { 62330fb85f55cbd8df3005e652da3781f51294baf90ritchie count++; 62430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 62530fb85f55cbd8df3005e652da3781f51294baf90ritchie files.put(pname + count, path); 62630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 62730fb85f55cbd8df3005e652da3781f51294baf90ritchie value = disposition.get("filename"); 62830fb85f55cbd8df3005e652da3781f51294baf90ritchie value = value.substring(1, value.length() - 1); 62930fb85f55cbd8df3005e652da3781f51294baf90ritchie do { 63030fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 63130fb85f55cbd8df3005e652da3781f51294baf90ritchie } while (mpline != null && !mpline.contains(boundary)); 63230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 63330fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(pname, value); 63498d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 63598d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 63630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 63730fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 638c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 639fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 640fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 64130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 64230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes parameters in percent-encoded URI-format ( e.g. 64330fb85f55cbd8df3005e652da3781f51294baf90ritchie * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 64430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Map. NOTE: this doesn't support multiple identical keys due to the 64530fb85f55cbd8df3005e652da3781f51294baf90ritchie * simplicity of Map. 64630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 64730fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeParms(String parms, Map<String, String> p) { 64830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (parms == null) { 64930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = ""; 65030fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 651c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 652c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke 65330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = parms; 65430fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(parms, "&"); 65530fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 65630fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 65730fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 65830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (sep >= 0) { 65930fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); 66030fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 66130fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e).trim(), ""); 66230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 663fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 664fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 665fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 66630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 66730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void execute() throws IOException { 66830fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 66930fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the first 8192 bytes. 67030fb85f55cbd8df3005e652da3781f51294baf90ritchie // The full header should fit in here. 67130fb85f55cbd8df3005e652da3781f51294baf90ritchie // Apache's default header limit is 8KB. 67230fb85f55cbd8df3005e652da3781f51294baf90ritchie // Do NOT assume that a single read will get the entire header 67330fb85f55cbd8df3005e652da3781f51294baf90ritchie // at once! 67430fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buf = new byte[HTTPSession.BUFSIZE]; 67530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.splitbyte = 0; 67630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = 0; 6779c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie 6789c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie int read = -1; 6799c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie try { 6809c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); 6819c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } catch (Exception e) { 6829c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6839c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6849c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6859c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6869c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (read == -1) { 6879c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie // socket was been closed 6889c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6899c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6909c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6919c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6929c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie while (read > 0) { 6939c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.rlen += read; 6949c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.splitbyte = findHeaderEnd(buf, this.rlen); 6959c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (this.splitbyte > 0) { 6969c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie break; 69730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6989c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); 69930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 70030fb85f55cbd8df3005e652da3781f51294baf90ritchie 70130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.splitbyte < this.rlen) { 70230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream.unread(buf, this.splitbyte, this.rlen - this.splitbyte); 70330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 70430fb85f55cbd8df3005e652da3781f51294baf90ritchie 70530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.parms = new HashMap<String, String>(); 70630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null == this.headers) { 70730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 70830fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 70930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.clear(); 71030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 71130fb85f55cbd8df3005e652da3781f51294baf90ritchie 71230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null != this.remoteIp) { 71330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("remote-addr", this.remoteIp); 71430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("http-client-ip", this.remoteIp); 71530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 71630fb85f55cbd8df3005e652da3781f51294baf90ritchie 71730fb85f55cbd8df3005e652da3781f51294baf90ritchie // Create a BufferedReader for parsing the header. 71830fb85f55cbd8df3005e652da3781f51294baf90ritchie BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen))); 71930fb85f55cbd8df3005e652da3781f51294baf90ritchie 72030fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode the header into parms and header java properties 72130fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> pre = new HashMap<String, String>(); 72230fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeHeader(hin, pre, this.parms, this.headers); 72330fb85f55cbd8df3005e652da3781f51294baf90ritchie 72430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.method = Method.lookup(pre.get("method")); 72530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.method == null) { 72630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); 72730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 72830fb85f55cbd8df3005e652da3781f51294baf90ritchie 72930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.uri = pre.get("uri"); 73030fb85f55cbd8df3005e652da3781f51294baf90ritchie 73130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies = new CookieHandler(this.headers); 73230fb85f55cbd8df3005e652da3781f51294baf90ritchie 73330fb85f55cbd8df3005e652da3781f51294baf90ritchie // Ok, now do the serve() 73430fb85f55cbd8df3005e652da3781f51294baf90ritchie Response r = serve(this); 73530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (r == null) { 73630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 73730fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 73830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.unloadQueue(r); 73930fb85f55cbd8df3005e652da3781f51294baf90ritchie r.setRequestMethod(this.method); 74030fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 74130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 74230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketException e) { 74330fb85f55cbd8df3005e652da3781f51294baf90ritchie // throw it out to close socket object (finalAccept) 74430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw e; 74530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketTimeoutException ste) { 74630fb85f55cbd8df3005e652da3781f51294baf90ritchie // treat socket timeouts the same way we treat socket exceptions 74730fb85f55cbd8df3005e652da3781f51294baf90ritchie // i.e. close the stream & finalAccept object by throwing the 74830fb85f55cbd8df3005e652da3781f51294baf90ritchie // exception up the call stack. 74930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw ste; 75030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 7515e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie Response r = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 75230fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 75330fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 75430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 7555e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie Response r = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 75630fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 75730fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 75830fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 75930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager.clear(); 760fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 761fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 762fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 76330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 76430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find byte index separating header from body. It must be the last byte 76530fb85f55cbd8df3005e652da3781f51294baf90ritchie * of the first two sequential new lines. 76630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 76730fb85f55cbd8df3005e652da3781f51294baf90ritchie private int findHeaderEnd(final byte[] buf, int rlen) { 76830fb85f55cbd8df3005e652da3781f51294baf90ritchie int splitbyte = 0; 76930fb85f55cbd8df3005e652da3781f51294baf90ritchie while (splitbyte + 3 < rlen) { 77030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 77130fb85f55cbd8df3005e652da3781f51294baf90ritchie return splitbyte + 4; 772fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 77330fb85f55cbd8df3005e652da3781f51294baf90ritchie splitbyte++; 774fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 77530fb85f55cbd8df3005e652da3781f51294baf90ritchie return 0; 776fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 777fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 77830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 77930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find the byte positions where multipart boundaries start. 78030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 78130fb85f55cbd8df3005e652da3781f51294baf90ritchie private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) { 78230fb85f55cbd8df3005e652da3781f51294baf90ritchie int matchcount = 0; 78330fb85f55cbd8df3005e652da3781f51294baf90ritchie int matchbyte = -1; 78430fb85f55cbd8df3005e652da3781f51294baf90ritchie List<Integer> matchbytes = new ArrayList<Integer>(); 78530fb85f55cbd8df3005e652da3781f51294baf90ritchie for (int i = 0; i < b.limit(); i++) { 78630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (b.get(i) == boundary[matchcount]) { 78730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (matchcount == 0) { 78830fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbyte = i; 78930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 79030fb85f55cbd8df3005e652da3781f51294baf90ritchie matchcount++; 79130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (matchcount == boundary.length) { 79230fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbytes.add(matchbyte); 79330fb85f55cbd8df3005e652da3781f51294baf90ritchie matchcount = 0; 79430fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbyte = -1; 79530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 79630fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 79730fb85f55cbd8df3005e652da3781f51294baf90ritchie i -= matchcount; 79830fb85f55cbd8df3005e652da3781f51294baf90ritchie matchcount = 0; 79930fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbyte = -1; 80030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 80130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 80230fb85f55cbd8df3005e652da3781f51294baf90ritchie int[] ret = new int[matchbytes.size()]; 80330fb85f55cbd8df3005e652da3781f51294baf90ritchie for (int i = 0; i < ret.length; i++) { 80430fb85f55cbd8df3005e652da3781f51294baf90ritchie ret[i] = matchbytes.get(i); 80530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 80630fb85f55cbd8df3005e652da3781f51294baf90ritchie return ret; 807c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 808c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 80930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 81030fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler getCookies() { 81130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies; 812c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 813c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 81430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 81530fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getHeaders() { 81630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.headers; 817c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 818c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 81930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 82030fb85f55cbd8df3005e652da3781f51294baf90ritchie public final InputStream getInputStream() { 82130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.inputStream; 822c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 823c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 82430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 82530fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Method getMethod() { 82630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.method; 827c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 828c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 82930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 83030fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getParms() { 83130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.parms; 832c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 833c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 83430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 83530fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getQueryParameterString() { 83630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.queryParameterString; 837c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 838c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 83930fb85f55cbd8df3005e652da3781f51294baf90ritchie private RandomAccessFile getTmpBucket() { 84030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 84130fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile tempFile = this.tempFileManager.createTempFile(); 84230fb85f55cbd8df3005e652da3781f51294baf90ritchie return new RandomAccessFile(tempFile.getName(), "rw"); 84330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 84430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 8459788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8469788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8477e4e4ae652c755faea626f1e2538d495f96e648esynapticloop 848bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 84930fb85f55cbd8df3005e652da3781f51294baf90ritchie public final String getUri() { 85030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.uri; 85193441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 85293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 853bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 854bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel public void parseBody(Map<String, String> files) throws IOException, ResponseException { 85593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed RandomAccessFile randomAccessFile = null; 85693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed BufferedReader in = null; 85793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed try { 85893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 859412e2e136193da510e859f73f9ff2ac21565df69james.mcclure randomAccessFile = getTmpBucket(); 860d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 861745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed long size; 86230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.headers.containsKey("content-length")) { 86330fb85f55cbd8df3005e652da3781f51294baf90ritchie size = Integer.parseInt(this.headers.get("content-length")); 86430fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (this.splitbyte < this.rlen) { 86530fb85f55cbd8df3005e652da3781f51294baf90ritchie size = this.rlen - this.splitbyte; 866745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed } else { 867d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke size = 0; 8680cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 869d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 870d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Now read all the body and write it to f 871745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed byte[] buf = new byte[512]; 87230fb85f55cbd8df3005e652da3781f51294baf90ritchie while (this.rlen >= 0 && size > 0) { 87330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, 512)); 87430fb85f55cbd8df3005e652da3781f51294baf90ritchie size -= this.rlen; 87530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.rlen > 0) { 87630fb85f55cbd8df3005e652da3781f51294baf90ritchie randomAccessFile.write(buf, 0, this.rlen); 8770cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 878269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 879269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 880269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Get the raw body as a byte [] 881412e2e136193da510e859f73f9ff2ac21565df69james.mcclure ByteBuffer fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); 882412e2e136193da510e859f73f9ff2ac21565df69james.mcclure randomAccessFile.seek(0); 883269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 884269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Create a BufferedReader for easily reading it as string. 885412e2e136193da510e859f73f9ff2ac21565df69james.mcclure InputStream bin = new FileInputStream(randomAccessFile.getFD()); 88693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed in = new BufferedReader(new InputStreamReader(bin)); 887269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 888269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 889269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 89030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.POST.equals(this.method)) { 891269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 89230fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentTypeHeader = this.headers.get("content-type"); 8932b54eda5fee8430268a99d73b061e790362db544Tom Hermann 8942c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke StringTokenizer st = null; 8952c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (contentTypeHeader != null) { 8962c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke st = new StringTokenizer(contentTypeHeader, ",; "); 8972c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (st.hasMoreTokens()) { 8982c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke contentType = st.nextToken(); 8992c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke } 900269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 901269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9020277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke if ("multipart/form-data".equalsIgnoreCase(contentType)) { 903269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 9040cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 9059058464950a9734da0a7ff2dc47f3081bbb5117critchie throw new ResponseException(Response.Status.BAD_REQUEST, 9069058464950a9734da0a7ff2dc47f3081bbb5117critchie "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 9070cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 9082b54eda5fee8430268a99d73b061e790362db544Tom Hermann 9094e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundaryStartString = "boundary="; 9102b54eda5fee8430268a99d73b061e790362db544Tom Hermann int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length(); 9114e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length()); 912220e1a21e7bbb831d06551c72799dfedc1db979fPaul Hawke if (boundary.startsWith("\"") && boundary.endsWith("\"")) { 9134e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke boundary = boundary.substring(1, boundary.length() - 1); 9144e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 915269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 91630fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeMultipartData(boundary, fbuf, in, this.parms, files); 917269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 918269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String postLine = ""; 91935b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright StringBuilder postLineBuffer = new StringBuilder(); 920269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke char pbuf[] = new char[512]; 921269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = in.read(pbuf); 9229fbdbfaf9eb72f29f2244919051b9d7c533ebf17Jarno Elonen while (read >= 0) { 92335b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright postLine = String.valueOf(pbuf, 0, read); 92435b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright postLineBuffer.append(postLine); 925269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke read = in.read(pbuf); 926269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 92735b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright postLine = postLineBuffer.toString().trim(); 928e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown // Handle application/x-www-form-urlencoded 929e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) { 93030fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(postLine, this.parms); 931e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } else if (postLine.length() != 0) { 9329058464950a9734da0a7ff2dc47f3081bbb5117critchie // Special case for raw POST data => create a 9339058464950a9734da0a7ff2dc47f3081bbb5117critchie // special files entry "postData" with raw content 9349058464950a9734da0a7ff2dc47f3081bbb5117critchie // data 9359058464950a9734da0a7ff2dc47f3081bbb5117critchie files.put("postData", postLine); 936e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } 937269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 93830fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (Method.PUT.equals(this.method)) { 939dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke files.put("content", saveTmpFile(fbuf, 0, fbuf.limit())); 940b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } 9414e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } finally { 9429cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed safeClose(randomAccessFile); 94393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed safeClose(in); 944269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 945269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 946269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 947269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 94830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Retrieves the content of a sent file and saves it to a temporary 94930fb85f55cbd8df3005e652da3781f51294baf90ritchie * file. The full path to the saved file is returned. 950d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 95130fb85f55cbd8df3005e652da3781f51294baf90ritchie private String saveTmpFile(ByteBuffer b, int offset, int len) { 95230fb85f55cbd8df3005e652da3781f51294baf90ritchie String path = ""; 95330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (len > 0) { 95430fb85f55cbd8df3005e652da3781f51294baf90ritchie FileOutputStream fileOutputStream = null; 95530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 95630fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile tempFile = this.tempFileManager.createTempFile(); 95730fb85f55cbd8df3005e652da3781f51294baf90ritchie ByteBuffer src = b.duplicate(); 95830fb85f55cbd8df3005e652da3781f51294baf90ritchie fileOutputStream = new FileOutputStream(tempFile.getName()); 95930fb85f55cbd8df3005e652da3781f51294baf90ritchie FileChannel dest = fileOutputStream.getChannel(); 96030fb85f55cbd8df3005e652da3781f51294baf90ritchie src.position(offset).limit(offset + len); 96130fb85f55cbd8df3005e652da3781f51294baf90ritchie dest.write(src.slice()); 96230fb85f55cbd8df3005e652da3781f51294baf90ritchie path = tempFile.getName(); 96330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { // Catch exception if any 96430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 96530fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 96630fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(fileOutputStream); 9670cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 96830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 96930fb85f55cbd8df3005e652da3781f51294baf90ritchie return path; 97030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 9710cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke 97230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 97330fb85f55cbd8df3005e652da3781f51294baf90ritchie * It returns the offset separating multipart file headers from the 97430fb85f55cbd8df3005e652da3781f51294baf90ritchie * file's data. 97530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 97630fb85f55cbd8df3005e652da3781f51294baf90ritchie private int stripMultipartHeaders(ByteBuffer b, int offset) { 97730fb85f55cbd8df3005e652da3781f51294baf90ritchie int i; 97830fb85f55cbd8df3005e652da3781f51294baf90ritchie for (i = offset; i < b.limit(); i++) { 97930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (b.get(i) == '\r' && b.get(++i) == '\n' && b.get(++i) == '\r' && b.get(++i) == '\n') { 98030fb85f55cbd8df3005e652da3781f51294baf90ritchie break; 9810cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 98230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 98330fb85f55cbd8df3005e652da3781f51294baf90ritchie return i + 1; 98430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 98530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 986269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 98730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 98830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Handles one session, i.e. parses the HTTP request and returns the 98930fb85f55cbd8df3005e652da3781f51294baf90ritchie * response. 99030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 99130fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IHTTPSession { 992269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 99330fb85f55cbd8df3005e652da3781f51294baf90ritchie void execute() throws IOException; 994269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 99530fb85f55cbd8df3005e652da3781f51294baf90ritchie CookieHandler getCookies(); 996269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 99730fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getHeaders(); 998269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 99930fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream getInputStream(); 100030fb85f55cbd8df3005e652da3781f51294baf90ritchie 100130fb85f55cbd8df3005e652da3781f51294baf90ritchie Method getMethod(); 100230fb85f55cbd8df3005e652da3781f51294baf90ritchie 100330fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getParms(); 100430fb85f55cbd8df3005e652da3781f51294baf90ritchie 100530fb85f55cbd8df3005e652da3781f51294baf90ritchie String getQueryParameterString(); 1006269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1007269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 100830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return the path part of the URL. 1009d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 101030fb85f55cbd8df3005e652da3781f51294baf90ritchie String getUri(); 1011269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1012269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 101330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds the files in the request body to the files map. 10149e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 101530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param files 101630fb85f55cbd8df3005e652da3781f51294baf90ritchie * map to modify 1017d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 101830fb85f55cbd8df3005e652da3781f51294baf90ritchie void parseBody(Map<String, String> files) throws IOException, ResponseException; 101930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 102030fb85f55cbd8df3005e652da3781f51294baf90ritchie 102130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 102230fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP Request methods, with the ability to decode a <code>String</code> 102330fb85f55cbd8df3005e652da3781f51294baf90ritchie * back to its enum value. 102430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 102530fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Method { 102630fb85f55cbd8df3005e652da3781f51294baf90ritchie GET, 102730fb85f55cbd8df3005e652da3781f51294baf90ritchie PUT, 102830fb85f55cbd8df3005e652da3781f51294baf90ritchie POST, 102930fb85f55cbd8df3005e652da3781f51294baf90ritchie DELETE, 103030fb85f55cbd8df3005e652da3781f51294baf90ritchie HEAD, 103130fb85f55cbd8df3005e652da3781f51294baf90ritchie OPTIONS; 103230fb85f55cbd8df3005e652da3781f51294baf90ritchie 103330fb85f55cbd8df3005e652da3781f51294baf90ritchie static Method lookup(String method) { 103430fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Method m : Method.values()) { 103530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (m.toString().equalsIgnoreCase(method)) { 103630fb85f55cbd8df3005e652da3781f51294baf90ritchie return m; 1037269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1038269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 103930fb85f55cbd8df3005e652da3781f51294baf90ritchie return null; 104030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 104130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 104230fb85f55cbd8df3005e652da3781f51294baf90ritchie 104330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 104430fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP response. Return one of these from serve(). 104530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 104630fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Response { 104730fb85f55cbd8df3005e652da3781f51294baf90ritchie 104830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IStatus { 104930fb85f55cbd8df3005e652da3781f51294baf90ritchie 105030fb85f55cbd8df3005e652da3781f51294baf90ritchie String getDescription(); 105130fb85f55cbd8df3005e652da3781f51294baf90ritchie 105230fb85f55cbd8df3005e652da3781f51294baf90ritchie int getRequestStatus(); 1053269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1054269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1055269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 105630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Some HTTP response status codes 1057d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 105830fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Status implements IStatus { 105930fb85f55cbd8df3005e652da3781f51294baf90ritchie SWITCH_PROTOCOL(101, "Switching Protocols"), 106030fb85f55cbd8df3005e652da3781f51294baf90ritchie OK(200, "OK"), 106130fb85f55cbd8df3005e652da3781f51294baf90ritchie CREATED(201, "Created"), 106230fb85f55cbd8df3005e652da3781f51294baf90ritchie ACCEPTED(202, "Accepted"), 106330fb85f55cbd8df3005e652da3781f51294baf90ritchie NO_CONTENT(204, "No Content"), 106430fb85f55cbd8df3005e652da3781f51294baf90ritchie PARTIAL_CONTENT(206, "Partial Content"), 106530fb85f55cbd8df3005e652da3781f51294baf90ritchie REDIRECT(301, "Moved Permanently"), 106630fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_MODIFIED(304, "Not Modified"), 106730fb85f55cbd8df3005e652da3781f51294baf90ritchie BAD_REQUEST(400, "Bad Request"), 106830fb85f55cbd8df3005e652da3781f51294baf90ritchie UNAUTHORIZED(401, "Unauthorized"), 106930fb85f55cbd8df3005e652da3781f51294baf90ritchie FORBIDDEN(403, "Forbidden"), 107030fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_FOUND(404, "Not Found"), 107130fb85f55cbd8df3005e652da3781f51294baf90ritchie METHOD_NOT_ALLOWED(405, "Method Not Allowed"), 107230fb85f55cbd8df3005e652da3781f51294baf90ritchie RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), 1073de2bb1bc9fa3959846741e1fd14076971e17b90cElonen INTERNAL_ERROR(500, "Internal Server Error"), 1074de2bb1bc9fa3959846741e1fd14076971e17b90cElonen UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported"); 107530fb85f55cbd8df3005e652da3781f51294baf90ritchie 107630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int requestStatus; 107730fb85f55cbd8df3005e652da3781f51294baf90ritchie 107830fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String description; 107930fb85f55cbd8df3005e652da3781f51294baf90ritchie 108030fb85f55cbd8df3005e652da3781f51294baf90ritchie Status(int requestStatus, String description) { 108130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestStatus = requestStatus; 108230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.description = description; 1083269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 108430fb85f55cbd8df3005e652da3781f51294baf90ritchie 108530fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 108630fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getDescription() { 108730fb85f55cbd8df3005e652da3781f51294baf90ritchie return "" + this.requestStatus + " " + this.description; 108830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 108930fb85f55cbd8df3005e652da3781f51294baf90ritchie 109030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 109130fb85f55cbd8df3005e652da3781f51294baf90ritchie public int getRequestStatus() { 109230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestStatus; 1093269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1094269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1095269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1096269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 109730fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP status code after processing, e.g. "200 OK", Status.OK 1098d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 109930fb85f55cbd8df3005e652da3781f51294baf90ritchie private IStatus status; 1100269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 110130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 110230fb85f55cbd8df3005e652da3781f51294baf90ritchie * MIME type of content, e.g. "text/html" 110330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 110430fb85f55cbd8df3005e652da3781f51294baf90ritchie private String mimeType; 110530fb85f55cbd8df3005e652da3781f51294baf90ritchie 110630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 110730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Data of the response, may be null. 110830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 110930fb85f55cbd8df3005e652da3781f51294baf90ritchie private InputStream data; 111030fb85f55cbd8df3005e652da3781f51294baf90ritchie 11119e1ec7bff40d70d31953a04dd448665aaf549395ritchie private long contentLength; 1112292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen 111330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 111430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Headers for the HTTP response. Use addHeader() to add lines. 111530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 111630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Map<String, String> header = new HashMap<String, String>(); 111730fb85f55cbd8df3005e652da3781f51294baf90ritchie 111830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 111930fb85f55cbd8df3005e652da3781f51294baf90ritchie * The request method that spawned this response. 112030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 112130fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method requestMethod; 112230fb85f55cbd8df3005e652da3781f51294baf90ritchie 112330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 112430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Use chunkedTransfer 112530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 112630fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean chunkedTransfer; 112730fb85f55cbd8df3005e652da3781f51294baf90ritchie 112830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1129dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen * Creates a fixed length response if totalBytes>=0, otherwise chunked. 1130dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen */ 11319e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) { 1132dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.status = status; 1133dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.mimeType = mimeType; 11345e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (data == null) { 11355e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = new ByteArrayInputStream(new byte[0]); 11369e1ec7bff40d70d31953a04dd448665aaf549395ritchie this.contentLength = 0L; 11375e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 11385e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = data; 11395e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.contentLength = totalBytes; 1140292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen } 11415e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.chunkedTransfer = this.contentLength < 0; 1142269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1143269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1144269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 114530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds given line to the header. 1146269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 114730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void addHeader(String name, String value) { 114830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.header.put(name, value); 114930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 115030fb85f55cbd8df3005e652da3781f51294baf90ritchie 115130fb85f55cbd8df3005e652da3781f51294baf90ritchie public InputStream getData() { 115230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.data; 115330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 115430fb85f55cbd8df3005e652da3781f51294baf90ritchie 115530fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHeader(String name) { 115630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.header.get(name); 115730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 115830fb85f55cbd8df3005e652da3781f51294baf90ritchie 115930fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getMimeType() { 116030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.mimeType; 116130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 116230fb85f55cbd8df3005e652da3781f51294baf90ritchie 116330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Method getRequestMethod() { 116430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestMethod; 116530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 116630fb85f55cbd8df3005e652da3781f51294baf90ritchie 116730fb85f55cbd8df3005e652da3781f51294baf90ritchie public IStatus getStatus() { 116830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 116930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 117030fb85f55cbd8df3005e652da3781f51294baf90ritchie 117130fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean headerAlreadySent(Map<String, String> header, String name) { 117230fb85f55cbd8df3005e652da3781f51294baf90ritchie boolean alreadySent = false; 117330fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 117430fb85f55cbd8df3005e652da3781f51294baf90ritchie alreadySent |= headerName.equalsIgnoreCase(name); 1175f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 117630fb85f55cbd8df3005e652da3781f51294baf90ritchie return alreadySent; 117730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1178269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 117930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 118030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sends given response to the socket. 118130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 118230fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void send(OutputStream outputStream) { 118330fb85f55cbd8df3005e652da3781f51294baf90ritchie String mime = this.mimeType; 118430fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 118530fb85f55cbd8df3005e652da3781f51294baf90ritchie gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 118630fb85f55cbd8df3005e652da3781f51294baf90ritchie 118730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 118830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.status == null) { 118930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error("sendResponse(): Status can't be null."); 119030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 11915e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")), false); 119230fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("HTTP/1.1 " + this.status.getDescription() + " \r\n"); 119330fb85f55cbd8df3005e652da3781f51294baf90ritchie 119430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mime != null) { 119530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Type: " + mime + "\r\n"); 119630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 119730fb85f55cbd8df3005e652da3781f51294baf90ritchie 119830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header == null || this.header.get("Date") == null) { 119930fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 120030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 120130fb85f55cbd8df3005e652da3781f51294baf90ritchie 120230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header != null) { 120330fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String key : this.header.keySet()) { 120430fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = this.header.get(key); 120530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print(key + ": " + value + "\r\n"); 120630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 120730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 120830fb85f55cbd8df3005e652da3781f51294baf90ritchie 120930fb85f55cbd8df3005e652da3781f51294baf90ritchie sendConnectionHeaderIfNotAlreadyPresent(pw, this.header); 121030fb85f55cbd8df3005e652da3781f51294baf90ritchie 121130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 121230fb85f55cbd8df3005e652da3781f51294baf90ritchie sendAsChunked(outputStream, pw); 1213b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } else { 12149e1ec7bff40d70d31953a04dd448665aaf549395ritchie long pending = this.data != null ? this.contentLength : 0; 121530fb85f55cbd8df3005e652da3781f51294baf90ritchie pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, this.header, pending); 121630fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("\r\n"); 121730fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.flush(); 121830fb85f55cbd8df3005e652da3781f51294baf90ritchie sendAsFixedLength(outputStream, pending); 1219269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 122030fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.flush(); 122130fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.data); 122230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 122330fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe); 1224269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1225269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 122693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 122730fb85f55cbd8df3005e652da3781f51294baf90ritchie private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException { 122830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Transfer-Encoding: chunked\r\n"); 122930fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("\r\n"); 123030fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.flush(); 123130fb85f55cbd8df3005e652da3781f51294baf90ritchie int BUFFER_SIZE = 16 * 1024; 123230fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] CRLF = "\r\n".getBytes(); 123330fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buff = new byte[BUFFER_SIZE]; 123430fb85f55cbd8df3005e652da3781f51294baf90ritchie int read; 123530fb85f55cbd8df3005e652da3781f51294baf90ritchie while ((read = this.data.read(buff)) > 0) { 123630fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(String.format("%x\r\n", read).getBytes()); 123730fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(buff, 0, read); 123830fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(CRLF); 123930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 124030fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(String.format("0\r\n\r\n").getBytes()); 124193441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 124293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 12439e1ec7bff40d70d31953a04dd448665aaf549395ritchie private void sendAsFixedLength(OutputStream outputStream, long pending) throws IOException { 124430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.data != null) { 12459e1ec7bff40d70d31953a04dd448665aaf549395ritchie long BUFFER_SIZE = 16 * 1024; 12469e1ec7bff40d70d31953a04dd448665aaf549395ritchie byte[] buff = new byte[(int) BUFFER_SIZE]; 124730fb85f55cbd8df3005e652da3781f51294baf90ritchie while (pending > 0) { 12489e1ec7bff40d70d31953a04dd448665aaf549395ritchie int read = this.data.read(buff, 0, (int) (pending > BUFFER_SIZE ? BUFFER_SIZE : pending)); 124930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (read <= 0) { 125030fb85f55cbd8df3005e652da3781f51294baf90ritchie break; 125130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 125230fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(buff, 0, read); 125330fb85f55cbd8df3005e652da3781f51294baf90ritchie pending -= read; 125430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 125530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1256fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke } 1257fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke 125830fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) { 125930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!headerAlreadySent(header, "connection")) { 126030fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Connection: keep-alive\r\n"); 126130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 126230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 126330fb85f55cbd8df3005e652da3781f51294baf90ritchie 12649e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, long size) { 126530fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 126630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (headerName.equalsIgnoreCase("content-length")) { 126730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 12689e1ec7bff40d70d31953a04dd448665aaf549395ritchie return Long.parseLong(header.get(headerName)); 126930fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (NumberFormatException ex) { 127030fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 127130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127430fb85f55cbd8df3005e652da3781f51294baf90ritchie 127530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Length: " + size + "\r\n"); 127630fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 127730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127830fb85f55cbd8df3005e652da3781f51294baf90ritchie 127930fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setChunkedTransfer(boolean chunkedTransfer) { 128030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.chunkedTransfer = chunkedTransfer; 128130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 128230fb85f55cbd8df3005e652da3781f51294baf90ritchie 128330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setData(InputStream data) { 128430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = data; 128530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 128630fb85f55cbd8df3005e652da3781f51294baf90ritchie 128730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setMimeType(String mimeType) { 128830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 128930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129030fb85f55cbd8df3005e652da3781f51294baf90ritchie 129130fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setRequestMethod(Method requestMethod) { 129230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestMethod = requestMethod; 129330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129430fb85f55cbd8df3005e652da3781f51294baf90ritchie 129530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setStatus(IStatus status) { 129630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 129730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129930fb85f55cbd8df3005e652da3781f51294baf90ritchie 130030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final class ResponseException extends Exception { 130130fb85f55cbd8df3005e652da3781f51294baf90ritchie 130230fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final long serialVersionUID = 6569838532917408380L; 130330fb85f55cbd8df3005e652da3781f51294baf90ritchie 130430fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Response.Status status; 130530fb85f55cbd8df3005e652da3781f51294baf90ritchie 130630fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message) { 130730fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message); 130830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 130930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 131030fb85f55cbd8df3005e652da3781f51294baf90ritchie 131130fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message, Exception e) { 131230fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message, e); 131330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 131430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 131530fb85f55cbd8df3005e652da3781f51294baf90ritchie 131630fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response.Status getStatus() { 131730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 131830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 131930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 132030fb85f55cbd8df3005e652da3781f51294baf90ritchie 132130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1322abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for the main listening thread. 1323abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1324abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ServerRunnable implements Runnable { 1325abcf1089ce1de49278970f088883cb32acb4f225ritchie 1326abcf1089ce1de49278970f088883cb32acb4f225ritchie private final int timeout; 1327abcf1089ce1de49278970f088883cb32acb4f225ritchie 1328abcf1089ce1de49278970f088883cb32acb4f225ritchie private ServerRunnable(int timeout) { 1329abcf1089ce1de49278970f088883cb32acb4f225ritchie this.timeout = timeout; 1330abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1331abcf1089ce1de49278970f088883cb32acb4f225ritchie 1332abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 1333abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 1334abcf1089ce1de49278970f088883cb32acb4f225ritchie do { 1335abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 1336abcf1089ce1de49278970f088883cb32acb4f225ritchie final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept(); 1337901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie if (this.timeout > 0) { 1338901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie finalAccept.setSoTimeout(this.timeout); 1339901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie } 1340abcf1089ce1de49278970f088883cb32acb4f225ritchie final InputStream inputStream = finalAccept.getInputStream(); 1341abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream)); 1342abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (IOException e) { 1343abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 1344abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1345abcf1089ce1de49278970f088883cb32acb4f225ritchie } while (!NanoHTTPD.this.myServerSocket.isClosed()); 1346abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1347abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1348abcf1089ce1de49278970f088883cb32acb4f225ritchie 1349abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 135030fb85f55cbd8df3005e652da3781f51294baf90ritchie * A temp file. 135130fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 135230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 135330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp files are responsible for managing the actual temporary storage and 135430fb85f55cbd8df3005e652da3781f51294baf90ritchie * cleaning themselves up when no longer needed. 135530fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 135630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 135730fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFile { 135830fb85f55cbd8df3005e652da3781f51294baf90ritchie 135930fb85f55cbd8df3005e652da3781f51294baf90ritchie void delete() throws Exception; 136030fb85f55cbd8df3005e652da3781f51294baf90ritchie 136130fb85f55cbd8df3005e652da3781f51294baf90ritchie String getName(); 136230fb85f55cbd8df3005e652da3781f51294baf90ritchie 136330fb85f55cbd8df3005e652da3781f51294baf90ritchie OutputStream open() throws Exception; 136430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 136530fb85f55cbd8df3005e652da3781f51294baf90ritchie 136630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 136730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file manager. 136830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 136930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 137030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file managers are created 1-to-1 with incoming requests, to create 137130fb85f55cbd8df3005e652da3781f51294baf90ritchie * and cleanup temporary files created as a result of handling the request. 137230fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 137330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 137430fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManager { 137530fb85f55cbd8df3005e652da3781f51294baf90ritchie 137630fb85f55cbd8df3005e652da3781f51294baf90ritchie void clear(); 137730fb85f55cbd8df3005e652da3781f51294baf90ritchie 137830fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile createTempFile() throws Exception; 137930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 138030fb85f55cbd8df3005e652da3781f51294baf90ritchie 138130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 138230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Factory to create temp file managers. 138330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 138430fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManagerFactory { 138530fb85f55cbd8df3005e652da3781f51294baf90ritchie 138630fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFileManager create(); 138730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 138830fb85f55cbd8df3005e652da3781f51294baf90ritchie 138930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 139030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) 139130fb85f55cbd8df3005e652da3781f51294baf90ritchie * This is required as the Keep-Alive HTTP connections would otherwise block 139230fb85f55cbd8df3005e652da3781f51294baf90ritchie * the socket reading thread forever (or as long the browser is open). 139330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 139430fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int SOCKET_READ_TIMEOUT = 5000; 139530fb85f55cbd8df3005e652da3781f51294baf90ritchie 139630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 139730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: plain text 139830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 139930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_PLAINTEXT = "text/plain"; 140030fb85f55cbd8df3005e652da3781f51294baf90ritchie 140130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 140230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: html 140330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 140430fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_HTML = "text/html"; 140530fb85f55cbd8df3005e652da3781f51294baf90ritchie 140630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 140730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pseudo-Parameter to use to store the actual query string in the 140830fb85f55cbd8df3005e652da3781f51294baf90ritchie * parameters map for later re-processing. 140930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 141030fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; 141130fb85f55cbd8df3005e652da3781f51294baf90ritchie 141230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 141330fb85f55cbd8df3005e652da3781f51294baf90ritchie * logger to log to. 141430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 14159c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName()); 141630fb85f55cbd8df3005e652da3781f51294baf90ritchie 141730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 141830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an 141930fb85f55cbd8df3005e652da3781f51294baf90ritchie * array of loaded KeyManagers. These objects must properly 142030fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded/initialized by the caller. 142130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 142230fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException { 142330fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 142430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 142530fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 142630fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 142730fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 142830fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); 142930fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 143030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 143130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 143230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 143330fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 143430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 143530fb85f55cbd8df3005e652da3781f51294baf90ritchie 143630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 143730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a 143830fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded KeyManagerFactory. These objects must properly loaded/initialized 143930fb85f55cbd8df3005e652da3781f51294baf90ritchie * by the caller. 144030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 144130fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException { 144230fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 144330fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 144430fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 144530fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 144630fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 144730fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(loadedKeyFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 144830fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 144930fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 145030fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 145130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 145230fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 145330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 145430fb85f55cbd8df3005e652da3781f51294baf90ritchie 145530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 145630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your 145730fb85f55cbd8df3005e652da3781f51294baf90ritchie * certificate and passphrase 145830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 145930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException { 146030fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 146130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 146230fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 146330fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); 146430fb85f55cbd8df3005e652da3781f51294baf90ritchie keystore.load(keystoreStream, passphrase); 146530fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 146630fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(keystore); 146730fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 146830fb85f55cbd8df3005e652da3781f51294baf90ritchie keyManagerFactory.init(keystore, passphrase); 146930fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 147030fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 147130fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 147230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 147330fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 147430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 147530fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 147630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 147730fb85f55cbd8df3005e652da3781f51294baf90ritchie 147830fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final void safeClose(Closeable closeable) { 147930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (closeable != null) { 148030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 148130fb85f55cbd8df3005e652da3781f51294baf90ritchie closeable.close(); 148230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException e) { 148330fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e); 148430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 148530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 148630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 148730fb85f55cbd8df3005e652da3781f51294baf90ritchie 148830fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String hostname; 148930fb85f55cbd8df3005e652da3781f51294baf90ritchie 149030fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int myPort; 149130fb85f55cbd8df3005e652da3781f51294baf90ritchie 149230fb85f55cbd8df3005e652da3781f51294baf90ritchie private ServerSocket myServerSocket; 149330fb85f55cbd8df3005e652da3781f51294baf90ritchie 149430fb85f55cbd8df3005e652da3781f51294baf90ritchie private SSLServerSocketFactory sslServerSocketFactory; 149530fb85f55cbd8df3005e652da3781f51294baf90ritchie 149630fb85f55cbd8df3005e652da3781f51294baf90ritchie private Thread myThread; 149730fb85f55cbd8df3005e652da3781f51294baf90ritchie 149830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 149930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 150030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1501d685218eacc23e69f685a76113665f50cc560edfritchie protected AsyncRunner asyncRunner; 150230fb85f55cbd8df3005e652da3781f51294baf90ritchie 150330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 150430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 150530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 150630fb85f55cbd8df3005e652da3781f51294baf90ritchie private TempFileManagerFactory tempFileManagerFactory; 150730fb85f55cbd8df3005e652da3781f51294baf90ritchie 150830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 150930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given port. 151030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 151130fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(int port) { 151230fb85f55cbd8df3005e652da3781f51294baf90ritchie this(null, port); 151330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 151430fb85f55cbd8df3005e652da3781f51294baf90ritchie 151530fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 151630fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 151730fb85f55cbd8df3005e652da3781f51294baf90ritchie // 151830fb85f55cbd8df3005e652da3781f51294baf90ritchie // Threading Strategy. 151930fb85f55cbd8df3005e652da3781f51294baf90ritchie // 152030fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 152130fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 152230fb85f55cbd8df3005e652da3781f51294baf90ritchie 152330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 152430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given hostname and port. 152530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 152630fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(String hostname, int port) { 152730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.hostname = hostname; 152830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myPort = port; 152930fb85f55cbd8df3005e652da3781f51294baf90ritchie setTempFileManagerFactory(new DefaultTempFileManagerFactory()); 153030fb85f55cbd8df3005e652da3781f51294baf90ritchie setAsyncRunner(new DefaultAsyncRunner()); 153130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 153230fb85f55cbd8df3005e652da3781f51294baf90ritchie 153330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 153430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Forcibly closes all connections that are open. 153530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 153630fb85f55cbd8df3005e652da3781f51294baf90ritchie public synchronized void closeAllConnections() { 1537abcf1089ce1de49278970f088883cb32acb4f225ritchie stop(); 1538abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1539abcf1089ce1de49278970f088883cb32acb4f225ritchie 1540abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1541abcf1089ce1de49278970f088883cb32acb4f225ritchie * create a instance of the client handler, subclasses can return a subclass 1542abcf1089ce1de49278970f088883cb32acb4f225ritchie * of the ClientHandler. 15439e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1544abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param finalAccept 1545abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socket the cleint is connected to 1546abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param inputStream 1547abcf1089ce1de49278970f088883cb32acb4f225ritchie * the input stream 1548abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the client handler 1549abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1550abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) { 1551abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ClientHandler(inputStream, finalAccept); 1552abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1553abcf1089ce1de49278970f088883cb32acb4f225ritchie 1554abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1555abcf1089ce1de49278970f088883cb32acb4f225ritchie * Instantiate the server runnable, can be overwritten by subclasses to 1556abcf1089ce1de49278970f088883cb32acb4f225ritchie * provide a subclass of the ServerRunnable. 15579e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1558abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param timeout 1559abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socet timeout to use. 1560abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the server runnable. 1561abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1562abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ServerRunnable createServerRunnable(final int timeout) { 1563abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ServerRunnable(timeout); 156430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 156530fb85f55cbd8df3005e652da3781f51294baf90ritchie 156630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 156730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 156830fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 156930fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 15709e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 157130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 157230fb85f55cbd8df3005e652da3781f51294baf90ritchie * original <b>NanoHTTPD</b> parameters values, as passed to the 157330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>serve()</code> method. 157430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 157530fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 157630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 157730fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(Map<String, String> parms) { 157830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER)); 157930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 158030fb85f55cbd8df3005e652da3781f51294baf90ritchie 1581abcf1089ce1de49278970f088883cb32acb4f225ritchie // ------------------------------------------------------------------------------- 1582abcf1089ce1de49278970f088883cb32acb4f225ritchie // // 1583abcf1089ce1de49278970f088883cb32acb4f225ritchie 158430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 158530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 158630fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 158730fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 15889e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 158930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param queryString 159030fb85f55cbd8df3005e652da3781f51294baf90ritchie * a query string pulled from the URL. 159130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 159230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 159330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 159430fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(String queryString) { 159530fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, List<String>> parms = new HashMap<String, List<String>>(); 159630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (queryString != null) { 159730fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(queryString, "&"); 159830fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 159930fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 160030fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 160130fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); 160230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!parms.containsKey(propertyName)) { 160330fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(propertyName, new ArrayList<String>()); 160430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160530fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null; 160630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (propertyValue != null) { 160730fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.get(propertyName).add(propertyValue); 160830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 161093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 161130fb85f55cbd8df3005e652da3781f51294baf90ritchie return parms; 161230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 161393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 161430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 161530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode percent encoded <code>String</code> values. 16169e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 161730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param str 161830fb85f55cbd8df3005e652da3781f51294baf90ritchie * the percent encoded <code>String</code> 161930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return expanded form of the input, for example "foo%20bar" becomes 162030fb85f55cbd8df3005e652da3781f51294baf90ritchie * "foo bar" 162130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 162230fb85f55cbd8df3005e652da3781f51294baf90ritchie protected String decodePercent(String str) { 162330fb85f55cbd8df3005e652da3781f51294baf90ritchie String decoded = null; 162430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 162530fb85f55cbd8df3005e652da3781f51294baf90ritchie decoded = URLDecoder.decode(str, "UTF8"); 162630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (UnsupportedEncodingException ignored) { 162730fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored); 162893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 162930fb85f55cbd8df3005e652da3781f51294baf90ritchie return decoded; 163030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 163193441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 163230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final int getListeningPort() { 163330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort(); 163430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1635964393fd7e5ed49088882a126cf82507184467efMartin M Reed 163630fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean isAlive() { 163730fb85f55cbd8df3005e652da3781f51294baf90ritchie return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); 16388dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1639964393fd7e5ed49088882a126cf82507184467efMartin M Reed 164030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 164130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Call before start() to serve over HTTPS instead of HTTP 164230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 164330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void makeSecure(SSLServerSocketFactory sslServerSocketFactory) { 164430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.sslServerSocketFactory = sslServerSocketFactory; 164530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 16469058464950a9734da0a7ff2dc47f3081bbb5117critchie 164730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 16485e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with unknown length (using HTTP 1.1 chunking). 16495e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 16505e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newChunkedResponse(IStatus status, String mimeType, InputStream data) { 16515e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, -1); 16525e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 16535e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 16545e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 16555e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with known length. 16565e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 16579e1ec7bff40d70d31953a04dd448665aaf549395ritchie public Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) { 16585e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, totalBytes); 16595e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 16605e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 16615e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 16625e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 16635e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 16645e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newFixedLengthResponse(IStatus status, String mimeType, String txt) { 16655e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (txt == null) { 16665e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0); 16675e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 16685e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie byte[] bytes; 16695e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie try { 16705e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = txt.getBytes("UTF-8"); 16715e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } catch (UnsupportedEncodingException e) { 16725e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e); 16735e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = new byte[0]; 16745e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 16755e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(bytes), bytes.length); 16765e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 16775e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 16785e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 16795e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 16805e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 16815e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 16825e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newFixedLengthResponse(String msg) { 16835e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg); 16845e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 16855e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 16865e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 168730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 168830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 168930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 169030fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 16919e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 169230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param session 169330fb85f55cbd8df3005e652da3781f51294baf90ritchie * The HTTP session 169430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 169530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 169630fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(IHTTPSession session) { 169730fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> files = new HashMap<String, String>(); 169830fb85f55cbd8df3005e652da3781f51294baf90ritchie Method method = session.getMethod(); 169930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.PUT.equals(method) || Method.POST.equals(method)) { 170030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 170130fb85f55cbd8df3005e652da3781f51294baf90ritchie session.parseBody(files); 170230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 17035e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 170430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 17055e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 170630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17075820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 17085820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 170930fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> parms = session.getParms(); 171030fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString()); 171130fb85f55cbd8df3005e652da3781f51294baf90ritchie return serve(session.getUri(), method, session.getHeaders(), parms, files); 171230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17135820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 171430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 171530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 171630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 171730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 171830fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 17199e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 172030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param uri 172130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Percent-decoded URI without parameters, for example 172230fb85f55cbd8df3005e652da3781f51294baf90ritchie * "/index.cgi" 172330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param method 172430fb85f55cbd8df3005e652da3781f51294baf90ritchie * "GET", "POST" etc. 172530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 172630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Parsed, percent decoded parameters from URI and, in case of 172730fb85f55cbd8df3005e652da3781f51294baf90ritchie * POST, data. 172830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param headers 172930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Header entries, percent decoded 173030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 173130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 173230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Deprecated 173330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) { 17345e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); 173530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17365820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 173730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 173830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 17399e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 174030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param asyncRunner 174130fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling threads. 174230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 174330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setAsyncRunner(AsyncRunner asyncRunner) { 174430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.asyncRunner = asyncRunner; 174530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17465820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 174730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 174830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 17499e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 175030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param tempFileManagerFactory 175130fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling temp files. 175230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 175330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { 175430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManagerFactory = tempFileManagerFactory; 17555820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 17565820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 17578dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 175830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Start the server. 17599e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 176030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @throws IOException 176130fb85f55cbd8df3005e652da3781f51294baf90ritchie * if the socket is in use. 17628dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 176330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void start() throws IOException { 1764c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie start(NanoHTTPD.SOCKET_READ_TIMEOUT); 1765c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie } 1766c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie 1767c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie /** 1768c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * Start the server. 17699e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1770c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @param timeout 1771c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * timeout to use for socket connections. 1772c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @throws IOException 1773c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * if the socket is in use. 1774c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie */ 1775c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie public void start(final int timeout) throws IOException { 177630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.sslServerSocketFactory != null) { 177730fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocket ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); 177830fb85f55cbd8df3005e652da3781f51294baf90ritchie ss.setNeedClientAuth(false); 177930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = ss; 178030fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 178130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = new ServerSocket(); 178230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 178330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.setReuseAddress(true); 178430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.bind(this.hostname != null ? new InetSocketAddress(this.hostname, this.myPort) : new InetSocketAddress(this.myPort)); 17859058464950a9734da0a7ff2dc47f3081bbb5117critchie 1786abcf1089ce1de49278970f088883cb32acb4f225ritchie this.myThread = new Thread(createServerRunnable(timeout)); 178730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setDaemon(true); 178830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setName("NanoHttpd Main Listener"); 178930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.start(); 179030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17915820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 179230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 179330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Stop the server. 179430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 179530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void stop() { 179630fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 179730fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.myServerSocket); 1798abcf1089ce1de49278970f088883cb32acb4f225ritchie this.asyncRunner.closeAll(); 179930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.myThread != null) { 180030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.join(); 180130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 180230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 180330fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e); 1804964393fd7e5ed49088882a126cf82507184467efMartin M Reed } 180530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1806964393fd7e5ed49088882a126cf82507184467efMartin M Reed 180730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean wasStarted() { 180830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket != null && this.myThread != null; 18097e423dde0fe863fadbd363de8a94746a30fca0eeMartin M Reed } 18105e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 1811269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 1812