NanoHTTPD.java revision 1f2440c5f2b5b511de0e76c02d5424ddd6be1482
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 36a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theanderimport java.io.*; 37b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetAddress; 38b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetSocketAddress; 39b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.ServerSocket; 40b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.Socket; 41b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.SocketException; 4213736e18ec88e3df74d055e061fb324e04778ad6Paul Hawkeimport java.net.SocketTimeoutException; 43b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.URLDecoder; 44dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.ByteBuffer; 45dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.channels.FileChannel; 46367e30264ff0572adbb53483a6a65cdebe406fbcritchieimport java.nio.charset.Charset; 4730fb85f55cbd8df3005e652da3781f51294baf90ritchieimport java.security.KeyStore; 487b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat; 49b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.ArrayList; 50b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Calendar; 51abcf1089ce1de49278970f088883cb32acb4f225ritchieimport java.util.Collections; 52b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Date; 53b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.HashMap; 54b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Iterator; 55b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.List; 56b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Locale; 57b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Map; 58b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.StringTokenizer; 59b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.TimeZone; 603526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Level; 613526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Logger; 62731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchieimport java.util.regex.Matcher; 63731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchieimport java.util.regex.Pattern; 64a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theanderimport java.util.zip.GZIPOutputStream; 65269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 6630fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManager; 6730fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManagerFactory; 6830fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLContext; 6930fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocket; 7030fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocketFactory; 7130fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.TrustManagerFactory; 72f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 735e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport fi.iki.elonen.NanoHTTPD.Response.IStatus; 745e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport fi.iki.elonen.NanoHTTPD.Response.Status; 755e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 76269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 775b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke * A simple, tiny, nicely embeddable HTTP server in Java 78d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 79d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 80b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke * NanoHTTPD 819058464950a9734da0a7ff2dc47f3081bbb5117critchie * <p> 829058464950a9734da0a7ff2dc47f3081bbb5117critchie * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 839058464950a9734da0a7ff2dc47f3081bbb5117critchie * 2010 by Konstantinos Togias 849058464950a9734da0a7ff2dc47f3081bbb5117critchie * </p> 85d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 86d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 87269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 88269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 89d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 90269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 9101ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li> 92269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 939058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>No fixed config files, logging, authorization etc. (Implement yourself if 949058464950a9734da0a7ff2dc47f3081bbb5117critchie * you need them.)</li> 959058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT 969058464950a9734da0a7ff2dc47f3081bbb5117critchie * support in 1.25)</li> 97269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 98269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 99269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 100269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 101269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 102269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 103269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 104269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 105269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 106269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 107269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 110cf230611ca85378ae2b0e8130dace5766edfd295Philipp Wiesemann * <li>Contains a built-in list of most common MIME types</li> 1119058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>All header names are converted to lower case so they don't vary between 1129058464950a9734da0a7ff2dc47f3081bbb5117critchie * browsers/clients</li> 113d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 114269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 115d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 116d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 11701ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b> 118269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 119d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 12001ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li> 121d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 122269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 123d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 1249058464950a9734da0a7ff2dc47f3081bbb5117critchie * See the separate "LICENSE.md" file for the distribution license (Modified BSD 1259058464950a9734da0a7ff2dc47f3081bbb5117critchie * licence) 126269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 127d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD { 1289058464950a9734da0a7ff2dc47f3081bbb5117critchie 129269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 13030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 131293e04675f3d4f427c125a26e831d70d5011c79bhflicka */ 13230fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface AsyncRunner { 1339058464950a9734da0a7ff2dc47f3081bbb5117critchie 134abcf1089ce1de49278970f088883cb32acb4f225ritchie void closeAll(); 135abcf1089ce1de49278970f088883cb32acb4f225ritchie 136abcf1089ce1de49278970f088883cb32acb4f225ritchie void closed(ClientHandler clientHandler); 137abcf1089ce1de49278970f088883cb32acb4f225ritchie 138abcf1089ce1de49278970f088883cb32acb4f225ritchie void exec(ClientHandler code); 139abcf1089ce1de49278970f088883cb32acb4f225ritchie } 140abcf1089ce1de49278970f088883cb32acb4f225ritchie 141abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 142abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for every new client connection. 143abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 144abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ClientHandler implements Runnable { 145abcf1089ce1de49278970f088883cb32acb4f225ritchie 146abcf1089ce1de49278970f088883cb32acb4f225ritchie private final InputStream inputStream; 147abcf1089ce1de49278970f088883cb32acb4f225ritchie 148abcf1089ce1de49278970f088883cb32acb4f225ritchie private final Socket acceptSocket; 149abcf1089ce1de49278970f088883cb32acb4f225ritchie 150abcf1089ce1de49278970f088883cb32acb4f225ritchie private ClientHandler(InputStream inputStream, Socket acceptSocket) { 151abcf1089ce1de49278970f088883cb32acb4f225ritchie this.inputStream = inputStream; 152abcf1089ce1de49278970f088883cb32acb4f225ritchie this.acceptSocket = acceptSocket; 153abcf1089ce1de49278970f088883cb32acb4f225ritchie } 154abcf1089ce1de49278970f088883cb32acb4f225ritchie 155abcf1089ce1de49278970f088883cb32acb4f225ritchie public void close() { 156abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 157abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 158abcf1089ce1de49278970f088883cb32acb4f225ritchie } 159abcf1089ce1de49278970f088883cb32acb4f225ritchie 160abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 161abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 162abcf1089ce1de49278970f088883cb32acb4f225ritchie OutputStream outputStream = null; 163abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 164abcf1089ce1de49278970f088883cb32acb4f225ritchie outputStream = this.acceptSocket.getOutputStream(); 165abcf1089ce1de49278970f088883cb32acb4f225ritchie TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create(); 166abcf1089ce1de49278970f088883cb32acb4f225ritchie HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress()); 167abcf1089ce1de49278970f088883cb32acb4f225ritchie while (!this.acceptSocket.isClosed()) { 168abcf1089ce1de49278970f088883cb32acb4f225ritchie session.execute(); 169abcf1089ce1de49278970f088883cb32acb4f225ritchie } 170abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (Exception e) { 171abcf1089ce1de49278970f088883cb32acb4f225ritchie // When the socket is closed by the client, 172abcf1089ce1de49278970f088883cb32acb4f225ritchie // we throw our own SocketException 173abcf1089ce1de49278970f088883cb32acb4f225ritchie // to break the "keep alive" loop above. If 174abcf1089ce1de49278970f088883cb32acb4f225ritchie // the exception was anything other 175abcf1089ce1de49278970f088883cb32acb4f225ritchie // than the expected SocketException OR a 176abcf1089ce1de49278970f088883cb32acb4f225ritchie // SocketTimeoutException, print the 177abcf1089ce1de49278970f088883cb32acb4f225ritchie // stacktrace 178abcf1089ce1de49278970f088883cb32acb4f225ritchie if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) { 179abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 180abcf1089ce1de49278970f088883cb32acb4f225ritchie } 181abcf1089ce1de49278970f088883cb32acb4f225ritchie } finally { 182abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(outputStream); 183abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 184abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 185abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.closed(this); 186abcf1089ce1de49278970f088883cb32acb4f225ritchie } 187abcf1089ce1de49278970f088883cb32acb4f225ritchie } 18830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1899058464950a9734da0a7ff2dc47f3081bbb5117critchie 19030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Cookie { 1919058464950a9734da0a7ff2dc47f3081bbb5117critchie 19230fb85f55cbd8df3005e652da3781f51294baf90ritchie public static String getHTTPTime(int days) { 19330fb85f55cbd8df3005e652da3781f51294baf90ritchie Calendar calendar = Calendar.getInstance(); 19430fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 19530fb85f55cbd8df3005e652da3781f51294baf90ritchie dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 19630fb85f55cbd8df3005e652da3781f51294baf90ritchie calendar.add(Calendar.DAY_OF_MONTH, days); 19730fb85f55cbd8df3005e652da3781f51294baf90ritchie return dateFormat.format(calendar.getTime()); 19830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 19930fb85f55cbd8df3005e652da3781f51294baf90ritchie 20030fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String n, v, e; 20130fb85f55cbd8df3005e652da3781f51294baf90ritchie 20230fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value) { 20330fb85f55cbd8df3005e652da3781f51294baf90ritchie this(name, value, 30); 20430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20530fb85f55cbd8df3005e652da3781f51294baf90ritchie 20630fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, int numDays) { 20730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 20830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 20930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = getHTTPTime(numDays); 21030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21130fb85f55cbd8df3005e652da3781f51294baf90ritchie 21230fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, String expires) { 21330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 21430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 21530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = expires; 21630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21730fb85f55cbd8df3005e652da3781f51294baf90ritchie 21830fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHTTPHeader() { 21930fb85f55cbd8df3005e652da3781f51294baf90ritchie String fmt = "%s=%s; expires=%s"; 22030fb85f55cbd8df3005e652da3781f51294baf90ritchie return String.format(fmt, this.n, this.v, this.e); 22130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2239058464950a9734da0a7ff2dc47f3081bbb5117critchie 2249058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 22530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Provides rudimentary support for cookies. Doesn't support 'path', 22630fb85f55cbd8df3005e652da3781f51294baf90ritchie * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported 22730fb85f55cbd8df3005e652da3781f51294baf90ritchie * features. 2289e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 22930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @author LordFokas 2309058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 23130fb85f55cbd8df3005e652da3781f51294baf90ritchie public class CookieHandler implements Iterable<String> { 2329058464950a9734da0a7ff2dc47f3081bbb5117critchie 23330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final HashMap<String, String> cookies = new HashMap<String, String>(); 2349058464950a9734da0a7ff2dc47f3081bbb5117critchie 23530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final ArrayList<Cookie> queue = new ArrayList<Cookie>(); 2369058464950a9734da0a7ff2dc47f3081bbb5117critchie 23730fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler(Map<String, String> httpHeaders) { 23830fb85f55cbd8df3005e652da3781f51294baf90ritchie String raw = httpHeaders.get("cookie"); 23930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (raw != null) { 24030fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] tokens = raw.split(";"); 24130fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String token : tokens) { 24230fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] data = token.trim().split("="); 24330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (data.length == 2) { 24430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.put(data[0], data[1]); 24530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 24630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 24730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 24830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2499058464950a9734da0a7ff2dc47f3081bbb5117critchie 25030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 25130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Set a cookie with an expiration date from a month ago, effectively 25230fb85f55cbd8df3005e652da3781f51294baf90ritchie * deleting it on the client side. 2539e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 25430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 25530fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie name. 25630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 25730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete(String name) { 25830fb85f55cbd8df3005e652da3781f51294baf90ritchie set(name, "-delete-", -30); 25930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2609058464950a9734da0a7ff2dc47f3081bbb5117critchie 26130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 26230fb85f55cbd8df3005e652da3781f51294baf90ritchie public Iterator<String> iterator() { 26330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.keySet().iterator(); 26430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2659058464950a9734da0a7ff2dc47f3081bbb5117critchie 26630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 26730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Read a cookie from the HTTP Headers. 2689e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 26930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 27030fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 27130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return The cookie's value if it exists, null otherwise. 27230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 27330fb85f55cbd8df3005e652da3781f51294baf90ritchie public String read(String name) { 27430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.get(name); 27530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2769058464950a9734da0a7ff2dc47f3081bbb5117critchie 27730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(Cookie cookie) { 27830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(cookie); 27930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2809058464950a9734da0a7ff2dc47f3081bbb5117critchie 28130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 28230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sets a cookie. 2839e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 28430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 28530fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 28630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param value 28730fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's value. 28830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param expires 28930fb85f55cbd8df3005e652da3781f51294baf90ritchie * How many days until the cookie expires. 29030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 29130fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(String name, String value, int expires) { 29230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); 29330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 294269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 29530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 29630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Internally used by the webserver to add all queued cookies into the 29730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Response's HTTP Headers. 2989e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 29930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param response 30030fb85f55cbd8df3005e652da3781f51294baf90ritchie * The Response object to which headers the queued cookies 30130fb85f55cbd8df3005e652da3781f51294baf90ritchie * will be added. 30230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 30330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void unloadQueue(Response response) { 30430fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Cookie cookie : this.queue) { 30530fb85f55cbd8df3005e652da3781f51294baf90ritchie response.addHeader("Set-Cookie", cookie.getHTTPHeader()); 30630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 30730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 308f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke } 309f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 3105b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 31130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default threading strategy for NanoHTTPD. 31230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 31330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 31430fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, the server spawns a new Thread for every incoming request. 31530fb85f55cbd8df3005e652da3781f51294baf90ritchie * These are set to <i>daemon</i> status, and named according to the request 31630fb85f55cbd8df3005e652da3781f51294baf90ritchie * number. The name is useful when profiling the application. 31730fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3185b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 31930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultAsyncRunner implements AsyncRunner { 3203f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke 32130fb85f55cbd8df3005e652da3781f51294baf90ritchie private long requestCount; 32230fb85f55cbd8df3005e652da3781f51294baf90ritchie 323abcf1089ce1de49278970f088883cb32acb4f225ritchie private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>()); 324abcf1089ce1de49278970f088883cb32acb4f225ritchie 325d685218eacc23e69f685a76113665f50cc560edfritchie /** 326d685218eacc23e69f685a76113665f50cc560edfritchie * @return a list with currently running clients. 327d685218eacc23e69f685a76113665f50cc560edfritchie */ 328d685218eacc23e69f685a76113665f50cc560edfritchie public List<ClientHandler> getRunning() { 329d685218eacc23e69f685a76113665f50cc560edfritchie return running; 330d685218eacc23e69f685a76113665f50cc560edfritchie } 331d685218eacc23e69f685a76113665f50cc560edfritchie 33230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 333abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closeAll() { 334abcf1089ce1de49278970f088883cb32acb4f225ritchie // copy of the list for concurrency 335abcf1089ce1de49278970f088883cb32acb4f225ritchie for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) { 336abcf1089ce1de49278970f088883cb32acb4f225ritchie clientHandler.close(); 337abcf1089ce1de49278970f088883cb32acb4f225ritchie } 338abcf1089ce1de49278970f088883cb32acb4f225ritchie } 339abcf1089ce1de49278970f088883cb32acb4f225ritchie 340abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 341abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closed(ClientHandler clientHandler) { 342abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.remove(clientHandler); 343abcf1089ce1de49278970f088883cb32acb4f225ritchie } 344abcf1089ce1de49278970f088883cb32acb4f225ritchie 345abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 346abcf1089ce1de49278970f088883cb32acb4f225ritchie public void exec(ClientHandler clientHandler) { 34730fb85f55cbd8df3005e652da3781f51294baf90ritchie ++this.requestCount; 348abcf1089ce1de49278970f088883cb32acb4f225ritchie Thread t = new Thread(clientHandler); 34930fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setDaemon(true); 35030fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")"); 351abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.add(clientHandler); 35230fb85f55cbd8df3005e652da3781f51294baf90ritchie t.start(); 353c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 354c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 355c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke 3569058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 35730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 35830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 35930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 36030fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, files are created by <code>File.createTempFile()</code> in 36130fb85f55cbd8df3005e652da3781f51294baf90ritchie * the directory specified. 36230fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3639058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 36430fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFile implements TempFile { 36530fb85f55cbd8df3005e652da3781f51294baf90ritchie 36630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final File file; 36730fb85f55cbd8df3005e652da3781f51294baf90ritchie 36830fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream fstream; 36930fb85f55cbd8df3005e652da3781f51294baf90ritchie 37030fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFile(String tempdir) throws IOException { 37130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.file = File.createTempFile("NanoHTTPD-", "", new File(tempdir)); 37230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.fstream = new FileOutputStream(this.file); 3739058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3749058464950a9734da0a7ff2dc47f3081bbb5117critchie 37530fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 37630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete() throws Exception { 37730fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.fstream); 3786034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann if (!this.file.delete()) { 3796034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann throw new Exception("could not delete temporary file"); 3806034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann } 3819058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3829058464950a9734da0a7ff2dc47f3081bbb5117critchie 38330fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38430fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getName() { 38530fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.file.getAbsolutePath(); 3869058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3879058464950a9734da0a7ff2dc47f3081bbb5117critchie 38830fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38930fb85f55cbd8df3005e652da3781f51294baf90ritchie public OutputStream open() throws Exception { 39030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.fstream; 39130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 3929058464950a9734da0a7ff2dc47f3081bbb5117critchie } 393f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 3943f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke /** 39530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 39630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 39730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 39830fb85f55cbd8df3005e652da3781f51294baf90ritchie * This class stores its files in the standard location (that is, wherever 39930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>java.io.tmpdir</code> points to). Files are added to an internal 40030fb85f55cbd8df3005e652da3781f51294baf90ritchie * list, and deleted when no longer needed (that is, when 40130fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>clear()</code> is invoked at the end of processing a request). 40230fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 4033f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke */ 40430fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFileManager implements TempFileManager { 405f39ce1dd765bb79b0a83a8e6dd921f0d3986ebf6Aaron Davidson 40630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String tmpdir; 4079058464950a9734da0a7ff2dc47f3081bbb5117critchie 40830fb85f55cbd8df3005e652da3781f51294baf90ritchie private final List<TempFile> tempFiles; 4099058464950a9734da0a7ff2dc47f3081bbb5117critchie 41030fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFileManager() { 41130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tmpdir = System.getProperty("java.io.tmpdir"); 41230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles = new ArrayList<TempFile>(); 41330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 414d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 41530fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 41630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void clear() { 41730fb85f55cbd8df3005e652da3781f51294baf90ritchie for (TempFile file : this.tempFiles) { 41830fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 41930fb85f55cbd8df3005e652da3781f51294baf90ritchie file.delete(); 42030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception ignored) { 42130fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); 42230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 423fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 42430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.clear(); 425269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 426269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 42730fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 4281f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen public TempFile createTempFile(String filename_hint) throws Exception { 42930fb85f55cbd8df3005e652da3781f51294baf90ritchie DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); 43030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.add(tempFile); 43130fb85f55cbd8df3005e652da3781f51294baf90ritchie return tempFile; 43230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 4330a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4340a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 4350a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander /** 43630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 4370a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander */ 43830fb85f55cbd8df3005e652da3781f51294baf90ritchie private class DefaultTempFileManagerFactory implements TempFileManagerFactory { 4390a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 44030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 44130fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFileManager create() { 44230fb85f55cbd8df3005e652da3781f51294baf90ritchie return new DefaultTempFileManager(); 4430a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4440a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4458dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 446731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)"; 447731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 448731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE); 449731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 450731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)"; 451731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 452731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE); 453731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 454731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]"; 455731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 456731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX); 457731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 45830fb85f55cbd8df3005e652da3781f51294baf90ritchie protected class HTTPSession implements IHTTPSession { 4598dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int BUFSIZE = 8192; 4618dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46230fb85f55cbd8df3005e652da3781f51294baf90ritchie private final TempFileManager tempFileManager; 4638dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46430fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream outputStream; 4659788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 46630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final PushbackInputStream inputStream; 46793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 46830fb85f55cbd8df3005e652da3781f51294baf90ritchie private int splitbyte; 46993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 47030fb85f55cbd8df3005e652da3781f51294baf90ritchie private int rlen; 471f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 47230fb85f55cbd8df3005e652da3781f51294baf90ritchie private String uri; 473f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 47430fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method method; 475f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 47630fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> parms; 4775b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 47830fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> headers; 4795b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48030fb85f55cbd8df3005e652da3781f51294baf90ritchie private CookieHandler cookies; 4815b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48230fb85f55cbd8df3005e652da3781f51294baf90ritchie private String queryParameterString; 4835b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48430fb85f55cbd8df3005e652da3781f51294baf90ritchie private String remoteIp; 4858dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 486dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen private String protocolVersion; 487dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 48830fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { 48930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 49030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 49130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 4928dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 4938dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 49430fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) { 49530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 49630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 49730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 49830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString(); 49930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 5009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 5019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 5029788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 50330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the sent headers and loads the data into Key/value pairs 5049788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 50530fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException { 5069788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke try { 50730fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the request line 50830fb85f55cbd8df3005e652da3781f51294baf90ritchie String inLine = in.readLine(); 50930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (inLine == null) { 51030fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 51130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 512fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 51330fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(inLine); 51430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 51530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 51630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 5179788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 51830fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("method", st.nextToken()); 5197f0727787957c2f093412c01d165846842ec2425Paul Hawke 52030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 52130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 5227f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5237f0727787957c2f093412c01d165846842ec2425Paul Hawke 52430fb85f55cbd8df3005e652da3781f51294baf90ritchie String uri = st.nextToken(); 5257f0727787957c2f093412c01d165846842ec2425Paul Hawke 52630fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode parameters from the URI 52730fb85f55cbd8df3005e652da3781f51294baf90ritchie int qmi = uri.indexOf('?'); 52830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (qmi >= 0) { 52930fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(uri.substring(qmi + 1), parms); 53030fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri.substring(0, qmi)); 53130fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 53230fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri); 5337f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5347f0727787957c2f093412c01d165846842ec2425Paul Hawke 53530fb85f55cbd8df3005e652da3781f51294baf90ritchie // If there's another token, its protocol version, 536dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen // followed by HTTP headers. 53730fb85f55cbd8df3005e652da3781f51294baf90ritchie // NOTE: this now forces header names lower case since they are 53830fb85f55cbd8df3005e652da3781f51294baf90ritchie // case insensitive and vary by client. 539de2bb1bc9fa3959846741e1fd14076971e17b90cElonen if (st.hasMoreTokens()) { 540dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen protocolVersion = st.nextToken(); 541de2bb1bc9fa3959846741e1fd14076971e17b90cElonen } else { 542dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen protocolVersion = "HTTP/1.1"; 543dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1."); 54430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 54530fb85f55cbd8df3005e652da3781f51294baf90ritchie String line = in.readLine(); 54630fb85f55cbd8df3005e652da3781f51294baf90ritchie while (line != null && line.trim().length() > 0) { 54730fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = line.indexOf(':'); 54830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p >= 0) { 54930fb85f55cbd8df3005e652da3781f51294baf90ritchie headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim()); 5507f0727787957c2f093412c01d165846842ec2425Paul Hawke } 55130fb85f55cbd8df3005e652da3781f51294baf90ritchie line = in.readLine(); 5527f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5537f0727787957c2f093412c01d165846842ec2425Paul Hawke 55430fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("uri", uri); 5557f0727787957c2f093412c01d165846842ec2425Paul Hawke } catch (IOException ioe) { 55630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 5577f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5587f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5597f0727787957c2f093412c01d165846842ec2425Paul Hawke 56030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 56130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the Multipart Body data and put it into Key/Value pairs. 56230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 5633cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen private void decodeMultipartFormData(String boundary, ByteBuffer fbuf, Map<String, String> parms, Map<String, String> files) throws ResponseException { 56430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 5653cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int[] boundary_idxs = getBoundaryPositions(fbuf, boundary.getBytes()); 5663cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (boundary_idxs.length < 2) { 5673cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings."); 5683cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 5693cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 5705e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen final int MAX_HEADER_SIZE = 1024; 5715e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen byte[] part_header_buff = new byte[MAX_HEADER_SIZE]; 5723cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen for (int bi = 0; bi < boundary_idxs.length - 1; bi++) { 5733cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.position(boundary_idxs[bi]); 5745e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE; 5753cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(part_header_buff, 0, len); 5763cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen ByteArrayInputStream bais = new ByteArrayInputStream(part_header_buff, 0, len); 577367e30264ff0572adbb53483a6a65cdebe406fbcritchie BufferedReader in = new BufferedReader(new InputStreamReader(bais, Charset.forName("US-ASCII"))); 5783cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 5793cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // First line is boundary string 5803cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String mpline = in.readLine(); 58130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!mpline.contains(boundary)) { 5823cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary."); 58330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 5843cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 5853cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String part_name = null, file_name = null, content_type = null; 5863cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Parse the reset of the header lines 58730fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 58830fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && mpline.trim().length() > 0) { 589731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline); 590731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (matcher.matches()) { 591731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie String attributeString = matcher.group(2); 592731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString); 593731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie while (matcher.find()) { 594731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie String key = matcher.group(1); 595731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (key.equalsIgnoreCase("name")) { 596731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie part_name = matcher.group(2); 597731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie } else if (key.equalsIgnoreCase("filename")) { 598731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie file_name = matcher.group(2); 59930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 60030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6013cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 602731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie matcher = CONTENT_TYPE_PATTERN.matcher(mpline); 603731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (matcher.matches()) { 604731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie content_type = matcher.group(2).trim(); 605731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie } 6063cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen mpline = in.readLine(); 6073cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6083cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6093cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read the part data 6105e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen int part_header_len = len - (int) in.skip(MAX_HEADER_SIZE); 6113cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (part_header_len >= len - 4) { 6125e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen throw new ResponseException(Response.Status.INTERNAL_ERROR, "Multipart header size exceeds MAX_HEADER_SIZE."); 6133cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6143cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int part_data_start = boundary_idxs[bi] + part_header_len; 6153cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int part_data_end = boundary_idxs[bi + 1] - 4; 6163cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6173cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.position(part_data_start); 6183cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (content_type == null) { 6193cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read the part into a string 6203cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen byte[] data_bytes = new byte[part_data_end - part_data_start]; 6213cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(data_bytes); 6223cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen parms.put(part_name, new String(data_bytes)); 6233cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } else { 6243cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read it into a file 6251f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen String path = saveTmpFile(fbuf, part_data_start, part_data_end - part_data_start, file_name); 6263cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (!files.containsKey(part_name)) { 6273cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen files.put(part_name, path); 62830fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 6293cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int count = 2; 6303cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen while (files.containsKey(part_name + count)) { 6313cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen count++; 63230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6333cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen files.put(part_name + count, path); 63430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6353cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen parms.put(part_name, file_name); 63698d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 63798d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 6383cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } catch (ResponseException re) { 6393cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw re; 6403cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } catch (Exception e) { 6413cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.INTERNAL_ERROR, e.toString()); 642c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 643fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 644fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 64530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 64630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes parameters in percent-encoded URI-format ( e.g. 64730fb85f55cbd8df3005e652da3781f51294baf90ritchie * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 64830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Map. NOTE: this doesn't support multiple identical keys due to the 64930fb85f55cbd8df3005e652da3781f51294baf90ritchie * simplicity of Map. 65030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 65130fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeParms(String parms, Map<String, String> p) { 65230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (parms == null) { 65330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = ""; 65430fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 655c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 656c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke 65730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = parms; 65830fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(parms, "&"); 65930fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 66030fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 66130fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 66230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (sep >= 0) { 66330fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); 66430fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 66530fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e).trim(), ""); 66630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 667fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 668fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 669fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 67030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 67130fb85f55cbd8df3005e652da3781f51294baf90ritchie public void execute() throws IOException { 672bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite Response r = null; 67330fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 67430fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the first 8192 bytes. 67530fb85f55cbd8df3005e652da3781f51294baf90ritchie // The full header should fit in here. 67630fb85f55cbd8df3005e652da3781f51294baf90ritchie // Apache's default header limit is 8KB. 67730fb85f55cbd8df3005e652da3781f51294baf90ritchie // Do NOT assume that a single read will get the entire header 67830fb85f55cbd8df3005e652da3781f51294baf90ritchie // at once! 67930fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buf = new byte[HTTPSession.BUFSIZE]; 68030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.splitbyte = 0; 68130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = 0; 6829c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie 6839c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie int read = -1; 6849c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie try { 6859c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); 6869c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } catch (Exception e) { 6879c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6889c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6899c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6909c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6919c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (read == -1) { 6929c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie // socket was been closed 6939c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6949c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6959c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6969c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6979c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie while (read > 0) { 6989c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.rlen += read; 6999c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.splitbyte = findHeaderEnd(buf, this.rlen); 7009c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (this.splitbyte > 0) { 7019c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie break; 70230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 7039c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); 70430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 70530fb85f55cbd8df3005e652da3781f51294baf90ritchie 70630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.splitbyte < this.rlen) { 70730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream.unread(buf, this.splitbyte, this.rlen - this.splitbyte); 70830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 70930fb85f55cbd8df3005e652da3781f51294baf90ritchie 71030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.parms = new HashMap<String, String>(); 71130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null == this.headers) { 71230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 71330fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 71430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.clear(); 71530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 71630fb85f55cbd8df3005e652da3781f51294baf90ritchie 71730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null != this.remoteIp) { 71830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("remote-addr", this.remoteIp); 71930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("http-client-ip", this.remoteIp); 72030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 72130fb85f55cbd8df3005e652da3781f51294baf90ritchie 72230fb85f55cbd8df3005e652da3781f51294baf90ritchie // Create a BufferedReader for parsing the header. 72330fb85f55cbd8df3005e652da3781f51294baf90ritchie BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen))); 72430fb85f55cbd8df3005e652da3781f51294baf90ritchie 72530fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode the header into parms and header java properties 72630fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> pre = new HashMap<String, String>(); 72730fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeHeader(hin, pre, this.parms, this.headers); 72830fb85f55cbd8df3005e652da3781f51294baf90ritchie 72930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.method = Method.lookup(pre.get("method")); 73030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.method == null) { 73130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); 73230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 73330fb85f55cbd8df3005e652da3781f51294baf90ritchie 73430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.uri = pre.get("uri"); 73530fb85f55cbd8df3005e652da3781f51294baf90ritchie 73630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies = new CookieHandler(this.headers); 73730fb85f55cbd8df3005e652da3781f51294baf90ritchie 738dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen String connection = this.headers.get("connection"); 739dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen boolean keepAlive = protocolVersion.equals("HTTP/1.1") && (connection == null || !connection.matches("(?i).*close.*")); 740dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 74130fb85f55cbd8df3005e652da3781f51294baf90ritchie // Ok, now do the serve() 7420760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen 7430760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // TODO: long body_size = getBodySize(); 7440760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // TODO: long pos_before_serve = this.inputStream.totalRead() 7450760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // (requires implementaion for totalRead()) 746bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite r = serve(this); 7470760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // TODO: this.inputStream.skip(body_size - 7480760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // (this.inputStream.totalRead() - pos_before_serve)) 7490760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen 75030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (r == null) { 75130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 75230fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 753a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander String acceptEncoding = this.headers.get("accept-encoding"); 75430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.unloadQueue(r); 75530fb85f55cbd8df3005e652da3781f51294baf90ritchie r.setRequestMethod(this.method); 756ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen r.setGzipEncoding(useGzipWhenAccepted(r) && acceptEncoding != null && acceptEncoding.contains("gzip")); 757dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen r.setKeepAlive(keepAlive); 75830fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 75930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 760dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (!keepAlive || "close".equalsIgnoreCase(r.getHeader("connection"))) { 761dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen throw new SocketException("NanoHttpd Shutdown"); 762dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 76330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketException e) { 76430fb85f55cbd8df3005e652da3781f51294baf90ritchie // throw it out to close socket object (finalAccept) 76530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw e; 76630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketTimeoutException ste) { 76730fb85f55cbd8df3005e652da3781f51294baf90ritchie // treat socket timeouts the same way we treat socket exceptions 76830fb85f55cbd8df3005e652da3781f51294baf90ritchie // i.e. close the stream & finalAccept object by throwing the 76930fb85f55cbd8df3005e652da3781f51294baf90ritchie // exception up the call stack. 77030fb85f55cbd8df3005e652da3781f51294baf90ritchie throw ste; 77130fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 772bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 773bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite resp.send(this.outputStream); 77430fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 77530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 776bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite Response resp = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 777bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite resp.send(this.outputStream); 77830fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 77930fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 780bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite safeClose(r); 78130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager.clear(); 782fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 783fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 784fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 78530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 78630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find byte index separating header from body. It must be the last byte 78730fb85f55cbd8df3005e652da3781f51294baf90ritchie * of the first two sequential new lines. 78830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 78930fb85f55cbd8df3005e652da3781f51294baf90ritchie private int findHeaderEnd(final byte[] buf, int rlen) { 79030fb85f55cbd8df3005e652da3781f51294baf90ritchie int splitbyte = 0; 79130fb85f55cbd8df3005e652da3781f51294baf90ritchie while (splitbyte + 3 < rlen) { 79230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 79330fb85f55cbd8df3005e652da3781f51294baf90ritchie return splitbyte + 4; 794fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 79530fb85f55cbd8df3005e652da3781f51294baf90ritchie splitbyte++; 796fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 79730fb85f55cbd8df3005e652da3781f51294baf90ritchie return 0; 798fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 799fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 80030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 801af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * Find the byte positions where multipart boundaries start. This reads 802af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * a large block at a time and uses a temporary buffer to optimize 803af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * (memory mapped) file access. 80430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 80530fb85f55cbd8df3005e652da3781f51294baf90ritchie private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) { 806af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int[] res = new int[0]; 807af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (b.remaining() < boundary.length) { 808af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen return res; 809af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } 810af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 811af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int search_window_pos = 0; 812af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen byte[] search_window = new byte[4 * 1024 + boundary.length]; 813af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 814af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length; 815af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen b.get(search_window, 0, first_fill); 816af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int new_bytes = first_fill - boundary.length; 817af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 818af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen do { 819af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Search the search_window 820af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen for (int j = 0; j < new_bytes; j++) { 821af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen for (int i = 0; i < boundary.length; i++) { 822af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (search_window[j + i] != boundary[i]) 823af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen break; 824af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (i == boundary.length - 1) { 825af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Match found, add it to results 826af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int[] new_res = new int[res.length + 1]; 827af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen System.arraycopy(res, 0, new_res, 0, res.length); 828af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_res[res.length] = search_window_pos + j; 829af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen res = new_res; 830af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } 83130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 83230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 833af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen search_window_pos += new_bytes; 834af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 835af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Copy the end of the buffer to the start 836af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, boundary.length); 837af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 838af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Refill search_window 839af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_bytes = search_window.length - boundary.length; 840af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes; 841af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen b.get(search_window, boundary.length, new_bytes); 842af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } while (new_bytes > 0); 843af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen return res; 844c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 845c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 84630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 84730fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler getCookies() { 84830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies; 849c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 850c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 85130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 85230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getHeaders() { 85330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.headers; 854c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 855c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 85630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 85730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final InputStream getInputStream() { 85830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.inputStream; 859c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 860c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 86130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 86230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Method getMethod() { 86330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.method; 864c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 865c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 86630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 86730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getParms() { 86830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.parms; 869c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 870c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 87130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 87230fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getQueryParameterString() { 87330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.queryParameterString; 874c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 875c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 87630fb85f55cbd8df3005e652da3781f51294baf90ritchie private RandomAccessFile getTmpBucket() { 87730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 8781f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen TempFile tempFile = this.tempFileManager.createTempFile(null); 87930fb85f55cbd8df3005e652da3781f51294baf90ritchie return new RandomAccessFile(tempFile.getName(), "rw"); 88030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 88130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 8829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8847e4e4ae652c755faea626f1e2538d495f96e648esynapticloop 885bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 88630fb85f55cbd8df3005e652da3781f51294baf90ritchie public final String getUri() { 88730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.uri; 88893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 88993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 8900760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen /** 8910760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen * Deduce body length in bytes. Either from "content-length" header or 8920760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen * read bytes. 8930760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen */ 8940760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen public long getBodySize() { 8950760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen if (this.headers.containsKey("content-length")) { 8960760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen return Integer.parseInt(this.headers.get("content-length")); 8970760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen } else if (this.splitbyte < this.rlen) { 8980760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen return this.rlen - this.splitbyte; 8990760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen } 9000760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen return 0; 9010760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen } 9020760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen 903bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 904bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel public void parseBody(Map<String, String> files) throws IOException, ResponseException { 9056625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen final int REQUEST_BUFFER_LEN = 512; 906770aaf0c6567d8b62a6eef18c8f0359208427c77Jarno Elonen final int MEMORY_STORE_LIMIT = 1024; 90793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed RandomAccessFile randomAccessFile = null; 90893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed try { 9090760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen long size = getBodySize(); 9106625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ByteArrayOutputStream baos = null; 9116625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen DataOutput request_data_output = null; 9126625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen 9136625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen // Store the request in memory or a file, depending on size 914770aaf0c6567d8b62a6eef18c8f0359208427c77Jarno Elonen if (size < MEMORY_STORE_LIMIT) { 9156625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen baos = new ByteArrayOutputStream(); 9166625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output = new DataOutputStream(baos); 9176625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 9186625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen randomAccessFile = getTmpBucket(); 9196625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output = randomAccessFile; 9206625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 9216625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen 9226625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen // Read all the body and write it to request_data_output 9236625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen byte[] buf = new byte[REQUEST_BUFFER_LEN]; 92430fb85f55cbd8df3005e652da3781f51294baf90ritchie while (this.rlen >= 0 && size > 0) { 9256625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN)); 92630fb85f55cbd8df3005e652da3781f51294baf90ritchie size -= this.rlen; 92730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.rlen > 0) { 9286625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output.write(buf, 0, this.rlen); 9290cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 930269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 931269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9326625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ByteBuffer fbuf = null; 9336625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (baos != null) { 9346625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size()); 9356625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 9366625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); 9376625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen randomAccessFile.seek(0); 9386625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 939269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 940269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 941269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 94230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.POST.equals(this.method)) { 943269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 94430fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentTypeHeader = this.headers.get("content-type"); 9452b54eda5fee8430268a99d73b061e790362db544Tom Hermann 9462c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke StringTokenizer st = null; 9472c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (contentTypeHeader != null) { 9482c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke st = new StringTokenizer(contentTypeHeader, ",; "); 9492c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (st.hasMoreTokens()) { 9502c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke contentType = st.nextToken(); 9512c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke } 952269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 953269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9540277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke if ("multipart/form-data".equalsIgnoreCase(contentType)) { 955269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 9560cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 9579058464950a9734da0a7ff2dc47f3081bbb5117critchie throw new ResponseException(Response.Status.BAD_REQUEST, 9589058464950a9734da0a7ff2dc47f3081bbb5117critchie "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 9590cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 9602b54eda5fee8430268a99d73b061e790362db544Tom Hermann 9614e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundaryStartString = "boundary="; 9622b54eda5fee8430268a99d73b061e790362db544Tom Hermann int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length(); 9634e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length()); 964220e1a21e7bbb831d06551c72799dfedc1db979fPaul Hawke if (boundary.startsWith("\"") && boundary.endsWith("\"")) { 9654e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke boundary = boundary.substring(1, boundary.length() - 1); 9664e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 967269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9683cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen decodeMultipartFormData(boundary, fbuf, this.parms, files); 969269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 9703cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen byte[] postBytes = new byte[fbuf.remaining()]; 9713cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(postBytes); 9723cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String postLine = new String(postBytes).trim(); 973e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown // Handle application/x-www-form-urlencoded 974e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) { 97530fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(postLine, this.parms); 976e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } else if (postLine.length() != 0) { 9779058464950a9734da0a7ff2dc47f3081bbb5117critchie // Special case for raw POST data => create a 9789058464950a9734da0a7ff2dc47f3081bbb5117critchie // special files entry "postData" with raw content 9799058464950a9734da0a7ff2dc47f3081bbb5117critchie // data 9809058464950a9734da0a7ff2dc47f3081bbb5117critchie files.put("postData", postLine); 981e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } 982269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 98330fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (Method.PUT.equals(this.method)) { 9841f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen files.put("content", saveTmpFile(fbuf, 0, fbuf.limit(), null)); 985b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } 9864e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } finally { 9879cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed safeClose(randomAccessFile); 988269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 989269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 990269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 991269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 99230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Retrieves the content of a sent file and saves it to a temporary 99330fb85f55cbd8df3005e652da3781f51294baf90ritchie * file. The full path to the saved file is returned. 994d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 9951f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) { 99630fb85f55cbd8df3005e652da3781f51294baf90ritchie String path = ""; 99730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (len > 0) { 99830fb85f55cbd8df3005e652da3781f51294baf90ritchie FileOutputStream fileOutputStream = null; 99930fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 10001f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen TempFile tempFile = this.tempFileManager.createTempFile(filename_hint); 100130fb85f55cbd8df3005e652da3781f51294baf90ritchie ByteBuffer src = b.duplicate(); 100230fb85f55cbd8df3005e652da3781f51294baf90ritchie fileOutputStream = new FileOutputStream(tempFile.getName()); 100330fb85f55cbd8df3005e652da3781f51294baf90ritchie FileChannel dest = fileOutputStream.getChannel(); 100430fb85f55cbd8df3005e652da3781f51294baf90ritchie src.position(offset).limit(offset + len); 100530fb85f55cbd8df3005e652da3781f51294baf90ritchie dest.write(src.slice()); 100630fb85f55cbd8df3005e652da3781f51294baf90ritchie path = tempFile.getName(); 100730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { // Catch exception if any 100830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 100930fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 101030fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(fileOutputStream); 10110cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 101230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 101330fb85f55cbd8df3005e652da3781f51294baf90ritchie return path; 101430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 101530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1016269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 101730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 101830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Handles one session, i.e. parses the HTTP request and returns the 101930fb85f55cbd8df3005e652da3781f51294baf90ritchie * response. 102030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 102130fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IHTTPSession { 1022269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 102330fb85f55cbd8df3005e652da3781f51294baf90ritchie void execute() throws IOException; 1024269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 102530fb85f55cbd8df3005e652da3781f51294baf90ritchie CookieHandler getCookies(); 1026269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 102730fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getHeaders(); 1028269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 102930fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream getInputStream(); 103030fb85f55cbd8df3005e652da3781f51294baf90ritchie 103130fb85f55cbd8df3005e652da3781f51294baf90ritchie Method getMethod(); 103230fb85f55cbd8df3005e652da3781f51294baf90ritchie 103330fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getParms(); 103430fb85f55cbd8df3005e652da3781f51294baf90ritchie 103530fb85f55cbd8df3005e652da3781f51294baf90ritchie String getQueryParameterString(); 1036269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1037269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 103830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return the path part of the URL. 1039d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 104030fb85f55cbd8df3005e652da3781f51294baf90ritchie String getUri(); 1041269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1042269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 104330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds the files in the request body to the files map. 10449e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 104530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param files 104630fb85f55cbd8df3005e652da3781f51294baf90ritchie * map to modify 1047d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 104830fb85f55cbd8df3005e652da3781f51294baf90ritchie void parseBody(Map<String, String> files) throws IOException, ResponseException; 104930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 105030fb85f55cbd8df3005e652da3781f51294baf90ritchie 105130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 105230fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP Request methods, with the ability to decode a <code>String</code> 105330fb85f55cbd8df3005e652da3781f51294baf90ritchie * back to its enum value. 105430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 105530fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Method { 105630fb85f55cbd8df3005e652da3781f51294baf90ritchie GET, 105730fb85f55cbd8df3005e652da3781f51294baf90ritchie PUT, 105830fb85f55cbd8df3005e652da3781f51294baf90ritchie POST, 105930fb85f55cbd8df3005e652da3781f51294baf90ritchie DELETE, 106030fb85f55cbd8df3005e652da3781f51294baf90ritchie HEAD, 10615fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen OPTIONS, 10625fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen TRACE, 10635fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen CONNECT, 10645fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen PATCH; 106530fb85f55cbd8df3005e652da3781f51294baf90ritchie 106630fb85f55cbd8df3005e652da3781f51294baf90ritchie static Method lookup(String method) { 106730fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Method m : Method.values()) { 106830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (m.toString().equalsIgnoreCase(method)) { 106930fb85f55cbd8df3005e652da3781f51294baf90ritchie return m; 1070269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1071269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 107230fb85f55cbd8df3005e652da3781f51294baf90ritchie return null; 107330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 107430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 107530fb85f55cbd8df3005e652da3781f51294baf90ritchie 107630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 107730fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP response. Return one of these from serve(). 107830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1079bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite public static class Response implements Closeable { 108030fb85f55cbd8df3005e652da3781f51294baf90ritchie 108130fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IStatus { 108230fb85f55cbd8df3005e652da3781f51294baf90ritchie 108330fb85f55cbd8df3005e652da3781f51294baf90ritchie String getDescription(); 108430fb85f55cbd8df3005e652da3781f51294baf90ritchie 108530fb85f55cbd8df3005e652da3781f51294baf90ritchie int getRequestStatus(); 1086269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1087269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1088269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 108930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Some HTTP response status codes 1090d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 109130fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Status implements IStatus { 109230fb85f55cbd8df3005e652da3781f51294baf90ritchie SWITCH_PROTOCOL(101, "Switching Protocols"), 109330fb85f55cbd8df3005e652da3781f51294baf90ritchie OK(200, "OK"), 109430fb85f55cbd8df3005e652da3781f51294baf90ritchie CREATED(201, "Created"), 109530fb85f55cbd8df3005e652da3781f51294baf90ritchie ACCEPTED(202, "Accepted"), 109630fb85f55cbd8df3005e652da3781f51294baf90ritchie NO_CONTENT(204, "No Content"), 109730fb85f55cbd8df3005e652da3781f51294baf90ritchie PARTIAL_CONTENT(206, "Partial Content"), 109830fb85f55cbd8df3005e652da3781f51294baf90ritchie REDIRECT(301, "Moved Permanently"), 109930fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_MODIFIED(304, "Not Modified"), 110030fb85f55cbd8df3005e652da3781f51294baf90ritchie BAD_REQUEST(400, "Bad Request"), 110130fb85f55cbd8df3005e652da3781f51294baf90ritchie UNAUTHORIZED(401, "Unauthorized"), 110230fb85f55cbd8df3005e652da3781f51294baf90ritchie FORBIDDEN(403, "Forbidden"), 110330fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_FOUND(404, "Not Found"), 110430fb85f55cbd8df3005e652da3781f51294baf90ritchie METHOD_NOT_ALLOWED(405, "Method Not Allowed"), 110512e6c0c5f70a6e2120ce03579884172d40093542luctrudeau REQUEST_TIMEOUT(408, "Request Timeout"), 110630fb85f55cbd8df3005e652da3781f51294baf90ritchie RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), 1107de2bb1bc9fa3959846741e1fd14076971e17b90cElonen INTERNAL_ERROR(500, "Internal Server Error"), 1108de2bb1bc9fa3959846741e1fd14076971e17b90cElonen UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported"); 110930fb85f55cbd8df3005e652da3781f51294baf90ritchie 111030fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int requestStatus; 111130fb85f55cbd8df3005e652da3781f51294baf90ritchie 111230fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String description; 111330fb85f55cbd8df3005e652da3781f51294baf90ritchie 111430fb85f55cbd8df3005e652da3781f51294baf90ritchie Status(int requestStatus, String description) { 111530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestStatus = requestStatus; 111630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.description = description; 1117269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 111830fb85f55cbd8df3005e652da3781f51294baf90ritchie 111930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 112030fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getDescription() { 112130fb85f55cbd8df3005e652da3781f51294baf90ritchie return "" + this.requestStatus + " " + this.description; 112230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 112330fb85f55cbd8df3005e652da3781f51294baf90ritchie 112430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 112530fb85f55cbd8df3005e652da3781f51294baf90ritchie public int getRequestStatus() { 112630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestStatus; 1127269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1128a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1129a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1130a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1131a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander /** 1132a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * Output stream that will automatically send every write to the wrapped 1133a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * OutputStream according to chunked transfer: 1134a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 1135a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander */ 1136a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private static class ChunkedOutputStream extends FilterOutputStream { 1137a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1138a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public ChunkedOutputStream(OutputStream out) { 1139a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander super(out); 1140a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1141a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1142a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1143a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(int b) throws IOException { 1144a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander byte[] data = { 1145a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander (byte) b 1146a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander }; 1147a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander write(data, 0, 1); 1148a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1149a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1150a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1151a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(byte[] b) throws IOException { 1152a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander write(b, 0, b.length); 1153a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1154a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1155a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1156a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(byte[] b, int off, int len) throws IOException { 1157a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (len == 0) 1158a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander return; 1159a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write(String.format("%x\r\n", len).getBytes()); 1160a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write(b, off, len); 1161a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write("\r\n".getBytes()); 1162a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1163a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1164a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void finish() throws IOException { 1165a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write("0\r\n\r\n".getBytes()); 1166a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1167a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1168269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1169269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1170269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 117130fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP status code after processing, e.g. "200 OK", Status.OK 1172d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 117330fb85f55cbd8df3005e652da3781f51294baf90ritchie private IStatus status; 1174269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 117530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 117630fb85f55cbd8df3005e652da3781f51294baf90ritchie * MIME type of content, e.g. "text/html" 117730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 117830fb85f55cbd8df3005e652da3781f51294baf90ritchie private String mimeType; 117930fb85f55cbd8df3005e652da3781f51294baf90ritchie 118030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 118130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Data of the response, may be null. 118230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 118330fb85f55cbd8df3005e652da3781f51294baf90ritchie private InputStream data; 118430fb85f55cbd8df3005e652da3781f51294baf90ritchie 11859e1ec7bff40d70d31953a04dd448665aaf549395ritchie private long contentLength; 1186292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen 118730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 118830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Headers for the HTTP response. Use addHeader() to add lines. 118930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 119030fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Map<String, String> header = new HashMap<String, String>(); 119130fb85f55cbd8df3005e652da3781f51294baf90ritchie 119230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 119330fb85f55cbd8df3005e652da3781f51294baf90ritchie * The request method that spawned this response. 119430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 119530fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method requestMethod; 119630fb85f55cbd8df3005e652da3781f51294baf90ritchie 119730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 119830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Use chunkedTransfer 119930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 120030fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean chunkedTransfer; 120130fb85f55cbd8df3005e652da3781f51294baf90ritchie 1202a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private boolean encodeAsGzip; 1203a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1204dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen private boolean keepAlive; 1205dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 120630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1207dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen * Creates a fixed length response if totalBytes>=0, otherwise chunked. 1208dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen */ 12099e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) { 1210dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.status = status; 1211dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.mimeType = mimeType; 12125e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (data == null) { 12135e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = new ByteArrayInputStream(new byte[0]); 12149e1ec7bff40d70d31953a04dd448665aaf549395ritchie this.contentLength = 0L; 12155e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 12165e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = data; 12175e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.contentLength = totalBytes; 1218292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen } 12195e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.chunkedTransfer = this.contentLength < 0; 1220dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen keepAlive = true; 1221269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1222269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1223bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite @Override 1224bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite public void close() throws IOException { 1225bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite if (this.data != null) { 1226bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite this.data.close(); 1227bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite } 1228bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite } 1229bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite 1230269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 123130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds given line to the header. 1232269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 123330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void addHeader(String name, String value) { 123430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.header.put(name, value); 123530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 123630fb85f55cbd8df3005e652da3781f51294baf90ritchie 123730fb85f55cbd8df3005e652da3781f51294baf90ritchie public InputStream getData() { 123830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.data; 123930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 124030fb85f55cbd8df3005e652da3781f51294baf90ritchie 124130fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHeader(String name) { 1242dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen for (String headerName : header.keySet()) { 1243dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (headerName.equalsIgnoreCase(name)) { 1244dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen return header.get(headerName); 1245dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1246dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1247dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen return null; 124830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 124930fb85f55cbd8df3005e652da3781f51294baf90ritchie 125030fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getMimeType() { 125130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.mimeType; 125230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 125330fb85f55cbd8df3005e652da3781f51294baf90ritchie 125430fb85f55cbd8df3005e652da3781f51294baf90ritchie public Method getRequestMethod() { 125530fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestMethod; 125630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 125730fb85f55cbd8df3005e652da3781f51294baf90ritchie 125830fb85f55cbd8df3005e652da3781f51294baf90ritchie public IStatus getStatus() { 125930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 126030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 126130fb85f55cbd8df3005e652da3781f51294baf90ritchie 1262a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void setGzipEncoding(boolean encodeAsGzip) { 1263a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander this.encodeAsGzip = encodeAsGzip; 1264a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1265a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1266dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen public void setKeepAlive(boolean useKeepAlive) { 1267dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen this.keepAlive = useKeepAlive; 1268dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1269dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 127030fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean headerAlreadySent(Map<String, String> header, String name) { 127130fb85f55cbd8df3005e652da3781f51294baf90ritchie boolean alreadySent = false; 127230fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 127330fb85f55cbd8df3005e652da3781f51294baf90ritchie alreadySent |= headerName.equalsIgnoreCase(name); 1274f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 127530fb85f55cbd8df3005e652da3781f51294baf90ritchie return alreadySent; 127630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1277269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 127830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 127930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sends given response to the socket. 128030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 128130fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void send(OutputStream outputStream) { 128230fb85f55cbd8df3005e652da3781f51294baf90ritchie String mime = this.mimeType; 128330fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 128430fb85f55cbd8df3005e652da3781f51294baf90ritchie gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 128530fb85f55cbd8df3005e652da3781f51294baf90ritchie 128630fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 128730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.status == null) { 128830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error("sendResponse(): Status can't be null."); 128930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 12905e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")), false); 129130fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("HTTP/1.1 " + this.status.getDescription() + " \r\n"); 129230fb85f55cbd8df3005e652da3781f51294baf90ritchie 129330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mime != null) { 129430fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Type: " + mime + "\r\n"); 129530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129630fb85f55cbd8df3005e652da3781f51294baf90ritchie 129730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header == null || this.header.get("Date") == null) { 129830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 129930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 130030fb85f55cbd8df3005e652da3781f51294baf90ritchie 130130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header != null) { 130230fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String key : this.header.keySet()) { 130330fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = this.header.get(key); 130430fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print(key + ": " + value + "\r\n"); 130530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 130630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 130730fb85f55cbd8df3005e652da3781f51294baf90ritchie 1308dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (!headerAlreadySent(header, "connection")) { 1309dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen pw.print("Connection: " + (this.keepAlive ? "keep-alive" : "close") + "\r\n"); 1310dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 131130fb85f55cbd8df3005e652da3781f51294baf90ritchie 1312a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (headerAlreadySent(this.header, "content-length")) { 1313a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander encodeAsGzip = false; 1314a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1315a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1316a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (encodeAsGzip) { 1317a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("Content-Encoding: gzip\r\n"); 1318f713632b4a7ad3fa9c7581dff3df6a5abf7de395kira setChunkedTransfer(true); 1319a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1320a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1321a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long pending = this.data != null ? this.contentLength : 0; 132230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 1323a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("Transfer-Encoding: chunked\r\n"); 1324a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else if (!encodeAsGzip) { 132530fb85f55cbd8df3005e652da3781f51294baf90ritchie pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, this.header, pending); 1326269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1327a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("\r\n"); 1328a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.flush(); 1329a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectTransferAndEncoding(outputStream, pending); 133030fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.flush(); 133130fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.data); 133230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 133330fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe); 1334269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1335269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 133693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1337a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException { 1338a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 1339a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream); 1340a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectEncoding(chunkedOutputStream, -1); 1341a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander chunkedOutputStream.finish(); 1342a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else { 1343a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectEncoding(outputStream, pending); 134430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 134593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 134693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1347a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException { 1348a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (encodeAsGzip) { 1349882f385eacea73737f9b0bb0d7c772e80b8dc63aPhilipp Wiesemann GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); 1350a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBody(gzipOutputStream, -1); 1351a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander gzipOutputStream.finish(); 1352a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else { 1353a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBody(outputStream, pending); 1354a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1355a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1356a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1357a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander /** 1358a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * Sends the body to the specified OutputStream. The pending parameter 1359a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * limits the maximum amounts of bytes sent unless it is -1, in which 1360a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * case everything is sent. 1361a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * 1362a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @param outputStream 1363a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * the OutputStream to send data to 1364a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @param pending 1365a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * -1 to send everything, otherwise sets a max limit to the 1366a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * number of bytes sent 1367a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @throws IOException 1368a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * if something goes wrong while sending the data. 1369a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander */ 1370a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBody(OutputStream outputStream, long pending) throws IOException { 1371a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long BUFFER_SIZE = 16 * 1024; 1372a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander byte[] buff = new byte[(int) BUFFER_SIZE]; 1373a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander boolean sendEverything = pending == -1; 1374a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander while (pending > 0 || sendEverything) { 1375a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE); 1376a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander int read = this.data.read(buff, 0, (int) bytesToRead); 1377a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (read <= 0) { 1378a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander break; 1379a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1380a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander outputStream.write(buff, 0, read); 1381a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (!sendEverything) { 138230fb85f55cbd8df3005e652da3781f51294baf90ritchie pending -= read; 138330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 138430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1385fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke } 1386fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke 13879e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, long size) { 138830fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 138930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (headerName.equalsIgnoreCase("content-length")) { 139030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 13919e1ec7bff40d70d31953a04dd448665aaf549395ritchie return Long.parseLong(header.get(headerName)); 139230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (NumberFormatException ex) { 139330fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 139430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139730fb85f55cbd8df3005e652da3781f51294baf90ritchie 139830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Length: " + size + "\r\n"); 139930fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 140030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140130fb85f55cbd8df3005e652da3781f51294baf90ritchie 140230fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setChunkedTransfer(boolean chunkedTransfer) { 140330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.chunkedTransfer = chunkedTransfer; 140430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140530fb85f55cbd8df3005e652da3781f51294baf90ritchie 140630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setData(InputStream data) { 140730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = data; 140830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140930fb85f55cbd8df3005e652da3781f51294baf90ritchie 141030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setMimeType(String mimeType) { 141130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 141230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 141330fb85f55cbd8df3005e652da3781f51294baf90ritchie 141430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setRequestMethod(Method requestMethod) { 141530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestMethod = requestMethod; 141630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 141730fb85f55cbd8df3005e652da3781f51294baf90ritchie 141830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setStatus(IStatus status) { 141930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 142030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 142130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 142230fb85f55cbd8df3005e652da3781f51294baf90ritchie 142330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final class ResponseException extends Exception { 142430fb85f55cbd8df3005e652da3781f51294baf90ritchie 142530fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final long serialVersionUID = 6569838532917408380L; 142630fb85f55cbd8df3005e652da3781f51294baf90ritchie 142730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Response.Status status; 142830fb85f55cbd8df3005e652da3781f51294baf90ritchie 142930fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message) { 143030fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message); 143130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 143230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 143330fb85f55cbd8df3005e652da3781f51294baf90ritchie 143430fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message, Exception e) { 143530fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message, e); 143630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 143730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 143830fb85f55cbd8df3005e652da3781f51294baf90ritchie 143930fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response.Status getStatus() { 144030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 144130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 144230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 144330fb85f55cbd8df3005e652da3781f51294baf90ritchie 144430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1445abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for the main listening thread. 1446abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1447abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ServerRunnable implements Runnable { 1448abcf1089ce1de49278970f088883cb32acb4f225ritchie 1449abcf1089ce1de49278970f088883cb32acb4f225ritchie private final int timeout; 1450abcf1089ce1de49278970f088883cb32acb4f225ritchie 1451284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie private IOException bindException; 1452284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie 1453284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie private boolean hasBinded = false; 1454284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie 1455abcf1089ce1de49278970f088883cb32acb4f225ritchie private ServerRunnable(int timeout) { 1456abcf1089ce1de49278970f088883cb32acb4f225ritchie this.timeout = timeout; 1457abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1458abcf1089ce1de49278970f088883cb32acb4f225ritchie 1459abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 1460abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 1461284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie try { 1462284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie myServerSocket.bind(hostname != null ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); 1463284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie hasBinded = true; 1464284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } catch (IOException e) { 1465284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie this.bindException = e; 1466284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie return; 1467284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1468abcf1089ce1de49278970f088883cb32acb4f225ritchie do { 1469abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 1470abcf1089ce1de49278970f088883cb32acb4f225ritchie final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept(); 1471901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie if (this.timeout > 0) { 1472901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie finalAccept.setSoTimeout(this.timeout); 1473901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie } 1474abcf1089ce1de49278970f088883cb32acb4f225ritchie final InputStream inputStream = finalAccept.getInputStream(); 1475abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream)); 1476abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (IOException e) { 1477abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 1478abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1479abcf1089ce1de49278970f088883cb32acb4f225ritchie } while (!NanoHTTPD.this.myServerSocket.isClosed()); 1480abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1481abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1482abcf1089ce1de49278970f088883cb32acb4f225ritchie 1483abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 148430fb85f55cbd8df3005e652da3781f51294baf90ritchie * A temp file. 148530fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 148630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 148730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp files are responsible for managing the actual temporary storage and 148830fb85f55cbd8df3005e652da3781f51294baf90ritchie * cleaning themselves up when no longer needed. 148930fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 149030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 149130fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFile { 149230fb85f55cbd8df3005e652da3781f51294baf90ritchie 149330fb85f55cbd8df3005e652da3781f51294baf90ritchie void delete() throws Exception; 149430fb85f55cbd8df3005e652da3781f51294baf90ritchie 149530fb85f55cbd8df3005e652da3781f51294baf90ritchie String getName(); 149630fb85f55cbd8df3005e652da3781f51294baf90ritchie 149730fb85f55cbd8df3005e652da3781f51294baf90ritchie OutputStream open() throws Exception; 149830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 149930fb85f55cbd8df3005e652da3781f51294baf90ritchie 150030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 150130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file manager. 150230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 150330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 150430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file managers are created 1-to-1 with incoming requests, to create 150530fb85f55cbd8df3005e652da3781f51294baf90ritchie * and cleanup temporary files created as a result of handling the request. 150630fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 150730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 150830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManager { 150930fb85f55cbd8df3005e652da3781f51294baf90ritchie 151030fb85f55cbd8df3005e652da3781f51294baf90ritchie void clear(); 151130fb85f55cbd8df3005e652da3781f51294baf90ritchie 15121f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen TempFile createTempFile(String filename_hint) throws Exception; 151330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 151430fb85f55cbd8df3005e652da3781f51294baf90ritchie 151530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 151630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Factory to create temp file managers. 151730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 151830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManagerFactory { 151930fb85f55cbd8df3005e652da3781f51294baf90ritchie 152030fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFileManager create(); 152130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 152230fb85f55cbd8df3005e652da3781f51294baf90ritchie 152330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 152430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) 152530fb85f55cbd8df3005e652da3781f51294baf90ritchie * This is required as the Keep-Alive HTTP connections would otherwise block 152630fb85f55cbd8df3005e652da3781f51294baf90ritchie * the socket reading thread forever (or as long the browser is open). 152730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 152830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int SOCKET_READ_TIMEOUT = 5000; 152930fb85f55cbd8df3005e652da3781f51294baf90ritchie 153030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 153130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: plain text 153230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 153330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_PLAINTEXT = "text/plain"; 153430fb85f55cbd8df3005e652da3781f51294baf90ritchie 153530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 153630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: html 153730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 153830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_HTML = "text/html"; 153930fb85f55cbd8df3005e652da3781f51294baf90ritchie 154030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 154130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pseudo-Parameter to use to store the actual query string in the 154230fb85f55cbd8df3005e652da3781f51294baf90ritchie * parameters map for later re-processing. 154330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 154430fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; 154530fb85f55cbd8df3005e652da3781f51294baf90ritchie 154630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 154730fb85f55cbd8df3005e652da3781f51294baf90ritchie * logger to log to. 154830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 15499c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName()); 155030fb85f55cbd8df3005e652da3781f51294baf90ritchie 155130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 155230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an 155330fb85f55cbd8df3005e652da3781f51294baf90ritchie * array of loaded KeyManagers. These objects must properly 155430fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded/initialized by the caller. 155530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 155630fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException { 155730fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 155830fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 155930fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 156030fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 156130fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 156230fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); 156330fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 156430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 156530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 156630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 156730fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 156830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 156930fb85f55cbd8df3005e652da3781f51294baf90ritchie 157030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 157130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a 157230fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded KeyManagerFactory. These objects must properly loaded/initialized 157330fb85f55cbd8df3005e652da3781f51294baf90ritchie * by the caller. 157430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 157530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException { 157630fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 157730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 157830fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 157930fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 158030fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 158130fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(loadedKeyFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 158230fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 158330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 158430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 158530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 158630fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 158730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 158830fb85f55cbd8df3005e652da3781f51294baf90ritchie 158930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 159030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your 159130fb85f55cbd8df3005e652da3781f51294baf90ritchie * certificate and passphrase 159230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 159330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException { 159430fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 159530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 159630fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 159730fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); 159830fb85f55cbd8df3005e652da3781f51294baf90ritchie keystore.load(keystoreStream, passphrase); 159930fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 160030fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(keystore); 160130fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 160230fb85f55cbd8df3005e652da3781f51294baf90ritchie keyManagerFactory.init(keystore, passphrase); 160330fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 160430fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 160530fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 160630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 160730fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 160830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160930fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 161030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 161130fb85f55cbd8df3005e652da3781f51294baf90ritchie 161229d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie private static final void safeClose(Object closeable) { 161329d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie try { 16146625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (closeable != null) { 16156625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (closeable instanceof Closeable) { 16166625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((Closeable) closeable).close(); 16176625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else if (closeable instanceof Socket) { 16186625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((Socket) closeable).close(); 16196625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else if (closeable instanceof ServerSocket) { 16206625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((ServerSocket) closeable).close(); 16216625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 16226625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen throw new IllegalArgumentException("Unknown object to close"); 16236625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 162430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 162529d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie } catch (IOException e) { 162629d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e); 162730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 162830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 162930fb85f55cbd8df3005e652da3781f51294baf90ritchie 163030fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String hostname; 163130fb85f55cbd8df3005e652da3781f51294baf90ritchie 163230fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int myPort; 163330fb85f55cbd8df3005e652da3781f51294baf90ritchie 163430fb85f55cbd8df3005e652da3781f51294baf90ritchie private ServerSocket myServerSocket; 163530fb85f55cbd8df3005e652da3781f51294baf90ritchie 163630fb85f55cbd8df3005e652da3781f51294baf90ritchie private SSLServerSocketFactory sslServerSocketFactory; 163730fb85f55cbd8df3005e652da3781f51294baf90ritchie 163830fb85f55cbd8df3005e652da3781f51294baf90ritchie private Thread myThread; 163930fb85f55cbd8df3005e652da3781f51294baf90ritchie 164030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 164130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 164230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1643d685218eacc23e69f685a76113665f50cc560edfritchie protected AsyncRunner asyncRunner; 164430fb85f55cbd8df3005e652da3781f51294baf90ritchie 164530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 164630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 164730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 164830fb85f55cbd8df3005e652da3781f51294baf90ritchie private TempFileManagerFactory tempFileManagerFactory; 164930fb85f55cbd8df3005e652da3781f51294baf90ritchie 165030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 165130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given port. 165230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 165330fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(int port) { 165430fb85f55cbd8df3005e652da3781f51294baf90ritchie this(null, port); 165530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 165630fb85f55cbd8df3005e652da3781f51294baf90ritchie 165730fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 165830fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 165930fb85f55cbd8df3005e652da3781f51294baf90ritchie // 166030fb85f55cbd8df3005e652da3781f51294baf90ritchie // Threading Strategy. 166130fb85f55cbd8df3005e652da3781f51294baf90ritchie // 166230fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 166330fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 166430fb85f55cbd8df3005e652da3781f51294baf90ritchie 166530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 166630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given hostname and port. 166730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 166830fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(String hostname, int port) { 166930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.hostname = hostname; 167030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myPort = port; 167130fb85f55cbd8df3005e652da3781f51294baf90ritchie setTempFileManagerFactory(new DefaultTempFileManagerFactory()); 167230fb85f55cbd8df3005e652da3781f51294baf90ritchie setAsyncRunner(new DefaultAsyncRunner()); 167330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 167430fb85f55cbd8df3005e652da3781f51294baf90ritchie 167530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 167630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Forcibly closes all connections that are open. 167730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 167830fb85f55cbd8df3005e652da3781f51294baf90ritchie public synchronized void closeAllConnections() { 1679abcf1089ce1de49278970f088883cb32acb4f225ritchie stop(); 1680abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1681abcf1089ce1de49278970f088883cb32acb4f225ritchie 1682abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1683abcf1089ce1de49278970f088883cb32acb4f225ritchie * create a instance of the client handler, subclasses can return a subclass 1684abcf1089ce1de49278970f088883cb32acb4f225ritchie * of the ClientHandler. 16859e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1686abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param finalAccept 1687abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socket the cleint is connected to 1688abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param inputStream 1689abcf1089ce1de49278970f088883cb32acb4f225ritchie * the input stream 1690abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the client handler 1691abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1692abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) { 1693abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ClientHandler(inputStream, finalAccept); 1694abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1695abcf1089ce1de49278970f088883cb32acb4f225ritchie 1696abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1697abcf1089ce1de49278970f088883cb32acb4f225ritchie * Instantiate the server runnable, can be overwritten by subclasses to 1698abcf1089ce1de49278970f088883cb32acb4f225ritchie * provide a subclass of the ServerRunnable. 16999e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1700abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param timeout 1701abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socet timeout to use. 1702abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the server runnable. 1703abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1704abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ServerRunnable createServerRunnable(final int timeout) { 1705abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ServerRunnable(timeout); 170630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 170730fb85f55cbd8df3005e652da3781f51294baf90ritchie 170830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 170930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 171030fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 171130fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 17129e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 171330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 171430fb85f55cbd8df3005e652da3781f51294baf90ritchie * original <b>NanoHTTPD</b> parameters values, as passed to the 171530fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>serve()</code> method. 171630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 171730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 171830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 171930fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(Map<String, String> parms) { 172030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER)); 172130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 172230fb85f55cbd8df3005e652da3781f51294baf90ritchie 1723abcf1089ce1de49278970f088883cb32acb4f225ritchie // ------------------------------------------------------------------------------- 1724abcf1089ce1de49278970f088883cb32acb4f225ritchie // // 1725abcf1089ce1de49278970f088883cb32acb4f225ritchie 172630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 172730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 172830fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 172930fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 17309e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 173130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param queryString 173230fb85f55cbd8df3005e652da3781f51294baf90ritchie * a query string pulled from the URL. 173330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 173430fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 173530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 173630fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(String queryString) { 173730fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, List<String>> parms = new HashMap<String, List<String>>(); 173830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (queryString != null) { 173930fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(queryString, "&"); 174030fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 174130fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 174230fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 174330fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); 174430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!parms.containsKey(propertyName)) { 174530fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(propertyName, new ArrayList<String>()); 174630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 174730fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null; 174830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (propertyValue != null) { 174930fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.get(propertyName).add(propertyValue); 175030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 175130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 175293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 175330fb85f55cbd8df3005e652da3781f51294baf90ritchie return parms; 175430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 175593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 175630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 175730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode percent encoded <code>String</code> values. 17589e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 175930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param str 176030fb85f55cbd8df3005e652da3781f51294baf90ritchie * the percent encoded <code>String</code> 176130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return expanded form of the input, for example "foo%20bar" becomes 176230fb85f55cbd8df3005e652da3781f51294baf90ritchie * "foo bar" 176330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 176430fb85f55cbd8df3005e652da3781f51294baf90ritchie protected String decodePercent(String str) { 176530fb85f55cbd8df3005e652da3781f51294baf90ritchie String decoded = null; 176630fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 176730fb85f55cbd8df3005e652da3781f51294baf90ritchie decoded = URLDecoder.decode(str, "UTF8"); 176830fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (UnsupportedEncodingException ignored) { 176930fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored); 177093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 177130fb85f55cbd8df3005e652da3781f51294baf90ritchie return decoded; 177230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 177393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 17748c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie /** 17758c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie * @return true if the gzip compression should be used if the client 1776ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen * accespts it. Default this option is on for text content and off 1777ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen * for everything else. 17788c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie */ 1779ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen protected boolean useGzipWhenAccepted(Response r) { 1780ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen return r.getMimeType() != null && r.getMimeType().toLowerCase().contains("text/"); 17818c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie } 17828c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie 178330fb85f55cbd8df3005e652da3781f51294baf90ritchie public final int getListeningPort() { 178430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort(); 178530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1786964393fd7e5ed49088882a126cf82507184467efMartin M Reed 178730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean isAlive() { 178830fb85f55cbd8df3005e652da3781f51294baf90ritchie return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); 17898dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1790964393fd7e5ed49088882a126cf82507184467efMartin M Reed 179130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 179230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Call before start() to serve over HTTPS instead of HTTP 179330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 179430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void makeSecure(SSLServerSocketFactory sslServerSocketFactory) { 179530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.sslServerSocketFactory = sslServerSocketFactory; 179630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17979058464950a9734da0a7ff2dc47f3081bbb5117critchie 179830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 17995e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with unknown length (using HTTP 1.1 chunking). 18005e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 18015e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newChunkedResponse(IStatus status, String mimeType, InputStream data) { 18025e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, -1); 18035e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18045e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 18055e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 18065e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with known length. 18075e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 18089e1ec7bff40d70d31953a04dd448665aaf549395ritchie public Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) { 18095e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, totalBytes); 18105e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18115e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 18125e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 18135e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 18145e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 18155e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newFixedLengthResponse(IStatus status, String mimeType, String txt) { 18165e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (txt == null) { 18175e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0); 18185e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 18195e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie byte[] bytes; 18205e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie try { 18215e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = txt.getBytes("UTF-8"); 18225e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } catch (UnsupportedEncodingException e) { 18235e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e); 18245e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = new byte[0]; 18255e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18265e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(bytes), bytes.length); 18275e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18285e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18295e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 18305e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 18315e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 18325e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 18335e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newFixedLengthResponse(String msg) { 18345e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg); 18355e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18365e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 18375e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 183830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 183930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 184030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 184130fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 18429e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 184330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param session 184430fb85f55cbd8df3005e652da3781f51294baf90ritchie * The HTTP session 184530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 184630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 184730fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(IHTTPSession session) { 184830fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> files = new HashMap<String, String>(); 184930fb85f55cbd8df3005e652da3781f51294baf90ritchie Method method = session.getMethod(); 185030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.PUT.equals(method) || Method.POST.equals(method)) { 185130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 185230fb85f55cbd8df3005e652da3781f51294baf90ritchie session.parseBody(files); 185330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 18545e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 185530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 18565e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 185730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18585820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 18595820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 186030fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> parms = session.getParms(); 186130fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString()); 186230fb85f55cbd8df3005e652da3781f51294baf90ritchie return serve(session.getUri(), method, session.getHeaders(), parms, files); 186330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18645820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 186530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 186630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 186730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 186830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 186930fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 18709e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 187130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param uri 187230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Percent-decoded URI without parameters, for example 187330fb85f55cbd8df3005e652da3781f51294baf90ritchie * "/index.cgi" 187430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param method 187530fb85f55cbd8df3005e652da3781f51294baf90ritchie * "GET", "POST" etc. 187630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 187730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Parsed, percent decoded parameters from URI and, in case of 187830fb85f55cbd8df3005e652da3781f51294baf90ritchie * POST, data. 187930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param headers 188030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Header entries, percent decoded 188130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 188230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 188330fb85f55cbd8df3005e652da3781f51294baf90ritchie @Deprecated 188430fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) { 18855e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); 188630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18875820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 188830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 188930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 18909e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 189130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param asyncRunner 189230fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling threads. 189330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 189430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setAsyncRunner(AsyncRunner asyncRunner) { 189530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.asyncRunner = asyncRunner; 189630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18975820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 189830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 189930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 19009e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 190130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param tempFileManagerFactory 190230fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling temp files. 190330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 190430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { 190530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManagerFactory = tempFileManagerFactory; 19065820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 19075820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 19088dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 190930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Start the server. 19109e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 191130fb85f55cbd8df3005e652da3781f51294baf90ritchie * @throws IOException 191230fb85f55cbd8df3005e652da3781f51294baf90ritchie * if the socket is in use. 19138dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 191430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void start() throws IOException { 1915c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie start(NanoHTTPD.SOCKET_READ_TIMEOUT); 1916c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie } 1917c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie 1918c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie /** 1919c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * Start the server. 19209e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1921c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @param timeout 1922c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * timeout to use for socket connections. 1923c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @throws IOException 1924c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * if the socket is in use. 1925c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie */ 1926c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie public void start(final int timeout) throws IOException { 192730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.sslServerSocketFactory != null) { 192830fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocket ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); 192930fb85f55cbd8df3005e652da3781f51294baf90ritchie ss.setNeedClientAuth(false); 193030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = ss; 193130fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 193230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = new ServerSocket(); 193330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 193430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.setReuseAddress(true); 19359058464950a9734da0a7ff2dc47f3081bbb5117critchie 1936284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie ServerRunnable serverRunnable = createServerRunnable(timeout); 1937284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie this.myThread = new Thread(serverRunnable); 193830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setDaemon(true); 193930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setName("NanoHttpd Main Listener"); 194030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.start(); 1941284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie while (!serverRunnable.hasBinded && serverRunnable.bindException == null) { 1942284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie try { 1943284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie Thread.sleep(10L); 1944284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } catch (Throwable e) { 1945284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // on android this may not be allowed, that's why we 1946284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // catch throwable the wait should be very short because we are 1947284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // just waiting for the bind of the socket 1948284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1949284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1950284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie if (serverRunnable.bindException != null) { 1951284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie throw serverRunnable.bindException; 1952284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 195330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 19545820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 195530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 195630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Stop the server. 195730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 195830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void stop() { 195930fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 196030fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.myServerSocket); 1961abcf1089ce1de49278970f088883cb32acb4f225ritchie this.asyncRunner.closeAll(); 196230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.myThread != null) { 196330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.join(); 196430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 196530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 196630fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e); 1967964393fd7e5ed49088882a126cf82507184467efMartin M Reed } 196830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1969964393fd7e5ed49088882a126cf82507184467efMartin M Reed 197030fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean wasStarted() { 197130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket != null && this.myThread != null; 19727e423dde0fe863fadbd363de8a94746a30fca0eeMartin M Reed } 1973269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 1974