NanoHTTPD.java revision f713632b4a7ad3fa9c7581dff3df6a5abf7de395
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; 4630fb85f55cbd8df3005e652da3781f51294baf90ritchieimport java.security.KeyStore; 477b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat; 48b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.ArrayList; 49b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Calendar; 50abcf1089ce1de49278970f088883cb32acb4f225ritchieimport java.util.Collections; 51b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Date; 52b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.HashMap; 53b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Iterator; 54b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.List; 55b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Locale; 56b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Map; 57b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.StringTokenizer; 58b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.TimeZone; 593526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Level; 603526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Logger; 61731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchieimport java.util.regex.Matcher; 62731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchieimport java.util.regex.Pattern; 63a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theanderimport java.util.zip.GZIPOutputStream; 64269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 6530fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManager; 6630fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManagerFactory; 6730fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLContext; 6830fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocket; 6930fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocketFactory; 7030fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.TrustManagerFactory; 71f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 725e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport fi.iki.elonen.NanoHTTPD.Response.IStatus; 735e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchieimport fi.iki.elonen.NanoHTTPD.Response.Status; 745e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 75269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 765b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke * A simple, tiny, nicely embeddable HTTP server in Java 77d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 78d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 79b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke * NanoHTTPD 809058464950a9734da0a7ff2dc47f3081bbb5117critchie * <p> 819058464950a9734da0a7ff2dc47f3081bbb5117critchie * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 829058464950a9734da0a7ff2dc47f3081bbb5117critchie * 2010 by Konstantinos Togias 839058464950a9734da0a7ff2dc47f3081bbb5117critchie * </p> 84d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 85d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 86269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 87269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 88d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 89269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 9001ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li> 91269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 929058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>No fixed config files, logging, authorization etc. (Implement yourself if 939058464950a9734da0a7ff2dc47f3081bbb5117critchie * you need them.)</li> 949058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT 959058464950a9734da0a7ff2dc47f3081bbb5117critchie * support in 1.25)</li> 96269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 97269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 98269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 99269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 100269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 101269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 102269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 103269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 104269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 105269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 106269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 107269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 109cf230611ca85378ae2b0e8130dace5766edfd295Philipp Wiesemann * <li>Contains a built-in list of most common MIME types</li> 1109058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>All header names are converted to lower case so they don't vary between 1119058464950a9734da0a7ff2dc47f3081bbb5117critchie * browsers/clients</li> 112d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 114d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 115d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 11601ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b> 117269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 118d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 11901ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li> 120d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 121269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 122d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 1239058464950a9734da0a7ff2dc47f3081bbb5117critchie * See the separate "LICENSE.md" file for the distribution license (Modified BSD 1249058464950a9734da0a7ff2dc47f3081bbb5117critchie * licence) 125269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 126d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD { 1279058464950a9734da0a7ff2dc47f3081bbb5117critchie 128269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 12930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 130293e04675f3d4f427c125a26e831d70d5011c79bhflicka */ 13130fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface AsyncRunner { 1329058464950a9734da0a7ff2dc47f3081bbb5117critchie 133abcf1089ce1de49278970f088883cb32acb4f225ritchie void closeAll(); 134abcf1089ce1de49278970f088883cb32acb4f225ritchie 135abcf1089ce1de49278970f088883cb32acb4f225ritchie void closed(ClientHandler clientHandler); 136abcf1089ce1de49278970f088883cb32acb4f225ritchie 137abcf1089ce1de49278970f088883cb32acb4f225ritchie void exec(ClientHandler code); 138abcf1089ce1de49278970f088883cb32acb4f225ritchie } 139abcf1089ce1de49278970f088883cb32acb4f225ritchie 140abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 141abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for every new client connection. 142abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 143abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ClientHandler implements Runnable { 144abcf1089ce1de49278970f088883cb32acb4f225ritchie 145abcf1089ce1de49278970f088883cb32acb4f225ritchie private final InputStream inputStream; 146abcf1089ce1de49278970f088883cb32acb4f225ritchie 147abcf1089ce1de49278970f088883cb32acb4f225ritchie private final Socket acceptSocket; 148abcf1089ce1de49278970f088883cb32acb4f225ritchie 149abcf1089ce1de49278970f088883cb32acb4f225ritchie private ClientHandler(InputStream inputStream, Socket acceptSocket) { 150abcf1089ce1de49278970f088883cb32acb4f225ritchie this.inputStream = inputStream; 151abcf1089ce1de49278970f088883cb32acb4f225ritchie this.acceptSocket = acceptSocket; 152abcf1089ce1de49278970f088883cb32acb4f225ritchie } 153abcf1089ce1de49278970f088883cb32acb4f225ritchie 154abcf1089ce1de49278970f088883cb32acb4f225ritchie public void close() { 155abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 156abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 157abcf1089ce1de49278970f088883cb32acb4f225ritchie } 158abcf1089ce1de49278970f088883cb32acb4f225ritchie 159abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 160abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 161abcf1089ce1de49278970f088883cb32acb4f225ritchie OutputStream outputStream = null; 162abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 163abcf1089ce1de49278970f088883cb32acb4f225ritchie outputStream = this.acceptSocket.getOutputStream(); 164abcf1089ce1de49278970f088883cb32acb4f225ritchie TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create(); 165abcf1089ce1de49278970f088883cb32acb4f225ritchie HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress()); 166abcf1089ce1de49278970f088883cb32acb4f225ritchie while (!this.acceptSocket.isClosed()) { 167abcf1089ce1de49278970f088883cb32acb4f225ritchie session.execute(); 168abcf1089ce1de49278970f088883cb32acb4f225ritchie } 169abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (Exception e) { 170abcf1089ce1de49278970f088883cb32acb4f225ritchie // When the socket is closed by the client, 171abcf1089ce1de49278970f088883cb32acb4f225ritchie // we throw our own SocketException 172abcf1089ce1de49278970f088883cb32acb4f225ritchie // to break the "keep alive" loop above. If 173abcf1089ce1de49278970f088883cb32acb4f225ritchie // the exception was anything other 174abcf1089ce1de49278970f088883cb32acb4f225ritchie // than the expected SocketException OR a 175abcf1089ce1de49278970f088883cb32acb4f225ritchie // SocketTimeoutException, print the 176abcf1089ce1de49278970f088883cb32acb4f225ritchie // stacktrace 177abcf1089ce1de49278970f088883cb32acb4f225ritchie if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) { 178abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 179abcf1089ce1de49278970f088883cb32acb4f225ritchie } 180abcf1089ce1de49278970f088883cb32acb4f225ritchie } finally { 181abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(outputStream); 182abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 183abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 184abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.closed(this); 185abcf1089ce1de49278970f088883cb32acb4f225ritchie } 186abcf1089ce1de49278970f088883cb32acb4f225ritchie } 18730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1889058464950a9734da0a7ff2dc47f3081bbb5117critchie 18930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Cookie { 1909058464950a9734da0a7ff2dc47f3081bbb5117critchie 19130fb85f55cbd8df3005e652da3781f51294baf90ritchie public static String getHTTPTime(int days) { 19230fb85f55cbd8df3005e652da3781f51294baf90ritchie Calendar calendar = Calendar.getInstance(); 19330fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 19430fb85f55cbd8df3005e652da3781f51294baf90ritchie dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 19530fb85f55cbd8df3005e652da3781f51294baf90ritchie calendar.add(Calendar.DAY_OF_MONTH, days); 19630fb85f55cbd8df3005e652da3781f51294baf90ritchie return dateFormat.format(calendar.getTime()); 19730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 19830fb85f55cbd8df3005e652da3781f51294baf90ritchie 19930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String n, v, e; 20030fb85f55cbd8df3005e652da3781f51294baf90ritchie 20130fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value) { 20230fb85f55cbd8df3005e652da3781f51294baf90ritchie this(name, value, 30); 20330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20430fb85f55cbd8df3005e652da3781f51294baf90ritchie 20530fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, int numDays) { 20630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 20730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 20830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = getHTTPTime(numDays); 20930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21030fb85f55cbd8df3005e652da3781f51294baf90ritchie 21130fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, String expires) { 21230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 21330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 21430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = expires; 21530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21630fb85f55cbd8df3005e652da3781f51294baf90ritchie 21730fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHTTPHeader() { 21830fb85f55cbd8df3005e652da3781f51294baf90ritchie String fmt = "%s=%s; expires=%s"; 21930fb85f55cbd8df3005e652da3781f51294baf90ritchie return String.format(fmt, this.n, this.v, this.e); 22030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2229058464950a9734da0a7ff2dc47f3081bbb5117critchie 2239058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 22430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Provides rudimentary support for cookies. Doesn't support 'path', 22530fb85f55cbd8df3005e652da3781f51294baf90ritchie * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported 22630fb85f55cbd8df3005e652da3781f51294baf90ritchie * features. 2279e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 22830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @author LordFokas 2299058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 23030fb85f55cbd8df3005e652da3781f51294baf90ritchie public class CookieHandler implements Iterable<String> { 2319058464950a9734da0a7ff2dc47f3081bbb5117critchie 23230fb85f55cbd8df3005e652da3781f51294baf90ritchie private final HashMap<String, String> cookies = new HashMap<String, String>(); 2339058464950a9734da0a7ff2dc47f3081bbb5117critchie 23430fb85f55cbd8df3005e652da3781f51294baf90ritchie private final ArrayList<Cookie> queue = new ArrayList<Cookie>(); 2359058464950a9734da0a7ff2dc47f3081bbb5117critchie 23630fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler(Map<String, String> httpHeaders) { 23730fb85f55cbd8df3005e652da3781f51294baf90ritchie String raw = httpHeaders.get("cookie"); 23830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (raw != null) { 23930fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] tokens = raw.split(";"); 24030fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String token : tokens) { 24130fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] data = token.trim().split("="); 24230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (data.length == 2) { 24330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.put(data[0], data[1]); 24430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 24530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 24630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 24730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2489058464950a9734da0a7ff2dc47f3081bbb5117critchie 24930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 25030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Set a cookie with an expiration date from a month ago, effectively 25130fb85f55cbd8df3005e652da3781f51294baf90ritchie * deleting it on the client side. 2529e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 25330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 25430fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie name. 25530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 25630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete(String name) { 25730fb85f55cbd8df3005e652da3781f51294baf90ritchie set(name, "-delete-", -30); 25830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2599058464950a9734da0a7ff2dc47f3081bbb5117critchie 26030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 26130fb85f55cbd8df3005e652da3781f51294baf90ritchie public Iterator<String> iterator() { 26230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.keySet().iterator(); 26330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2649058464950a9734da0a7ff2dc47f3081bbb5117critchie 26530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 26630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Read a cookie from the HTTP Headers. 2679e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 26830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 26930fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 27030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return The cookie's value if it exists, null otherwise. 27130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 27230fb85f55cbd8df3005e652da3781f51294baf90ritchie public String read(String name) { 27330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.get(name); 27430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2759058464950a9734da0a7ff2dc47f3081bbb5117critchie 27630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(Cookie cookie) { 27730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(cookie); 27830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2799058464950a9734da0a7ff2dc47f3081bbb5117critchie 28030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 28130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sets a cookie. 2829e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 28330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 28430fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 28530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param value 28630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's value. 28730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param expires 28830fb85f55cbd8df3005e652da3781f51294baf90ritchie * How many days until the cookie expires. 28930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 29030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(String name, String value, int expires) { 29130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); 29230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 293269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 29430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 29530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Internally used by the webserver to add all queued cookies into the 29630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Response's HTTP Headers. 2979e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 29830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param response 29930fb85f55cbd8df3005e652da3781f51294baf90ritchie * The Response object to which headers the queued cookies 30030fb85f55cbd8df3005e652da3781f51294baf90ritchie * will be added. 30130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 30230fb85f55cbd8df3005e652da3781f51294baf90ritchie public void unloadQueue(Response response) { 30330fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Cookie cookie : this.queue) { 30430fb85f55cbd8df3005e652da3781f51294baf90ritchie response.addHeader("Set-Cookie", cookie.getHTTPHeader()); 30530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 30630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 307f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke } 308f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 3095b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 31030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default threading strategy for NanoHTTPD. 31130fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 31230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 31330fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, the server spawns a new Thread for every incoming request. 31430fb85f55cbd8df3005e652da3781f51294baf90ritchie * These are set to <i>daemon</i> status, and named according to the request 31530fb85f55cbd8df3005e652da3781f51294baf90ritchie * number. The name is useful when profiling the application. 31630fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3175b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 31830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultAsyncRunner implements AsyncRunner { 3193f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke 32030fb85f55cbd8df3005e652da3781f51294baf90ritchie private long requestCount; 32130fb85f55cbd8df3005e652da3781f51294baf90ritchie 322abcf1089ce1de49278970f088883cb32acb4f225ritchie private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>()); 323abcf1089ce1de49278970f088883cb32acb4f225ritchie 324d685218eacc23e69f685a76113665f50cc560edfritchie /** 325d685218eacc23e69f685a76113665f50cc560edfritchie * @return a list with currently running clients. 326d685218eacc23e69f685a76113665f50cc560edfritchie */ 327d685218eacc23e69f685a76113665f50cc560edfritchie public List<ClientHandler> getRunning() { 328d685218eacc23e69f685a76113665f50cc560edfritchie return running; 329d685218eacc23e69f685a76113665f50cc560edfritchie } 330d685218eacc23e69f685a76113665f50cc560edfritchie 33130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 332abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closeAll() { 333abcf1089ce1de49278970f088883cb32acb4f225ritchie // copy of the list for concurrency 334abcf1089ce1de49278970f088883cb32acb4f225ritchie for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) { 335abcf1089ce1de49278970f088883cb32acb4f225ritchie clientHandler.close(); 336abcf1089ce1de49278970f088883cb32acb4f225ritchie } 337abcf1089ce1de49278970f088883cb32acb4f225ritchie } 338abcf1089ce1de49278970f088883cb32acb4f225ritchie 339abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 340abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closed(ClientHandler clientHandler) { 341abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.remove(clientHandler); 342abcf1089ce1de49278970f088883cb32acb4f225ritchie } 343abcf1089ce1de49278970f088883cb32acb4f225ritchie 344abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 345abcf1089ce1de49278970f088883cb32acb4f225ritchie public void exec(ClientHandler clientHandler) { 34630fb85f55cbd8df3005e652da3781f51294baf90ritchie ++this.requestCount; 347abcf1089ce1de49278970f088883cb32acb4f225ritchie Thread t = new Thread(clientHandler); 34830fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setDaemon(true); 34930fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")"); 350abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.add(clientHandler); 35130fb85f55cbd8df3005e652da3781f51294baf90ritchie t.start(); 352c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 353c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 354c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke 3559058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 35630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 35730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 35830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 35930fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, files are created by <code>File.createTempFile()</code> in 36030fb85f55cbd8df3005e652da3781f51294baf90ritchie * the directory specified. 36130fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3629058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 36330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFile implements TempFile { 36430fb85f55cbd8df3005e652da3781f51294baf90ritchie 36530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final File file; 36630fb85f55cbd8df3005e652da3781f51294baf90ritchie 36730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream fstream; 36830fb85f55cbd8df3005e652da3781f51294baf90ritchie 36930fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFile(String tempdir) throws IOException { 37030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.file = File.createTempFile("NanoHTTPD-", "", new File(tempdir)); 37130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.fstream = new FileOutputStream(this.file); 3729058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3739058464950a9734da0a7ff2dc47f3081bbb5117critchie 37430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 37530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete() throws Exception { 37630fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.fstream); 3776034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann if (!this.file.delete()) { 3786034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann throw new Exception("could not delete temporary file"); 3796034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann } 3809058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3819058464950a9734da0a7ff2dc47f3081bbb5117critchie 38230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38330fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getName() { 38430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.file.getAbsolutePath(); 3859058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3869058464950a9734da0a7ff2dc47f3081bbb5117critchie 38730fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38830fb85f55cbd8df3005e652da3781f51294baf90ritchie public OutputStream open() throws Exception { 38930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.fstream; 39030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 3919058464950a9734da0a7ff2dc47f3081bbb5117critchie } 392f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 3933f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke /** 39430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 39530fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 39630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 39730fb85f55cbd8df3005e652da3781f51294baf90ritchie * This class stores its files in the standard location (that is, wherever 39830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>java.io.tmpdir</code> points to). Files are added to an internal 39930fb85f55cbd8df3005e652da3781f51294baf90ritchie * list, and deleted when no longer needed (that is, when 40030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>clear()</code> is invoked at the end of processing a request). 40130fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 4023f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke */ 40330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFileManager implements TempFileManager { 404f39ce1dd765bb79b0a83a8e6dd921f0d3986ebf6Aaron Davidson 40530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String tmpdir; 4069058464950a9734da0a7ff2dc47f3081bbb5117critchie 40730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final List<TempFile> tempFiles; 4089058464950a9734da0a7ff2dc47f3081bbb5117critchie 40930fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFileManager() { 41030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tmpdir = System.getProperty("java.io.tmpdir"); 41130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles = new ArrayList<TempFile>(); 41230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 413d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 41430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 41530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void clear() { 41630fb85f55cbd8df3005e652da3781f51294baf90ritchie for (TempFile file : this.tempFiles) { 41730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 41830fb85f55cbd8df3005e652da3781f51294baf90ritchie file.delete(); 41930fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception ignored) { 42030fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); 42130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 422fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 42330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.clear(); 424269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 425269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 42630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 42730fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFile createTempFile() throws Exception { 42830fb85f55cbd8df3005e652da3781f51294baf90ritchie DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); 42930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.add(tempFile); 43030fb85f55cbd8df3005e652da3781f51294baf90ritchie return tempFile; 43130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 4320a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4330a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 4340a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander /** 43530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 4360a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander */ 43730fb85f55cbd8df3005e652da3781f51294baf90ritchie private class DefaultTempFileManagerFactory implements TempFileManagerFactory { 4380a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 43930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 44030fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFileManager create() { 44130fb85f55cbd8df3005e652da3781f51294baf90ritchie return new DefaultTempFileManager(); 4420a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4430a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4448dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 445731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)"; 446731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 447731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE); 448731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 449731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)"; 450731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 451731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE); 452731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 453731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]"; 454731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 455731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX); 456731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie 45730fb85f55cbd8df3005e652da3781f51294baf90ritchie protected class HTTPSession implements IHTTPSession { 4588dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 45930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int BUFSIZE = 8192; 4608dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final TempFileManager tempFileManager; 4628dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 46330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream outputStream; 4649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 46530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final PushbackInputStream inputStream; 46693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 46730fb85f55cbd8df3005e652da3781f51294baf90ritchie private int splitbyte; 46893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 46930fb85f55cbd8df3005e652da3781f51294baf90ritchie private int rlen; 470f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 47130fb85f55cbd8df3005e652da3781f51294baf90ritchie private String uri; 472f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 47330fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method method; 474f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 47530fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> parms; 4765b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 47730fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> headers; 4785b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 47930fb85f55cbd8df3005e652da3781f51294baf90ritchie private CookieHandler cookies; 4805b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48130fb85f55cbd8df3005e652da3781f51294baf90ritchie private String queryParameterString; 4825b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 48330fb85f55cbd8df3005e652da3781f51294baf90ritchie private String remoteIp; 4848dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 485dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen private String protocolVersion; 486dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 48730fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { 48830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 48930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 49030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 4918dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 4928dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 49330fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) { 49430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 49530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 49630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 49730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString(); 49830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 4999788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 5009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 5019788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 50230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the sent headers and loads the data into Key/value pairs 5039788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 50430fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException { 5059788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke try { 50630fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the request line 50730fb85f55cbd8df3005e652da3781f51294baf90ritchie String inLine = in.readLine(); 50830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (inLine == null) { 50930fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 51030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 511fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 51230fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(inLine); 51330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 51430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 51530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 5169788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 51730fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("method", st.nextToken()); 5187f0727787957c2f093412c01d165846842ec2425Paul Hawke 51930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 52030fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 5217f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5227f0727787957c2f093412c01d165846842ec2425Paul Hawke 52330fb85f55cbd8df3005e652da3781f51294baf90ritchie String uri = st.nextToken(); 5247f0727787957c2f093412c01d165846842ec2425Paul Hawke 52530fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode parameters from the URI 52630fb85f55cbd8df3005e652da3781f51294baf90ritchie int qmi = uri.indexOf('?'); 52730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (qmi >= 0) { 52830fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(uri.substring(qmi + 1), parms); 52930fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri.substring(0, qmi)); 53030fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 53130fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri); 5327f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5337f0727787957c2f093412c01d165846842ec2425Paul Hawke 53430fb85f55cbd8df3005e652da3781f51294baf90ritchie // If there's another token, its protocol version, 535dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen // followed by HTTP headers. 53630fb85f55cbd8df3005e652da3781f51294baf90ritchie // NOTE: this now forces header names lower case since they are 53730fb85f55cbd8df3005e652da3781f51294baf90ritchie // case insensitive and vary by client. 538de2bb1bc9fa3959846741e1fd14076971e17b90cElonen if (st.hasMoreTokens()) { 539dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen protocolVersion = st.nextToken(); 540de2bb1bc9fa3959846741e1fd14076971e17b90cElonen } else { 541dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen protocolVersion = "HTTP/1.1"; 542dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1."); 54330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 54430fb85f55cbd8df3005e652da3781f51294baf90ritchie String line = in.readLine(); 54530fb85f55cbd8df3005e652da3781f51294baf90ritchie while (line != null && line.trim().length() > 0) { 54630fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = line.indexOf(':'); 54730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p >= 0) { 54830fb85f55cbd8df3005e652da3781f51294baf90ritchie headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim()); 5497f0727787957c2f093412c01d165846842ec2425Paul Hawke } 55030fb85f55cbd8df3005e652da3781f51294baf90ritchie line = in.readLine(); 5517f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5527f0727787957c2f093412c01d165846842ec2425Paul Hawke 55330fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("uri", uri); 5547f0727787957c2f093412c01d165846842ec2425Paul Hawke } catch (IOException ioe) { 55530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 5567f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5577f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5587f0727787957c2f093412c01d165846842ec2425Paul Hawke 55930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 56030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the Multipart Body data and put it into Key/Value pairs. 56130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 5623cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen private void decodeMultipartFormData(String boundary, ByteBuffer fbuf, Map<String, String> parms, Map<String, String> files) throws ResponseException { 56330fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 5643cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int[] boundary_idxs = getBoundaryPositions(fbuf, boundary.getBytes()); 5653cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (boundary_idxs.length < 2) { 5663cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings."); 5673cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 5683cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 5695e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen final int MAX_HEADER_SIZE = 1024; 5705e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen byte[] part_header_buff = new byte[MAX_HEADER_SIZE]; 5713cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen for (int bi = 0; bi < boundary_idxs.length - 1; bi++) { 5723cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.position(boundary_idxs[bi]); 5735e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE; 5743cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(part_header_buff, 0, len); 5753cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen ByteArrayInputStream bais = new ByteArrayInputStream(part_header_buff, 0, len); 5763cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen BufferedReader in = new BufferedReader(new InputStreamReader(bais)); 5773cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 5783cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // First line is boundary string 5793cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String mpline = in.readLine(); 58030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!mpline.contains(boundary)) { 5813cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary."); 58230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 5833cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 5843cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String part_name = null, file_name = null, content_type = null; 5853cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Parse the reset of the header lines 58630fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 58730fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && mpline.trim().length() > 0) { 588731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline); 589731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (matcher.matches()) { 590731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie String attributeString = matcher.group(2); 591731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString); 592731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie while (matcher.find()) { 593731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie String key = matcher.group(1); 594731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (key.equalsIgnoreCase("name")) { 595731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie part_name = matcher.group(2); 596731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie } else if (key.equalsIgnoreCase("filename")) { 597731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie file_name = matcher.group(2); 59830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6003cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 601731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie matcher = CONTENT_TYPE_PATTERN.matcher(mpline); 602731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (matcher.matches()) { 603731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie content_type = matcher.group(2).trim(); 604731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie } 6053cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen mpline = in.readLine(); 6063cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6073cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6083cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read the part data 6095e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen int part_header_len = len - (int) in.skip(MAX_HEADER_SIZE); 6103cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (part_header_len >= len - 4) { 6115e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen throw new ResponseException(Response.Status.INTERNAL_ERROR, "Multipart header size exceeds MAX_HEADER_SIZE."); 6123cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6133cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int part_data_start = boundary_idxs[bi] + part_header_len; 6143cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int part_data_end = boundary_idxs[bi + 1] - 4; 6153cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6163cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.position(part_data_start); 6173cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (content_type == null) { 6183cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read the part into a string 6193cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen byte[] data_bytes = new byte[part_data_end - part_data_start]; 6203cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(data_bytes); 6213cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen parms.put(part_name, new String(data_bytes)); 6223cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } else { 6233cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read it into a file 6243cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String path = saveTmpFile(fbuf, part_data_start, part_data_end - part_data_start); 6253cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (!files.containsKey(part_name)) { 6263cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen files.put(part_name, path); 62730fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 6283cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int count = 2; 6293cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen while (files.containsKey(part_name + count)) { 6303cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen count++; 63130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6323cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen files.put(part_name + count, path); 63330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6343cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen parms.put(part_name, file_name); 63598d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 63698d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 6373cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } catch (ResponseException re) { 6383cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw re; 6393cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } catch (Exception e) { 6403cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.INTERNAL_ERROR, e.toString()); 641c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 642fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 643fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 64430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 64530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes parameters in percent-encoded URI-format ( e.g. 64630fb85f55cbd8df3005e652da3781f51294baf90ritchie * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 64730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Map. NOTE: this doesn't support multiple identical keys due to the 64830fb85f55cbd8df3005e652da3781f51294baf90ritchie * simplicity of Map. 64930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 65030fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeParms(String parms, Map<String, String> p) { 65130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (parms == null) { 65230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = ""; 65330fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 654c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 655c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke 65630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = parms; 65730fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(parms, "&"); 65830fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 65930fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 66030fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 66130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (sep >= 0) { 66230fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); 66330fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 66430fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e).trim(), ""); 66530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 666fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 667fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 668fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 66930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 67030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void execute() throws IOException { 67130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 67230fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the first 8192 bytes. 67330fb85f55cbd8df3005e652da3781f51294baf90ritchie // The full header should fit in here. 67430fb85f55cbd8df3005e652da3781f51294baf90ritchie // Apache's default header limit is 8KB. 67530fb85f55cbd8df3005e652da3781f51294baf90ritchie // Do NOT assume that a single read will get the entire header 67630fb85f55cbd8df3005e652da3781f51294baf90ritchie // at once! 67730fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buf = new byte[HTTPSession.BUFSIZE]; 67830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.splitbyte = 0; 67930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = 0; 6809c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie 6819c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie int read = -1; 6829c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie try { 6839c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); 6849c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } catch (Exception e) { 6859c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6869c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6879c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6889c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6899c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (read == -1) { 6909c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie // socket was been closed 6919c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6929c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6939c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6949c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6959c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie while (read > 0) { 6969c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.rlen += read; 6979c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.splitbyte = findHeaderEnd(buf, this.rlen); 6989c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (this.splitbyte > 0) { 6999c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie break; 70030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 7019c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); 70230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 70330fb85f55cbd8df3005e652da3781f51294baf90ritchie 70430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.splitbyte < this.rlen) { 70530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream.unread(buf, this.splitbyte, this.rlen - this.splitbyte); 70630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 70730fb85f55cbd8df3005e652da3781f51294baf90ritchie 70830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.parms = new HashMap<String, String>(); 70930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null == this.headers) { 71030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 71130fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 71230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.clear(); 71330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 71430fb85f55cbd8df3005e652da3781f51294baf90ritchie 71530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null != this.remoteIp) { 71630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("remote-addr", this.remoteIp); 71730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("http-client-ip", this.remoteIp); 71830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 71930fb85f55cbd8df3005e652da3781f51294baf90ritchie 72030fb85f55cbd8df3005e652da3781f51294baf90ritchie // Create a BufferedReader for parsing the header. 72130fb85f55cbd8df3005e652da3781f51294baf90ritchie BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen))); 72230fb85f55cbd8df3005e652da3781f51294baf90ritchie 72330fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode the header into parms and header java properties 72430fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> pre = new HashMap<String, String>(); 72530fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeHeader(hin, pre, this.parms, this.headers); 72630fb85f55cbd8df3005e652da3781f51294baf90ritchie 72730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.method = Method.lookup(pre.get("method")); 72830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.method == null) { 72930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); 73030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 73130fb85f55cbd8df3005e652da3781f51294baf90ritchie 73230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.uri = pre.get("uri"); 73330fb85f55cbd8df3005e652da3781f51294baf90ritchie 73430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies = new CookieHandler(this.headers); 73530fb85f55cbd8df3005e652da3781f51294baf90ritchie 736dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen String connection = this.headers.get("connection"); 737dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen boolean keepAlive = protocolVersion.equals("HTTP/1.1") && (connection == null || !connection.matches("(?i).*close.*")); 738dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 73930fb85f55cbd8df3005e652da3781f51294baf90ritchie // Ok, now do the serve() 74030fb85f55cbd8df3005e652da3781f51294baf90ritchie Response r = serve(this); 74130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (r == null) { 74230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 74330fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 744a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander String acceptEncoding = this.headers.get("accept-encoding"); 74530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.unloadQueue(r); 74630fb85f55cbd8df3005e652da3781f51294baf90ritchie r.setRequestMethod(this.method); 7478c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie r.setGzipEncoding(useGzipWhenAccepted() && acceptEncoding != null && acceptEncoding.contains("gzip")); 748dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen r.setKeepAlive(keepAlive); 74930fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 75030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 751dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (!keepAlive || "close".equalsIgnoreCase(r.getHeader("connection"))) { 752dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen throw new SocketException("NanoHttpd Shutdown"); 753dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 75430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketException e) { 75530fb85f55cbd8df3005e652da3781f51294baf90ritchie // throw it out to close socket object (finalAccept) 75630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw e; 75730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketTimeoutException ste) { 75830fb85f55cbd8df3005e652da3781f51294baf90ritchie // treat socket timeouts the same way we treat socket exceptions 75930fb85f55cbd8df3005e652da3781f51294baf90ritchie // i.e. close the stream & finalAccept object by throwing the 76030fb85f55cbd8df3005e652da3781f51294baf90ritchie // exception up the call stack. 76130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw ste; 76230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 7635e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie Response r = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 76430fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 76530fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 76630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 7675e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie Response r = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 76830fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 76930fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 77030fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 77130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager.clear(); 772fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 773fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 774fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 77530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 77630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find byte index separating header from body. It must be the last byte 77730fb85f55cbd8df3005e652da3781f51294baf90ritchie * of the first two sequential new lines. 77830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 77930fb85f55cbd8df3005e652da3781f51294baf90ritchie private int findHeaderEnd(final byte[] buf, int rlen) { 78030fb85f55cbd8df3005e652da3781f51294baf90ritchie int splitbyte = 0; 78130fb85f55cbd8df3005e652da3781f51294baf90ritchie while (splitbyte + 3 < rlen) { 78230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 78330fb85f55cbd8df3005e652da3781f51294baf90ritchie return splitbyte + 4; 784fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 78530fb85f55cbd8df3005e652da3781f51294baf90ritchie splitbyte++; 786fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 78730fb85f55cbd8df3005e652da3781f51294baf90ritchie return 0; 788fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 789fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 79030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 791af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * Find the byte positions where multipart boundaries start. This reads 792af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * a large block at a time and uses a temporary buffer to optimize 793af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * (memory mapped) file access. 79430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 79530fb85f55cbd8df3005e652da3781f51294baf90ritchie private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) { 796af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int[] res = new int[0]; 797af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (b.remaining() < boundary.length) { 798af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen return res; 799af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } 800af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 801af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int search_window_pos = 0; 802af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen byte[] search_window = new byte[4 * 1024 + boundary.length]; 803af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 804af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length; 805af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen b.get(search_window, 0, first_fill); 806af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int new_bytes = first_fill - boundary.length; 807af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 808af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen do { 809af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Search the search_window 810af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen for (int j = 0; j < new_bytes; j++) { 811af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen for (int i = 0; i < boundary.length; i++) { 812af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (search_window[j + i] != boundary[i]) 813af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen break; 814af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (i == boundary.length - 1) { 815af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Match found, add it to results 816af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int[] new_res = new int[res.length + 1]; 817af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen System.arraycopy(res, 0, new_res, 0, res.length); 818af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_res[res.length] = search_window_pos + j; 819af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen res = new_res; 820af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } 82130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 82230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 823af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen search_window_pos += new_bytes; 824af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 825af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Copy the end of the buffer to the start 826af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, boundary.length); 827af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen 828af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Refill search_window 829af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_bytes = search_window.length - boundary.length; 830af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes; 831af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen b.get(search_window, boundary.length, new_bytes); 832af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } while (new_bytes > 0); 833af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen return res; 834c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 835c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 83630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 83730fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler getCookies() { 83830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies; 839c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 840c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 84130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 84230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getHeaders() { 84330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.headers; 844c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 845c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 84630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 84730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final InputStream getInputStream() { 84830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.inputStream; 849c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 850c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 85130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 85230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Method getMethod() { 85330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.method; 854c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 855c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 85630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 85730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getParms() { 85830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.parms; 859c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 860c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 86130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 86230fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getQueryParameterString() { 86330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.queryParameterString; 864c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 865c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 86630fb85f55cbd8df3005e652da3781f51294baf90ritchie private RandomAccessFile getTmpBucket() { 86730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 86830fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile tempFile = this.tempFileManager.createTempFile(); 86930fb85f55cbd8df3005e652da3781f51294baf90ritchie return new RandomAccessFile(tempFile.getName(), "rw"); 87030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 87130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 8729788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8739788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8747e4e4ae652c755faea626f1e2538d495f96e648esynapticloop 875bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 87630fb85f55cbd8df3005e652da3781f51294baf90ritchie public final String getUri() { 87730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.uri; 87893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 87993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 880bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 881bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel public void parseBody(Map<String, String> files) throws IOException, ResponseException { 8826625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen final int REQUEST_BUFFER_LEN = 512; 883770aaf0c6567d8b62a6eef18c8f0359208427c77Jarno Elonen final int MEMORY_STORE_LIMIT = 1024; 88493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed RandomAccessFile randomAccessFile = null; 88593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed try { 886745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed long size; 88730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.headers.containsKey("content-length")) { 88830fb85f55cbd8df3005e652da3781f51294baf90ritchie size = Integer.parseInt(this.headers.get("content-length")); 88930fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (this.splitbyte < this.rlen) { 89030fb85f55cbd8df3005e652da3781f51294baf90ritchie size = this.rlen - this.splitbyte; 891745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed } else { 892d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke size = 0; 8930cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 894d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 8956625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ByteArrayOutputStream baos = null; 8966625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen DataOutput request_data_output = null; 8976625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen 8986625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen // Store the request in memory or a file, depending on size 899770aaf0c6567d8b62a6eef18c8f0359208427c77Jarno Elonen if (size < MEMORY_STORE_LIMIT) { 9006625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen baos = new ByteArrayOutputStream(); 9016625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output = new DataOutputStream(baos); 9026625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 9036625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen randomAccessFile = getTmpBucket(); 9046625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output = randomAccessFile; 9056625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 9066625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen 9076625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen // Read all the body and write it to request_data_output 9086625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen byte[] buf = new byte[REQUEST_BUFFER_LEN]; 90930fb85f55cbd8df3005e652da3781f51294baf90ritchie while (this.rlen >= 0 && size > 0) { 9106625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN)); 91130fb85f55cbd8df3005e652da3781f51294baf90ritchie size -= this.rlen; 91230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.rlen > 0) { 9136625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output.write(buf, 0, this.rlen); 9140cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 915269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 916269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9176625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ByteBuffer fbuf = null; 9186625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (baos != null) { 9196625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size()); 9206625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 9216625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); 9226625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen randomAccessFile.seek(0); 9236625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 924269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 925269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 926269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 92730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.POST.equals(this.method)) { 928269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 92930fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentTypeHeader = this.headers.get("content-type"); 9302b54eda5fee8430268a99d73b061e790362db544Tom Hermann 9312c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke StringTokenizer st = null; 9322c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (contentTypeHeader != null) { 9332c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke st = new StringTokenizer(contentTypeHeader, ",; "); 9342c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (st.hasMoreTokens()) { 9352c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke contentType = st.nextToken(); 9362c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke } 937269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 938269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9390277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke if ("multipart/form-data".equalsIgnoreCase(contentType)) { 940269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 9410cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 9429058464950a9734da0a7ff2dc47f3081bbb5117critchie throw new ResponseException(Response.Status.BAD_REQUEST, 9439058464950a9734da0a7ff2dc47f3081bbb5117critchie "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 9440cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 9452b54eda5fee8430268a99d73b061e790362db544Tom Hermann 9464e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundaryStartString = "boundary="; 9472b54eda5fee8430268a99d73b061e790362db544Tom Hermann int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length(); 9484e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length()); 949220e1a21e7bbb831d06551c72799dfedc1db979fPaul Hawke if (boundary.startsWith("\"") && boundary.endsWith("\"")) { 9504e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke boundary = boundary.substring(1, boundary.length() - 1); 9514e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 952269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 9533cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen decodeMultipartFormData(boundary, fbuf, this.parms, files); 954269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 9553cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen byte[] postBytes = new byte[fbuf.remaining()]; 9563cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(postBytes); 9573cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String postLine = new String(postBytes).trim(); 958e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown // Handle application/x-www-form-urlencoded 959e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) { 96030fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(postLine, this.parms); 961e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } else if (postLine.length() != 0) { 9629058464950a9734da0a7ff2dc47f3081bbb5117critchie // Special case for raw POST data => create a 9639058464950a9734da0a7ff2dc47f3081bbb5117critchie // special files entry "postData" with raw content 9649058464950a9734da0a7ff2dc47f3081bbb5117critchie // data 9659058464950a9734da0a7ff2dc47f3081bbb5117critchie files.put("postData", postLine); 966e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } 967269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 96830fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (Method.PUT.equals(this.method)) { 969dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke files.put("content", saveTmpFile(fbuf, 0, fbuf.limit())); 970b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } 9714e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } finally { 9729cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed safeClose(randomAccessFile); 973269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 974269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 975269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 976269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 97730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Retrieves the content of a sent file and saves it to a temporary 97830fb85f55cbd8df3005e652da3781f51294baf90ritchie * file. The full path to the saved file is returned. 979d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 98030fb85f55cbd8df3005e652da3781f51294baf90ritchie private String saveTmpFile(ByteBuffer b, int offset, int len) { 98130fb85f55cbd8df3005e652da3781f51294baf90ritchie String path = ""; 98230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (len > 0) { 98330fb85f55cbd8df3005e652da3781f51294baf90ritchie FileOutputStream fileOutputStream = null; 98430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 98530fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile tempFile = this.tempFileManager.createTempFile(); 98630fb85f55cbd8df3005e652da3781f51294baf90ritchie ByteBuffer src = b.duplicate(); 98730fb85f55cbd8df3005e652da3781f51294baf90ritchie fileOutputStream = new FileOutputStream(tempFile.getName()); 98830fb85f55cbd8df3005e652da3781f51294baf90ritchie FileChannel dest = fileOutputStream.getChannel(); 98930fb85f55cbd8df3005e652da3781f51294baf90ritchie src.position(offset).limit(offset + len); 99030fb85f55cbd8df3005e652da3781f51294baf90ritchie dest.write(src.slice()); 99130fb85f55cbd8df3005e652da3781f51294baf90ritchie path = tempFile.getName(); 99230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { // Catch exception if any 99330fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 99430fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 99530fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(fileOutputStream); 9960cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 99730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 99830fb85f55cbd8df3005e652da3781f51294baf90ritchie return path; 99930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 100030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1001269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 100230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 100330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Handles one session, i.e. parses the HTTP request and returns the 100430fb85f55cbd8df3005e652da3781f51294baf90ritchie * response. 100530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 100630fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IHTTPSession { 1007269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 100830fb85f55cbd8df3005e652da3781f51294baf90ritchie void execute() throws IOException; 1009269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 101030fb85f55cbd8df3005e652da3781f51294baf90ritchie CookieHandler getCookies(); 1011269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 101230fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getHeaders(); 1013269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 101430fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream getInputStream(); 101530fb85f55cbd8df3005e652da3781f51294baf90ritchie 101630fb85f55cbd8df3005e652da3781f51294baf90ritchie Method getMethod(); 101730fb85f55cbd8df3005e652da3781f51294baf90ritchie 101830fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getParms(); 101930fb85f55cbd8df3005e652da3781f51294baf90ritchie 102030fb85f55cbd8df3005e652da3781f51294baf90ritchie String getQueryParameterString(); 1021269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1022269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 102330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return the path part of the URL. 1024d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 102530fb85f55cbd8df3005e652da3781f51294baf90ritchie String getUri(); 1026269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1027269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 102830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds the files in the request body to the files map. 10299e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 103030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param files 103130fb85f55cbd8df3005e652da3781f51294baf90ritchie * map to modify 1032d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 103330fb85f55cbd8df3005e652da3781f51294baf90ritchie void parseBody(Map<String, String> files) throws IOException, ResponseException; 103430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 103530fb85f55cbd8df3005e652da3781f51294baf90ritchie 103630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 103730fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP Request methods, with the ability to decode a <code>String</code> 103830fb85f55cbd8df3005e652da3781f51294baf90ritchie * back to its enum value. 103930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 104030fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Method { 104130fb85f55cbd8df3005e652da3781f51294baf90ritchie GET, 104230fb85f55cbd8df3005e652da3781f51294baf90ritchie PUT, 104330fb85f55cbd8df3005e652da3781f51294baf90ritchie POST, 104430fb85f55cbd8df3005e652da3781f51294baf90ritchie DELETE, 104530fb85f55cbd8df3005e652da3781f51294baf90ritchie HEAD, 104630fb85f55cbd8df3005e652da3781f51294baf90ritchie OPTIONS; 104730fb85f55cbd8df3005e652da3781f51294baf90ritchie 104830fb85f55cbd8df3005e652da3781f51294baf90ritchie static Method lookup(String method) { 104930fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Method m : Method.values()) { 105030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (m.toString().equalsIgnoreCase(method)) { 105130fb85f55cbd8df3005e652da3781f51294baf90ritchie return m; 1052269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1053269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 105430fb85f55cbd8df3005e652da3781f51294baf90ritchie return null; 105530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 105630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 105730fb85f55cbd8df3005e652da3781f51294baf90ritchie 105830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 105930fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP response. Return one of these from serve(). 106030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 106130fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Response { 106230fb85f55cbd8df3005e652da3781f51294baf90ritchie 106330fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IStatus { 106430fb85f55cbd8df3005e652da3781f51294baf90ritchie 106530fb85f55cbd8df3005e652da3781f51294baf90ritchie String getDescription(); 106630fb85f55cbd8df3005e652da3781f51294baf90ritchie 106730fb85f55cbd8df3005e652da3781f51294baf90ritchie int getRequestStatus(); 1068269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1069269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1070269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 107130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Some HTTP response status codes 1072d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 107330fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Status implements IStatus { 107430fb85f55cbd8df3005e652da3781f51294baf90ritchie SWITCH_PROTOCOL(101, "Switching Protocols"), 107530fb85f55cbd8df3005e652da3781f51294baf90ritchie OK(200, "OK"), 107630fb85f55cbd8df3005e652da3781f51294baf90ritchie CREATED(201, "Created"), 107730fb85f55cbd8df3005e652da3781f51294baf90ritchie ACCEPTED(202, "Accepted"), 107830fb85f55cbd8df3005e652da3781f51294baf90ritchie NO_CONTENT(204, "No Content"), 107930fb85f55cbd8df3005e652da3781f51294baf90ritchie PARTIAL_CONTENT(206, "Partial Content"), 108030fb85f55cbd8df3005e652da3781f51294baf90ritchie REDIRECT(301, "Moved Permanently"), 108130fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_MODIFIED(304, "Not Modified"), 108230fb85f55cbd8df3005e652da3781f51294baf90ritchie BAD_REQUEST(400, "Bad Request"), 108330fb85f55cbd8df3005e652da3781f51294baf90ritchie UNAUTHORIZED(401, "Unauthorized"), 108430fb85f55cbd8df3005e652da3781f51294baf90ritchie FORBIDDEN(403, "Forbidden"), 108530fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_FOUND(404, "Not Found"), 108630fb85f55cbd8df3005e652da3781f51294baf90ritchie METHOD_NOT_ALLOWED(405, "Method Not Allowed"), 108730fb85f55cbd8df3005e652da3781f51294baf90ritchie RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), 1088de2bb1bc9fa3959846741e1fd14076971e17b90cElonen INTERNAL_ERROR(500, "Internal Server Error"), 1089de2bb1bc9fa3959846741e1fd14076971e17b90cElonen UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported"); 109030fb85f55cbd8df3005e652da3781f51294baf90ritchie 109130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int requestStatus; 109230fb85f55cbd8df3005e652da3781f51294baf90ritchie 109330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String description; 109430fb85f55cbd8df3005e652da3781f51294baf90ritchie 109530fb85f55cbd8df3005e652da3781f51294baf90ritchie Status(int requestStatus, String description) { 109630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestStatus = requestStatus; 109730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.description = description; 1098269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 109930fb85f55cbd8df3005e652da3781f51294baf90ritchie 110030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 110130fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getDescription() { 110230fb85f55cbd8df3005e652da3781f51294baf90ritchie return "" + this.requestStatus + " " + this.description; 110330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 110430fb85f55cbd8df3005e652da3781f51294baf90ritchie 110530fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 110630fb85f55cbd8df3005e652da3781f51294baf90ritchie public int getRequestStatus() { 110730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestStatus; 1108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1109a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1110a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1111a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1112a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander /** 1113a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * Output stream that will automatically send every write to the wrapped 1114a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * OutputStream according to chunked transfer: 1115a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 1116a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander */ 1117a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private static class ChunkedOutputStream extends FilterOutputStream { 1118a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1119a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public ChunkedOutputStream(OutputStream out) { 1120a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander super(out); 1121a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1122a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1123a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1124a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(int b) throws IOException { 1125a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander byte[] data = { 1126a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander (byte) b 1127a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander }; 1128a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander write(data, 0, 1); 1129a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1130a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1131a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1132a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(byte[] b) throws IOException { 1133a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander write(b, 0, b.length); 1134a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1135a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1136a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1137a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(byte[] b, int off, int len) throws IOException { 1138a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (len == 0) 1139a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander return; 1140a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write(String.format("%x\r\n", len).getBytes()); 1141a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write(b, off, len); 1142a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write("\r\n".getBytes()); 1143a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1144a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1145a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void finish() throws IOException { 1146a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write("0\r\n\r\n".getBytes()); 1147a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1148a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1149269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1150269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1151269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 115230fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP status code after processing, e.g. "200 OK", Status.OK 1153d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 115430fb85f55cbd8df3005e652da3781f51294baf90ritchie private IStatus status; 1155269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 115630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 115730fb85f55cbd8df3005e652da3781f51294baf90ritchie * MIME type of content, e.g. "text/html" 115830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 115930fb85f55cbd8df3005e652da3781f51294baf90ritchie private String mimeType; 116030fb85f55cbd8df3005e652da3781f51294baf90ritchie 116130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 116230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Data of the response, may be null. 116330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 116430fb85f55cbd8df3005e652da3781f51294baf90ritchie private InputStream data; 116530fb85f55cbd8df3005e652da3781f51294baf90ritchie 11669e1ec7bff40d70d31953a04dd448665aaf549395ritchie private long contentLength; 1167292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen 116830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 116930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Headers for the HTTP response. Use addHeader() to add lines. 117030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 117130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Map<String, String> header = new HashMap<String, String>(); 117230fb85f55cbd8df3005e652da3781f51294baf90ritchie 117330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 117430fb85f55cbd8df3005e652da3781f51294baf90ritchie * The request method that spawned this response. 117530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 117630fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method requestMethod; 117730fb85f55cbd8df3005e652da3781f51294baf90ritchie 117830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 117930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Use chunkedTransfer 118030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 118130fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean chunkedTransfer; 118230fb85f55cbd8df3005e652da3781f51294baf90ritchie 1183a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private boolean encodeAsGzip; 1184a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1185dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen private boolean keepAlive; 1186dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 118730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1188dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen * Creates a fixed length response if totalBytes>=0, otherwise chunked. 1189dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen */ 11909e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) { 1191dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.status = status; 1192dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.mimeType = mimeType; 11935e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (data == null) { 11945e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = new ByteArrayInputStream(new byte[0]); 11959e1ec7bff40d70d31953a04dd448665aaf549395ritchie this.contentLength = 0L; 11965e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 11975e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = data; 11985e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.contentLength = totalBytes; 1199292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen } 12005e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.chunkedTransfer = this.contentLength < 0; 1201dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen keepAlive = true; 1202269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1203269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1204269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 120530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds given line to the header. 1206269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 120730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void addHeader(String name, String value) { 120830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.header.put(name, value); 120930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 121030fb85f55cbd8df3005e652da3781f51294baf90ritchie 121130fb85f55cbd8df3005e652da3781f51294baf90ritchie public InputStream getData() { 121230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.data; 121330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 121430fb85f55cbd8df3005e652da3781f51294baf90ritchie 121530fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHeader(String name) { 1216dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen for (String headerName : header.keySet()) { 1217dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (headerName.equalsIgnoreCase(name)) { 1218dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen return header.get(headerName); 1219dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1220dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1221dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen return null; 122230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 122330fb85f55cbd8df3005e652da3781f51294baf90ritchie 122430fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getMimeType() { 122530fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.mimeType; 122630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 122730fb85f55cbd8df3005e652da3781f51294baf90ritchie 122830fb85f55cbd8df3005e652da3781f51294baf90ritchie public Method getRequestMethod() { 122930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestMethod; 123030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 123130fb85f55cbd8df3005e652da3781f51294baf90ritchie 123230fb85f55cbd8df3005e652da3781f51294baf90ritchie public IStatus getStatus() { 123330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 123430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 123530fb85f55cbd8df3005e652da3781f51294baf90ritchie 1236a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void setGzipEncoding(boolean encodeAsGzip) { 1237a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander this.encodeAsGzip = encodeAsGzip; 1238a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1239a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1240dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen public void setKeepAlive(boolean useKeepAlive) { 1241dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen this.keepAlive = useKeepAlive; 1242dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1243dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 124430fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean headerAlreadySent(Map<String, String> header, String name) { 124530fb85f55cbd8df3005e652da3781f51294baf90ritchie boolean alreadySent = false; 124630fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 124730fb85f55cbd8df3005e652da3781f51294baf90ritchie alreadySent |= headerName.equalsIgnoreCase(name); 1248f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 124930fb85f55cbd8df3005e652da3781f51294baf90ritchie return alreadySent; 125030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1251269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 125230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 125330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sends given response to the socket. 125430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 125530fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void send(OutputStream outputStream) { 125630fb85f55cbd8df3005e652da3781f51294baf90ritchie String mime = this.mimeType; 125730fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 125830fb85f55cbd8df3005e652da3781f51294baf90ritchie gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 125930fb85f55cbd8df3005e652da3781f51294baf90ritchie 126030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 126130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.status == null) { 126230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error("sendResponse(): Status can't be null."); 126330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 12645e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")), false); 126530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("HTTP/1.1 " + this.status.getDescription() + " \r\n"); 126630fb85f55cbd8df3005e652da3781f51294baf90ritchie 126730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mime != null) { 126830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Type: " + mime + "\r\n"); 126930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127030fb85f55cbd8df3005e652da3781f51294baf90ritchie 127130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header == null || this.header.get("Date") == null) { 127230fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 127330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127430fb85f55cbd8df3005e652da3781f51294baf90ritchie 127530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header != null) { 127630fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String key : this.header.keySet()) { 127730fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = this.header.get(key); 127830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print(key + ": " + value + "\r\n"); 127930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 128030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 128130fb85f55cbd8df3005e652da3781f51294baf90ritchie 1282dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (!headerAlreadySent(header, "connection")) { 1283dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen pw.print("Connection: " + (this.keepAlive ? "keep-alive" : "close") + "\r\n"); 1284dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 128530fb85f55cbd8df3005e652da3781f51294baf90ritchie 1286a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (headerAlreadySent(this.header, "content-length")) { 1287a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander encodeAsGzip = false; 1288a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1289a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1290a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (encodeAsGzip) { 1291a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("Content-Encoding: gzip\r\n"); 1292f713632b4a7ad3fa9c7581dff3df6a5abf7de395kira setChunkedTransfer(true); 1293a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1294a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1295a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long pending = this.data != null ? this.contentLength : 0; 129630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 1297a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("Transfer-Encoding: chunked\r\n"); 1298a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else if (!encodeAsGzip) { 129930fb85f55cbd8df3005e652da3781f51294baf90ritchie pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, this.header, pending); 1300269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1301a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("\r\n"); 1302a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.flush(); 1303a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectTransferAndEncoding(outputStream, pending); 130430fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.flush(); 130530fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.data); 130630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 130730fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe); 1308269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1309269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 131093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1311a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException { 1312a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 1313a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream); 1314a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectEncoding(chunkedOutputStream, -1); 1315a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander chunkedOutputStream.finish(); 1316a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else { 1317a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectEncoding(outputStream, pending); 131830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 131993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 132093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1321a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException { 1322a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (encodeAsGzip) { 1323882f385eacea73737f9b0bb0d7c772e80b8dc63aPhilipp Wiesemann GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); 1324a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBody(gzipOutputStream, -1); 1325a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander gzipOutputStream.finish(); 1326a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else { 1327a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBody(outputStream, pending); 1328a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1329a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1330a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1331a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander /** 1332a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * Sends the body to the specified OutputStream. The pending parameter 1333a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * limits the maximum amounts of bytes sent unless it is -1, in which 1334a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * case everything is sent. 1335a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * 1336a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @param outputStream 1337a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * the OutputStream to send data to 1338a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @param pending 1339a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * -1 to send everything, otherwise sets a max limit to the 1340a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * number of bytes sent 1341a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @throws IOException 1342a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * if something goes wrong while sending the data. 1343a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander */ 1344a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBody(OutputStream outputStream, long pending) throws IOException { 1345a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long BUFFER_SIZE = 16 * 1024; 1346a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander byte[] buff = new byte[(int) BUFFER_SIZE]; 1347a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander boolean sendEverything = pending == -1; 1348a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander while (pending > 0 || sendEverything) { 1349a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE); 1350a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander int read = this.data.read(buff, 0, (int) bytesToRead); 1351a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (read <= 0) { 1352a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander break; 1353a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1354a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander outputStream.write(buff, 0, read); 1355a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (!sendEverything) { 135630fb85f55cbd8df3005e652da3781f51294baf90ritchie pending -= read; 135730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 135830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1359fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke } 1360fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke 13619e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, long size) { 136230fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 136330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (headerName.equalsIgnoreCase("content-length")) { 136430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 13659e1ec7bff40d70d31953a04dd448665aaf549395ritchie return Long.parseLong(header.get(headerName)); 136630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (NumberFormatException ex) { 136730fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 136830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 136930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 137030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 137130fb85f55cbd8df3005e652da3781f51294baf90ritchie 137230fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Length: " + size + "\r\n"); 137330fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 137430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 137530fb85f55cbd8df3005e652da3781f51294baf90ritchie 137630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setChunkedTransfer(boolean chunkedTransfer) { 137730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.chunkedTransfer = chunkedTransfer; 137830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 137930fb85f55cbd8df3005e652da3781f51294baf90ritchie 138030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setData(InputStream data) { 138130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = data; 138230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 138330fb85f55cbd8df3005e652da3781f51294baf90ritchie 138430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setMimeType(String mimeType) { 138530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 138630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 138730fb85f55cbd8df3005e652da3781f51294baf90ritchie 138830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setRequestMethod(Method requestMethod) { 138930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestMethod = requestMethod; 139030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139130fb85f55cbd8df3005e652da3781f51294baf90ritchie 139230fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setStatus(IStatus status) { 139330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 139430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139630fb85f55cbd8df3005e652da3781f51294baf90ritchie 139730fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final class ResponseException extends Exception { 139830fb85f55cbd8df3005e652da3781f51294baf90ritchie 139930fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final long serialVersionUID = 6569838532917408380L; 140030fb85f55cbd8df3005e652da3781f51294baf90ritchie 140130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Response.Status status; 140230fb85f55cbd8df3005e652da3781f51294baf90ritchie 140330fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message) { 140430fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message); 140530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 140630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140730fb85f55cbd8df3005e652da3781f51294baf90ritchie 140830fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message, Exception e) { 140930fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message, e); 141030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 141130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 141230fb85f55cbd8df3005e652da3781f51294baf90ritchie 141330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response.Status getStatus() { 141430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 141530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 141630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 141730fb85f55cbd8df3005e652da3781f51294baf90ritchie 141830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1419abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for the main listening thread. 1420abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1421abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ServerRunnable implements Runnable { 1422abcf1089ce1de49278970f088883cb32acb4f225ritchie 1423abcf1089ce1de49278970f088883cb32acb4f225ritchie private final int timeout; 1424abcf1089ce1de49278970f088883cb32acb4f225ritchie 1425284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie private IOException bindException; 1426284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie 1427284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie private boolean hasBinded = false; 1428284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie 1429abcf1089ce1de49278970f088883cb32acb4f225ritchie private ServerRunnable(int timeout) { 1430abcf1089ce1de49278970f088883cb32acb4f225ritchie this.timeout = timeout; 1431abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1432abcf1089ce1de49278970f088883cb32acb4f225ritchie 1433abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 1434abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 1435284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie try { 1436284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie myServerSocket.bind(hostname != null ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); 1437284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie hasBinded = true; 1438284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } catch (IOException e) { 1439284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie this.bindException = e; 1440284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie return; 1441284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1442abcf1089ce1de49278970f088883cb32acb4f225ritchie do { 1443abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 1444abcf1089ce1de49278970f088883cb32acb4f225ritchie final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept(); 1445901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie if (this.timeout > 0) { 1446901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie finalAccept.setSoTimeout(this.timeout); 1447901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie } 1448abcf1089ce1de49278970f088883cb32acb4f225ritchie final InputStream inputStream = finalAccept.getInputStream(); 1449abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream)); 1450abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (IOException e) { 1451abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 1452abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1453abcf1089ce1de49278970f088883cb32acb4f225ritchie } while (!NanoHTTPD.this.myServerSocket.isClosed()); 1454abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1455abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1456abcf1089ce1de49278970f088883cb32acb4f225ritchie 1457abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 145830fb85f55cbd8df3005e652da3781f51294baf90ritchie * A temp file. 145930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 146030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 146130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp files are responsible for managing the actual temporary storage and 146230fb85f55cbd8df3005e652da3781f51294baf90ritchie * cleaning themselves up when no longer needed. 146330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 146430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 146530fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFile { 146630fb85f55cbd8df3005e652da3781f51294baf90ritchie 146730fb85f55cbd8df3005e652da3781f51294baf90ritchie void delete() throws Exception; 146830fb85f55cbd8df3005e652da3781f51294baf90ritchie 146930fb85f55cbd8df3005e652da3781f51294baf90ritchie String getName(); 147030fb85f55cbd8df3005e652da3781f51294baf90ritchie 147130fb85f55cbd8df3005e652da3781f51294baf90ritchie OutputStream open() throws Exception; 147230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 147330fb85f55cbd8df3005e652da3781f51294baf90ritchie 147430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 147530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file manager. 147630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 147730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 147830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file managers are created 1-to-1 with incoming requests, to create 147930fb85f55cbd8df3005e652da3781f51294baf90ritchie * and cleanup temporary files created as a result of handling the request. 148030fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 148130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 148230fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManager { 148330fb85f55cbd8df3005e652da3781f51294baf90ritchie 148430fb85f55cbd8df3005e652da3781f51294baf90ritchie void clear(); 148530fb85f55cbd8df3005e652da3781f51294baf90ritchie 148630fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile createTempFile() throws Exception; 148730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 148830fb85f55cbd8df3005e652da3781f51294baf90ritchie 148930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 149030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Factory to create temp file managers. 149130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 149230fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManagerFactory { 149330fb85f55cbd8df3005e652da3781f51294baf90ritchie 149430fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFileManager create(); 149530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 149630fb85f55cbd8df3005e652da3781f51294baf90ritchie 149730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 149830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) 149930fb85f55cbd8df3005e652da3781f51294baf90ritchie * This is required as the Keep-Alive HTTP connections would otherwise block 150030fb85f55cbd8df3005e652da3781f51294baf90ritchie * the socket reading thread forever (or as long the browser is open). 150130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 150230fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int SOCKET_READ_TIMEOUT = 5000; 150330fb85f55cbd8df3005e652da3781f51294baf90ritchie 150430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 150530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: plain text 150630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 150730fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_PLAINTEXT = "text/plain"; 150830fb85f55cbd8df3005e652da3781f51294baf90ritchie 150930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 151030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: html 151130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 151230fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_HTML = "text/html"; 151330fb85f55cbd8df3005e652da3781f51294baf90ritchie 151430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 151530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pseudo-Parameter to use to store the actual query string in the 151630fb85f55cbd8df3005e652da3781f51294baf90ritchie * parameters map for later re-processing. 151730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 151830fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; 151930fb85f55cbd8df3005e652da3781f51294baf90ritchie 152030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 152130fb85f55cbd8df3005e652da3781f51294baf90ritchie * logger to log to. 152230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 15239c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName()); 152430fb85f55cbd8df3005e652da3781f51294baf90ritchie 152530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 152630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an 152730fb85f55cbd8df3005e652da3781f51294baf90ritchie * array of loaded KeyManagers. These objects must properly 152830fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded/initialized by the caller. 152930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 153030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException { 153130fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 153230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 153330fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 153430fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 153530fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 153630fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); 153730fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 153830fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 153930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 154030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 154130fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 154230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 154330fb85f55cbd8df3005e652da3781f51294baf90ritchie 154430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 154530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a 154630fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded KeyManagerFactory. These objects must properly loaded/initialized 154730fb85f55cbd8df3005e652da3781f51294baf90ritchie * by the caller. 154830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 154930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException { 155030fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 155130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 155230fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 155330fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 155430fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 155530fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(loadedKeyFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 155630fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 155730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 155830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 155930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 156030fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 156130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 156230fb85f55cbd8df3005e652da3781f51294baf90ritchie 156330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 156430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your 156530fb85f55cbd8df3005e652da3781f51294baf90ritchie * certificate and passphrase 156630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 156730fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException { 156830fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 156930fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 157030fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 157130fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); 157230fb85f55cbd8df3005e652da3781f51294baf90ritchie keystore.load(keystoreStream, passphrase); 157330fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 157430fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(keystore); 157530fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 157630fb85f55cbd8df3005e652da3781f51294baf90ritchie keyManagerFactory.init(keystore, passphrase); 157730fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 157830fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 157930fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 158030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 158130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 158230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 158330fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 158430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 158530fb85f55cbd8df3005e652da3781f51294baf90ritchie 158629d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie private static final void safeClose(Object closeable) { 158729d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie try { 15886625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (closeable != null) { 15896625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (closeable instanceof Closeable) { 15906625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((Closeable) closeable).close(); 15916625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else if (closeable instanceof Socket) { 15926625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((Socket) closeable).close(); 15936625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else if (closeable instanceof ServerSocket) { 15946625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((ServerSocket) closeable).close(); 15956625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 15966625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen throw new IllegalArgumentException("Unknown object to close"); 15976625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 159830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 159929d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie } catch (IOException e) { 160029d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e); 160130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160330fb85f55cbd8df3005e652da3781f51294baf90ritchie 160430fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String hostname; 160530fb85f55cbd8df3005e652da3781f51294baf90ritchie 160630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int myPort; 160730fb85f55cbd8df3005e652da3781f51294baf90ritchie 160830fb85f55cbd8df3005e652da3781f51294baf90ritchie private ServerSocket myServerSocket; 160930fb85f55cbd8df3005e652da3781f51294baf90ritchie 161030fb85f55cbd8df3005e652da3781f51294baf90ritchie private SSLServerSocketFactory sslServerSocketFactory; 161130fb85f55cbd8df3005e652da3781f51294baf90ritchie 161230fb85f55cbd8df3005e652da3781f51294baf90ritchie private Thread myThread; 161330fb85f55cbd8df3005e652da3781f51294baf90ritchie 161430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 161530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 161630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1617d685218eacc23e69f685a76113665f50cc560edfritchie protected AsyncRunner asyncRunner; 161830fb85f55cbd8df3005e652da3781f51294baf90ritchie 161930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 162030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 162130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 162230fb85f55cbd8df3005e652da3781f51294baf90ritchie private TempFileManagerFactory tempFileManagerFactory; 162330fb85f55cbd8df3005e652da3781f51294baf90ritchie 162430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 162530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given port. 162630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 162730fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(int port) { 162830fb85f55cbd8df3005e652da3781f51294baf90ritchie this(null, port); 162930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 163030fb85f55cbd8df3005e652da3781f51294baf90ritchie 163130fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 163230fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 163330fb85f55cbd8df3005e652da3781f51294baf90ritchie // 163430fb85f55cbd8df3005e652da3781f51294baf90ritchie // Threading Strategy. 163530fb85f55cbd8df3005e652da3781f51294baf90ritchie // 163630fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 163730fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 163830fb85f55cbd8df3005e652da3781f51294baf90ritchie 163930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 164030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given hostname and port. 164130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 164230fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(String hostname, int port) { 164330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.hostname = hostname; 164430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myPort = port; 164530fb85f55cbd8df3005e652da3781f51294baf90ritchie setTempFileManagerFactory(new DefaultTempFileManagerFactory()); 164630fb85f55cbd8df3005e652da3781f51294baf90ritchie setAsyncRunner(new DefaultAsyncRunner()); 164730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 164830fb85f55cbd8df3005e652da3781f51294baf90ritchie 164930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 165030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Forcibly closes all connections that are open. 165130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 165230fb85f55cbd8df3005e652da3781f51294baf90ritchie public synchronized void closeAllConnections() { 1653abcf1089ce1de49278970f088883cb32acb4f225ritchie stop(); 1654abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1655abcf1089ce1de49278970f088883cb32acb4f225ritchie 1656abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1657abcf1089ce1de49278970f088883cb32acb4f225ritchie * create a instance of the client handler, subclasses can return a subclass 1658abcf1089ce1de49278970f088883cb32acb4f225ritchie * of the ClientHandler. 16599e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1660abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param finalAccept 1661abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socket the cleint is connected to 1662abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param inputStream 1663abcf1089ce1de49278970f088883cb32acb4f225ritchie * the input stream 1664abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the client handler 1665abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1666abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) { 1667abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ClientHandler(inputStream, finalAccept); 1668abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1669abcf1089ce1de49278970f088883cb32acb4f225ritchie 1670abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1671abcf1089ce1de49278970f088883cb32acb4f225ritchie * Instantiate the server runnable, can be overwritten by subclasses to 1672abcf1089ce1de49278970f088883cb32acb4f225ritchie * provide a subclass of the ServerRunnable. 16739e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1674abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param timeout 1675abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socet timeout to use. 1676abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the server runnable. 1677abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1678abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ServerRunnable createServerRunnable(final int timeout) { 1679abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ServerRunnable(timeout); 168030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 168130fb85f55cbd8df3005e652da3781f51294baf90ritchie 168230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 168330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 168430fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 168530fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 16869e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 168730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 168830fb85f55cbd8df3005e652da3781f51294baf90ritchie * original <b>NanoHTTPD</b> parameters values, as passed to the 168930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>serve()</code> method. 169030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 169130fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 169230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 169330fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(Map<String, String> parms) { 169430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER)); 169530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 169630fb85f55cbd8df3005e652da3781f51294baf90ritchie 1697abcf1089ce1de49278970f088883cb32acb4f225ritchie // ------------------------------------------------------------------------------- 1698abcf1089ce1de49278970f088883cb32acb4f225ritchie // // 1699abcf1089ce1de49278970f088883cb32acb4f225ritchie 170030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 170130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 170230fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 170330fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 17049e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 170530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param queryString 170630fb85f55cbd8df3005e652da3781f51294baf90ritchie * a query string pulled from the URL. 170730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 170830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 170930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 171030fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(String queryString) { 171130fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, List<String>> parms = new HashMap<String, List<String>>(); 171230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (queryString != null) { 171330fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(queryString, "&"); 171430fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 171530fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 171630fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 171730fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); 171830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!parms.containsKey(propertyName)) { 171930fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(propertyName, new ArrayList<String>()); 172030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 172130fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null; 172230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (propertyValue != null) { 172330fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.get(propertyName).add(propertyValue); 172430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 172530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 172693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 172730fb85f55cbd8df3005e652da3781f51294baf90ritchie return parms; 172830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 172993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 173030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 173130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode percent encoded <code>String</code> values. 17329e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 173330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param str 173430fb85f55cbd8df3005e652da3781f51294baf90ritchie * the percent encoded <code>String</code> 173530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return expanded form of the input, for example "foo%20bar" becomes 173630fb85f55cbd8df3005e652da3781f51294baf90ritchie * "foo bar" 173730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 173830fb85f55cbd8df3005e652da3781f51294baf90ritchie protected String decodePercent(String str) { 173930fb85f55cbd8df3005e652da3781f51294baf90ritchie String decoded = null; 174030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 174130fb85f55cbd8df3005e652da3781f51294baf90ritchie decoded = URLDecoder.decode(str, "UTF8"); 174230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (UnsupportedEncodingException ignored) { 174330fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored); 174493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 174530fb85f55cbd8df3005e652da3781f51294baf90ritchie return decoded; 174630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 174793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 17488c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie /** 17498c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie * @return true if the gzip compression should be used if the client 17508c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie * accespts it. 17518c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie */ 17528c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie protected boolean useGzipWhenAccepted() { 17538c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie return true; 17548c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie } 17558c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie 175630fb85f55cbd8df3005e652da3781f51294baf90ritchie public final int getListeningPort() { 175730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort(); 175830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1759964393fd7e5ed49088882a126cf82507184467efMartin M Reed 176030fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean isAlive() { 176130fb85f55cbd8df3005e652da3781f51294baf90ritchie return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); 17628dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1763964393fd7e5ed49088882a126cf82507184467efMartin M Reed 176430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 176530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Call before start() to serve over HTTPS instead of HTTP 176630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 176730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void makeSecure(SSLServerSocketFactory sslServerSocketFactory) { 176830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.sslServerSocketFactory = sslServerSocketFactory; 176930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17709058464950a9734da0a7ff2dc47f3081bbb5117critchie 177130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 17725e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with unknown length (using HTTP 1.1 chunking). 17735e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 17745e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newChunkedResponse(IStatus status, String mimeType, InputStream data) { 17755e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, -1); 17765e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 17775e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 17785e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 17795e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with known length. 17805e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 17819e1ec7bff40d70d31953a04dd448665aaf549395ritchie public Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) { 17825e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, totalBytes); 17835e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 17845e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 17855e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 17865e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 17875e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 17885e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newFixedLengthResponse(IStatus status, String mimeType, String txt) { 17895e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (txt == null) { 17905e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0); 17915e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 17925e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie byte[] bytes; 17935e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie try { 17945e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = txt.getBytes("UTF-8"); 17955e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } catch (UnsupportedEncodingException e) { 17965e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e); 17975e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = new byte[0]; 17985e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 17995e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(bytes), bytes.length); 18005e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18015e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18025e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 18035e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 18045e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 18055e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 18065e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie public Response newFixedLengthResponse(String msg) { 18075e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg); 18085e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 18095e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 18105e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 181130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 181230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 181330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 181430fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 18159e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 181630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param session 181730fb85f55cbd8df3005e652da3781f51294baf90ritchie * The HTTP session 181830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 181930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 182030fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(IHTTPSession session) { 182130fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> files = new HashMap<String, String>(); 182230fb85f55cbd8df3005e652da3781f51294baf90ritchie Method method = session.getMethod(); 182330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.PUT.equals(method) || Method.POST.equals(method)) { 182430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 182530fb85f55cbd8df3005e652da3781f51294baf90ritchie session.parseBody(files); 182630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 18275e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 182830fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 18295e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 183030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18315820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 18325820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 183330fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> parms = session.getParms(); 183430fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString()); 183530fb85f55cbd8df3005e652da3781f51294baf90ritchie return serve(session.getUri(), method, session.getHeaders(), parms, files); 183630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18375820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 183830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 183930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 184030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 184130fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 184230fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 18439e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 184430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param uri 184530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Percent-decoded URI without parameters, for example 184630fb85f55cbd8df3005e652da3781f51294baf90ritchie * "/index.cgi" 184730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param method 184830fb85f55cbd8df3005e652da3781f51294baf90ritchie * "GET", "POST" etc. 184930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 185030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Parsed, percent decoded parameters from URI and, in case of 185130fb85f55cbd8df3005e652da3781f51294baf90ritchie * POST, data. 185230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param headers 185330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Header entries, percent decoded 185430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 185530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 185630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Deprecated 185730fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) { 18585e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); 185930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18605820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 186130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 186230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 18639e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 186430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param asyncRunner 186530fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling threads. 186630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 186730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setAsyncRunner(AsyncRunner asyncRunner) { 186830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.asyncRunner = asyncRunner; 186930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 18705820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 187130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 187230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 18739e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 187430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param tempFileManagerFactory 187530fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling temp files. 187630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 187730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { 187830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManagerFactory = tempFileManagerFactory; 18795820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 18805820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 18818dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 188230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Start the server. 18839e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 188430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @throws IOException 188530fb85f55cbd8df3005e652da3781f51294baf90ritchie * if the socket is in use. 18868dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 188730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void start() throws IOException { 1888c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie start(NanoHTTPD.SOCKET_READ_TIMEOUT); 1889c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie } 1890c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie 1891c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie /** 1892c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * Start the server. 18939e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1894c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @param timeout 1895c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * timeout to use for socket connections. 1896c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @throws IOException 1897c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * if the socket is in use. 1898c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie */ 1899c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie public void start(final int timeout) throws IOException { 190030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.sslServerSocketFactory != null) { 190130fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocket ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); 190230fb85f55cbd8df3005e652da3781f51294baf90ritchie ss.setNeedClientAuth(false); 190330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = ss; 190430fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 190530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = new ServerSocket(); 190630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 190730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.setReuseAddress(true); 19089058464950a9734da0a7ff2dc47f3081bbb5117critchie 1909284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie ServerRunnable serverRunnable = createServerRunnable(timeout); 1910284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie this.myThread = new Thread(serverRunnable); 191130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setDaemon(true); 191230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setName("NanoHttpd Main Listener"); 191330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.start(); 1914284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie while (!serverRunnable.hasBinded && serverRunnable.bindException == null) { 1915284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie try { 1916284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie Thread.sleep(10L); 1917284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } catch (Throwable e) { 1918284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // on android this may not be allowed, that's why we 1919284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // catch throwable the wait should be very short because we are 1920284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // just waiting for the bind of the socket 1921284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1922284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1923284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie if (serverRunnable.bindException != null) { 1924284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie throw serverRunnable.bindException; 1925284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 192630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 19275820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 192830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 192930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Stop the server. 193030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 193130fb85f55cbd8df3005e652da3781f51294baf90ritchie public void stop() { 193230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 193330fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.myServerSocket); 1934abcf1089ce1de49278970f088883cb32acb4f225ritchie this.asyncRunner.closeAll(); 193530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.myThread != null) { 193630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.join(); 193730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 193830fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 193930fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e); 1940964393fd7e5ed49088882a126cf82507184467efMartin M Reed } 194130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1942964393fd7e5ed49088882a126cf82507184467efMartin M Reed 194330fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean wasStarted() { 194430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket != null && this.myThread != null; 19457e423dde0fe863fadbd363de8a94746a30fca0eeMartin M Reed } 1946269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 1947