NanoHTTPD.java revision abcf1089ce1de49278970f088883cb32acb4f225
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: 118b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 128b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 1. Redistributions of source code must retain the above copyright notice, this 138b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * list of conditions and the following disclaimer. 148b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 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. 188b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 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. 228b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 238b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 248b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 258b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 268b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 278b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 288b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 298b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 308b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 318b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 328b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * OF THE POSSIBILITY OF SUCH DAMAGE. 338b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * #L% 348b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie */ 358b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie 363526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.BufferedReader; 373526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.ByteArrayInputStream; 383526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.Closeable; 393526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.File; 403526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.FileInputStream; 413526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.FileOutputStream; 423526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.IOException; 433526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.InputStream; 443526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.InputStreamReader; 453526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.OutputStream; 463526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.PrintWriter; 473526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.PushbackInputStream; 483526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.RandomAccessFile; 493526d931ae635536355e46a6b242405d309c70f3ritchieimport java.io.UnsupportedEncodingException; 50b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetAddress; 51b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetSocketAddress; 52b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.ServerSocket; 53b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.Socket; 54b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.SocketException; 5513736e18ec88e3df74d055e061fb324e04778ad6Paul Hawkeimport java.net.SocketTimeoutException; 56b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.URLDecoder; 57dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.ByteBuffer; 58dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.channels.FileChannel; 5930fb85f55cbd8df3005e652da3781f51294baf90ritchieimport java.security.KeyStore; 607b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat; 61b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.ArrayList; 62b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Calendar; 63abcf1089ce1de49278970f088883cb32acb4f225ritchieimport java.util.Collections; 64b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Date; 65b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.HashMap; 66b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Iterator; 67b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.List; 68b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Locale; 69b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Map; 70b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.StringTokenizer; 71b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.TimeZone; 723526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Level; 733526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Logger; 74269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 7530fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManager; 7630fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.KeyManagerFactory; 7730fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLContext; 7830fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocket; 7930fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.SSLServerSocketFactory; 8030fb85f55cbd8df3005e652da3781f51294baf90ritchieimport javax.net.ssl.TrustManagerFactory; 81f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 82269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 835b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke * A simple, tiny, nicely embeddable HTTP server in Java 84d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 85d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 86b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke * NanoHTTPD 879058464950a9734da0a7ff2dc47f3081bbb5117critchie * <p> 889058464950a9734da0a7ff2dc47f3081bbb5117critchie * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 899058464950a9734da0a7ff2dc47f3081bbb5117critchie * 2010 by Konstantinos Togias 909058464950a9734da0a7ff2dc47f3081bbb5117critchie * </p> 91d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 92d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 93269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 94269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 95d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 96269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 9701ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li> 98269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 999058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>No fixed config files, logging, authorization etc. (Implement yourself if 1009058464950a9734da0a7ff2dc47f3081bbb5117critchie * you need them.)</li> 1019058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT 1029058464950a9734da0a7ff2dc47f3081bbb5117critchie * support in 1.25)</li> 103269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 104269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 105269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 106269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 107269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 110269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 111269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 112269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 114269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 115269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 116cf230611ca85378ae2b0e8130dace5766edfd295Philipp Wiesemann * <li>Contains a built-in list of most common MIME types</li> 1179058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>All header names are converted to lower case so they don't vary between 1189058464950a9734da0a7ff2dc47f3081bbb5117critchie * browsers/clients</li> 119d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 120269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 121d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 122d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 12301ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b> 124269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 125d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 12601ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li> 127d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 128269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 129d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 1309058464950a9734da0a7ff2dc47f3081bbb5117critchie * See the separate "LICENSE.md" file for the distribution license (Modified BSD 1319058464950a9734da0a7ff2dc47f3081bbb5117critchie * licence) 132269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 133d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD { 1349058464950a9734da0a7ff2dc47f3081bbb5117critchie 135269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 13630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 137293e04675f3d4f427c125a26e831d70d5011c79bhflicka */ 13830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface AsyncRunner { 1399058464950a9734da0a7ff2dc47f3081bbb5117critchie 140abcf1089ce1de49278970f088883cb32acb4f225ritchie void closeAll(); 141abcf1089ce1de49278970f088883cb32acb4f225ritchie 142abcf1089ce1de49278970f088883cb32acb4f225ritchie void closed(ClientHandler clientHandler); 143abcf1089ce1de49278970f088883cb32acb4f225ritchie 144abcf1089ce1de49278970f088883cb32acb4f225ritchie void exec(ClientHandler code); 145abcf1089ce1de49278970f088883cb32acb4f225ritchie } 146abcf1089ce1de49278970f088883cb32acb4f225ritchie 147abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 148abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for every new client connection. 149abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 150abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ClientHandler implements Runnable { 151abcf1089ce1de49278970f088883cb32acb4f225ritchie 152abcf1089ce1de49278970f088883cb32acb4f225ritchie private final InputStream inputStream; 153abcf1089ce1de49278970f088883cb32acb4f225ritchie 154abcf1089ce1de49278970f088883cb32acb4f225ritchie private final Socket acceptSocket; 155abcf1089ce1de49278970f088883cb32acb4f225ritchie 156abcf1089ce1de49278970f088883cb32acb4f225ritchie private ClientHandler(InputStream inputStream, Socket acceptSocket) { 157abcf1089ce1de49278970f088883cb32acb4f225ritchie this.inputStream = inputStream; 158abcf1089ce1de49278970f088883cb32acb4f225ritchie this.acceptSocket = acceptSocket; 159abcf1089ce1de49278970f088883cb32acb4f225ritchie } 160abcf1089ce1de49278970f088883cb32acb4f225ritchie 161abcf1089ce1de49278970f088883cb32acb4f225ritchie public void close() { 162abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 163abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 164abcf1089ce1de49278970f088883cb32acb4f225ritchie } 165abcf1089ce1de49278970f088883cb32acb4f225ritchie 166abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 167abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 168abcf1089ce1de49278970f088883cb32acb4f225ritchie OutputStream outputStream = null; 169abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 170abcf1089ce1de49278970f088883cb32acb4f225ritchie outputStream = this.acceptSocket.getOutputStream(); 171abcf1089ce1de49278970f088883cb32acb4f225ritchie TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create(); 172abcf1089ce1de49278970f088883cb32acb4f225ritchie HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress()); 173abcf1089ce1de49278970f088883cb32acb4f225ritchie while (!this.acceptSocket.isClosed()) { 174abcf1089ce1de49278970f088883cb32acb4f225ritchie session.execute(); 175abcf1089ce1de49278970f088883cb32acb4f225ritchie } 176abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (Exception e) { 177abcf1089ce1de49278970f088883cb32acb4f225ritchie // When the socket is closed by the client, 178abcf1089ce1de49278970f088883cb32acb4f225ritchie // we throw our own SocketException 179abcf1089ce1de49278970f088883cb32acb4f225ritchie // to break the "keep alive" loop above. If 180abcf1089ce1de49278970f088883cb32acb4f225ritchie // the exception was anything other 181abcf1089ce1de49278970f088883cb32acb4f225ritchie // than the expected SocketException OR a 182abcf1089ce1de49278970f088883cb32acb4f225ritchie // SocketTimeoutException, print the 183abcf1089ce1de49278970f088883cb32acb4f225ritchie // stacktrace 184abcf1089ce1de49278970f088883cb32acb4f225ritchie if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) { 185abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 186abcf1089ce1de49278970f088883cb32acb4f225ritchie } 187abcf1089ce1de49278970f088883cb32acb4f225ritchie } finally { 188abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(outputStream); 189abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 190abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 191abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.closed(this); 192abcf1089ce1de49278970f088883cb32acb4f225ritchie } 193abcf1089ce1de49278970f088883cb32acb4f225ritchie } 19430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1959058464950a9734da0a7ff2dc47f3081bbb5117critchie 19630fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Cookie { 1979058464950a9734da0a7ff2dc47f3081bbb5117critchie 19830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static String getHTTPTime(int days) { 19930fb85f55cbd8df3005e652da3781f51294baf90ritchie Calendar calendar = Calendar.getInstance(); 20030fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 20130fb85f55cbd8df3005e652da3781f51294baf90ritchie dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 20230fb85f55cbd8df3005e652da3781f51294baf90ritchie calendar.add(Calendar.DAY_OF_MONTH, days); 20330fb85f55cbd8df3005e652da3781f51294baf90ritchie return dateFormat.format(calendar.getTime()); 20430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20530fb85f55cbd8df3005e652da3781f51294baf90ritchie 20630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String n, v, e; 20730fb85f55cbd8df3005e652da3781f51294baf90ritchie 20830fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value) { 20930fb85f55cbd8df3005e652da3781f51294baf90ritchie this(name, value, 30); 21030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21130fb85f55cbd8df3005e652da3781f51294baf90ritchie 21230fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, int numDays) { 21330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 21430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 21530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = getHTTPTime(numDays); 21630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21730fb85f55cbd8df3005e652da3781f51294baf90ritchie 21830fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, String expires) { 21930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 22030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 22130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = expires; 22230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22330fb85f55cbd8df3005e652da3781f51294baf90ritchie 22430fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHTTPHeader() { 22530fb85f55cbd8df3005e652da3781f51294baf90ritchie String fmt = "%s=%s; expires=%s"; 22630fb85f55cbd8df3005e652da3781f51294baf90ritchie return String.format(fmt, this.n, this.v, this.e); 22730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2299058464950a9734da0a7ff2dc47f3081bbb5117critchie 2309058464950a9734da0a7ff2dc47f3081bbb5117critchie /** 23130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Provides rudimentary support for cookies. Doesn't support 'path', 23230fb85f55cbd8df3005e652da3781f51294baf90ritchie * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported 23330fb85f55cbd8df3005e652da3781f51294baf90ritchie * features. 23430fb85f55cbd8df3005e652da3781f51294baf90ritchie * 23530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @author LordFokas 2369058464950a9734da0a7ff2dc47f3081bbb5117critchie */ 23730fb85f55cbd8df3005e652da3781f51294baf90ritchie public class CookieHandler implements Iterable<String> { 2389058464950a9734da0a7ff2dc47f3081bbb5117critchie 23930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final HashMap<String, String> cookies = new HashMap<String, String>(); 2409058464950a9734da0a7ff2dc47f3081bbb5117critchie 24130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final ArrayList<Cookie> queue = new ArrayList<Cookie>(); 2429058464950a9734da0a7ff2dc47f3081bbb5117critchie 24330fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler(Map<String, String> httpHeaders) { 24430fb85f55cbd8df3005e652da3781f51294baf90ritchie String raw = httpHeaders.get("cookie"); 24530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (raw != null) { 24630fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] tokens = raw.split(";"); 24730fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String token : tokens) { 24830fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] data = token.trim().split("="); 24930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (data.length == 2) { 25030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.put(data[0], data[1]); 25130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 25230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 25330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 25430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2559058464950a9734da0a7ff2dc47f3081bbb5117critchie 25630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 25730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Set a cookie with an expiration date from a month ago, effectively 25830fb85f55cbd8df3005e652da3781f51294baf90ritchie * deleting it on the client side. 25930fb85f55cbd8df3005e652da3781f51294baf90ritchie * 26030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 26130fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie name. 26230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 26330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete(String name) { 26430fb85f55cbd8df3005e652da3781f51294baf90ritchie set(name, "-delete-", -30); 26530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2669058464950a9734da0a7ff2dc47f3081bbb5117critchie 26730fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 26830fb85f55cbd8df3005e652da3781f51294baf90ritchie public Iterator<String> iterator() { 26930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.keySet().iterator(); 27030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2719058464950a9734da0a7ff2dc47f3081bbb5117critchie 27230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 27330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Read a cookie from the HTTP Headers. 27430fb85f55cbd8df3005e652da3781f51294baf90ritchie * 27530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 27630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 27730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return The cookie's value if it exists, null otherwise. 27830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 27930fb85f55cbd8df3005e652da3781f51294baf90ritchie public String read(String name) { 28030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.get(name); 28130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2829058464950a9734da0a7ff2dc47f3081bbb5117critchie 28330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(Cookie cookie) { 28430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(cookie); 28530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2869058464950a9734da0a7ff2dc47f3081bbb5117critchie 28730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 28830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sets a cookie. 28930fb85f55cbd8df3005e652da3781f51294baf90ritchie * 29030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 29130fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 29230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param value 29330fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's value. 29430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param expires 29530fb85f55cbd8df3005e652da3781f51294baf90ritchie * How many days until the cookie expires. 29630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 29730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(String name, String value, int expires) { 29830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); 29930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 300269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 30130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 30230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Internally used by the webserver to add all queued cookies into the 30330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Response's HTTP Headers. 30430fb85f55cbd8df3005e652da3781f51294baf90ritchie * 30530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param response 30630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The Response object to which headers the queued cookies 30730fb85f55cbd8df3005e652da3781f51294baf90ritchie * will be added. 30830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 30930fb85f55cbd8df3005e652da3781f51294baf90ritchie public void unloadQueue(Response response) { 31030fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Cookie cookie : this.queue) { 31130fb85f55cbd8df3005e652da3781f51294baf90ritchie response.addHeader("Set-Cookie", cookie.getHTTPHeader()); 31230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 31330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 314f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke } 315f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 3165b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 31730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default threading strategy for NanoHTTPD. 31830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 31930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 32030fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, the server spawns a new Thread for every incoming request. 32130fb85f55cbd8df3005e652da3781f51294baf90ritchie * These are set to <i>daemon</i> status, and named according to the request 32230fb85f55cbd8df3005e652da3781f51294baf90ritchie * number. The name is useful when profiling the application. 32330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3245b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 32530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultAsyncRunner implements AsyncRunner { 3263f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke 32730fb85f55cbd8df3005e652da3781f51294baf90ritchie private long requestCount; 32830fb85f55cbd8df3005e652da3781f51294baf90ritchie 329abcf1089ce1de49278970f088883cb32acb4f225ritchie private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>()); 330abcf1089ce1de49278970f088883cb32acb4f225ritchie 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); 37730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.file.delete(); 3789058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3799058464950a9734da0a7ff2dc47f3081bbb5117critchie 38030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38130fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getName() { 38230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.file.getAbsolutePath(); 3839058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3849058464950a9734da0a7ff2dc47f3081bbb5117critchie 38530fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 38630fb85f55cbd8df3005e652da3781f51294baf90ritchie public OutputStream open() throws Exception { 38730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.fstream; 38830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 3899058464950a9734da0a7ff2dc47f3081bbb5117critchie } 390f7eb2ae15b4d921ae23e20cae59f36b21056b2fcAaron Davidson 3913f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke /** 39230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 39330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 39430fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 39530fb85f55cbd8df3005e652da3781f51294baf90ritchie * This class stores its files in the standard location (that is, wherever 39630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>java.io.tmpdir</code> points to). Files are added to an internal 39730fb85f55cbd8df3005e652da3781f51294baf90ritchie * list, and deleted when no longer needed (that is, when 39830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>clear()</code> is invoked at the end of processing a request). 39930fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 4003f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke */ 40130fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFileManager implements TempFileManager { 402f39ce1dd765bb79b0a83a8e6dd921f0d3986ebf6Aaron Davidson 40330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String tmpdir; 4049058464950a9734da0a7ff2dc47f3081bbb5117critchie 40530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final List<TempFile> tempFiles; 4069058464950a9734da0a7ff2dc47f3081bbb5117critchie 40730fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFileManager() { 40830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tmpdir = System.getProperty("java.io.tmpdir"); 40930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles = new ArrayList<TempFile>(); 41030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 411d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 41230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 41330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void clear() { 41430fb85f55cbd8df3005e652da3781f51294baf90ritchie for (TempFile file : this.tempFiles) { 41530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 41630fb85f55cbd8df3005e652da3781f51294baf90ritchie file.delete(); 41730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception ignored) { 41830fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); 41930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 420fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 42130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.clear(); 422269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 423269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 42430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 42530fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFile createTempFile() throws Exception { 42630fb85f55cbd8df3005e652da3781f51294baf90ritchie DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); 42730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.add(tempFile); 42830fb85f55cbd8df3005e652da3781f51294baf90ritchie return tempFile; 42930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 4300a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4310a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 4320a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander /** 43330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 4340a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander */ 43530fb85f55cbd8df3005e652da3781f51294baf90ritchie private class DefaultTempFileManagerFactory implements TempFileManagerFactory { 4360a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 43730fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 43830fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFileManager create() { 43930fb85f55cbd8df3005e652da3781f51294baf90ritchie return new DefaultTempFileManager(); 4400a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4410a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander } 4428dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 44330fb85f55cbd8df3005e652da3781f51294baf90ritchie protected class HTTPSession implements IHTTPSession { 4448dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 44530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int BUFSIZE = 8192; 4468dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 44730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final TempFileManager tempFileManager; 4488dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 44930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream outputStream; 4509788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 45130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final PushbackInputStream inputStream; 45293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 45330fb85f55cbd8df3005e652da3781f51294baf90ritchie private int splitbyte; 45493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 45530fb85f55cbd8df3005e652da3781f51294baf90ritchie private int rlen; 456f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 45730fb85f55cbd8df3005e652da3781f51294baf90ritchie private String uri; 458f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 45930fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method method; 460f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 46130fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> parms; 4625b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 46330fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> headers; 4645b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 46530fb85f55cbd8df3005e652da3781f51294baf90ritchie private CookieHandler cookies; 4665b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 46730fb85f55cbd8df3005e652da3781f51294baf90ritchie private String queryParameterString; 4685b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 46930fb85f55cbd8df3005e652da3781f51294baf90ritchie private String remoteIp; 4708dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 47130fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { 47230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 47330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 47430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 4758dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 4768dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 47730fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) { 47830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 47930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE); 48030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 48130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString(); 48230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 4839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 4849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 4859788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 48630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the sent headers and loads the data into Key/value pairs 4879788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 48830fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException { 4899788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke try { 49030fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the request line 49130fb85f55cbd8df3005e652da3781f51294baf90ritchie String inLine = in.readLine(); 49230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (inLine == null) { 49330fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 49430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 495fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 49630fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(inLine); 49730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 49830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 49930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 5009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 50130fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("method", st.nextToken()); 5027f0727787957c2f093412c01d165846842ec2425Paul Hawke 50330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 50430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 5057f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5067f0727787957c2f093412c01d165846842ec2425Paul Hawke 50730fb85f55cbd8df3005e652da3781f51294baf90ritchie String uri = st.nextToken(); 5087f0727787957c2f093412c01d165846842ec2425Paul Hawke 50930fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode parameters from the URI 51030fb85f55cbd8df3005e652da3781f51294baf90ritchie int qmi = uri.indexOf('?'); 51130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (qmi >= 0) { 51230fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(uri.substring(qmi + 1), parms); 51330fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri.substring(0, qmi)); 51430fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 51530fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri); 5167f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5177f0727787957c2f093412c01d165846842ec2425Paul Hawke 51830fb85f55cbd8df3005e652da3781f51294baf90ritchie // If there's another token, its protocol version, 51930fb85f55cbd8df3005e652da3781f51294baf90ritchie // followed by HTTP headers. Ignore version but parse headers. 52030fb85f55cbd8df3005e652da3781f51294baf90ritchie // NOTE: this now forces header names lower case since they are 52130fb85f55cbd8df3005e652da3781f51294baf90ritchie // case insensitive and vary by client. 52230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 52330fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange.."); 52430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 52530fb85f55cbd8df3005e652da3781f51294baf90ritchie String line = in.readLine(); 52630fb85f55cbd8df3005e652da3781f51294baf90ritchie while (line != null && line.trim().length() > 0) { 52730fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = line.indexOf(':'); 52830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p >= 0) { 52930fb85f55cbd8df3005e652da3781f51294baf90ritchie headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim()); 5307f0727787957c2f093412c01d165846842ec2425Paul Hawke } 53130fb85f55cbd8df3005e652da3781f51294baf90ritchie line = in.readLine(); 5327f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5337f0727787957c2f093412c01d165846842ec2425Paul Hawke 53430fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("uri", uri); 5357f0727787957c2f093412c01d165846842ec2425Paul Hawke } catch (IOException ioe) { 53630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 5377f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5387f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5397f0727787957c2f093412c01d165846842ec2425Paul Hawke 54030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 54130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the Multipart Body data and put it into Key/Value pairs. 54230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 54330fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms, Map<String, String> files) throws ResponseException { 54430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 54530fb85f55cbd8df3005e652da3781f51294baf90ritchie int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes()); 54630fb85f55cbd8df3005e652da3781f51294baf90ritchie int boundarycount = 1; 54730fb85f55cbd8df3005e652da3781f51294baf90ritchie String mpline = in.readLine(); 54830fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null) { 54930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!mpline.contains(boundary)) { 55030fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, 55130fb85f55cbd8df3005e652da3781f51294baf90ritchie "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html"); 55230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 55330fb85f55cbd8df3005e652da3781f51294baf90ritchie boundarycount++; 55430fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> item = new HashMap<String, String>(); 55530fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 55630fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && mpline.trim().length() > 0) { 55730fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = mpline.indexOf(':'); 55830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p != -1) { 55930fb85f55cbd8df3005e652da3781f51294baf90ritchie item.put(mpline.substring(0, p).trim().toLowerCase(Locale.US), mpline.substring(p + 1).trim()); 56030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 56130fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 56230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 56330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mpline != null) { 56430fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentDisposition = item.get("content-disposition"); 56530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (contentDisposition == null) { 56630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, 56730fb85f55cbd8df3005e652da3781f51294baf90ritchie "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html"); 56830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 56930fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(contentDisposition, ";"); 57030fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> disposition = new HashMap<String, String>(); 57130fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 57230fb85f55cbd8df3005e652da3781f51294baf90ritchie String token = st.nextToken().trim(); 57330fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = token.indexOf('='); 57430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p != -1) { 57530fb85f55cbd8df3005e652da3781f51294baf90ritchie disposition.put(token.substring(0, p).trim().toLowerCase(Locale.US), token.substring(p + 1).trim()); 57630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 57730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 57830fb85f55cbd8df3005e652da3781f51294baf90ritchie String pname = disposition.get("name"); 57930fb85f55cbd8df3005e652da3781f51294baf90ritchie pname = pname.substring(1, pname.length() - 1); 58030fb85f55cbd8df3005e652da3781f51294baf90ritchie 58130fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = ""; 58230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (item.get("content-type") == null) { 58330fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && !mpline.contains(boundary)) { 58430fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 58530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mpline != null) { 58630fb85f55cbd8df3005e652da3781f51294baf90ritchie int d = mpline.indexOf(boundary); 58730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (d == -1) { 58830fb85f55cbd8df3005e652da3781f51294baf90ritchie value += mpline; 58930fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 59030fb85f55cbd8df3005e652da3781f51294baf90ritchie value += mpline.substring(0, d - 2); 59130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59430fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 59530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (boundarycount > bpositions.length) { 59630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request"); 59730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 59830fb85f55cbd8df3005e652da3781f51294baf90ritchie int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]); 59930fb85f55cbd8df3005e652da3781f51294baf90ritchie String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4); 60030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!files.containsKey(pname)) { 60130fb85f55cbd8df3005e652da3781f51294baf90ritchie files.put(pname, path); 60230fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 60330fb85f55cbd8df3005e652da3781f51294baf90ritchie int count = 2; 60430fb85f55cbd8df3005e652da3781f51294baf90ritchie while (files.containsKey(pname + count)) { 60530fb85f55cbd8df3005e652da3781f51294baf90ritchie count++; 60630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 60730fb85f55cbd8df3005e652da3781f51294baf90ritchie files.put(pname + count, path); 60830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 60930fb85f55cbd8df3005e652da3781f51294baf90ritchie value = disposition.get("filename"); 61030fb85f55cbd8df3005e652da3781f51294baf90ritchie value = value.substring(1, value.length() - 1); 61130fb85f55cbd8df3005e652da3781f51294baf90ritchie do { 61230fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 61330fb85f55cbd8df3005e652da3781f51294baf90ritchie } while (mpline != null && !mpline.contains(boundary)); 61430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 61530fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(pname, value); 61698d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 61798d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 61830fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 61930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 620c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 621fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 622fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 62330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 62430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes parameters in percent-encoded URI-format ( e.g. 62530fb85f55cbd8df3005e652da3781f51294baf90ritchie * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 62630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Map. NOTE: this doesn't support multiple identical keys due to the 62730fb85f55cbd8df3005e652da3781f51294baf90ritchie * simplicity of Map. 62830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 62930fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeParms(String parms, Map<String, String> p) { 63030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (parms == null) { 63130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = ""; 63230fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 633c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 634c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke 63530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = parms; 63630fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(parms, "&"); 63730fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 63830fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 63930fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 64030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (sep >= 0) { 64130fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); 64230fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 64330fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e).trim(), ""); 64430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 645fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 646fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 647fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 64830fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 64930fb85f55cbd8df3005e652da3781f51294baf90ritchie public void execute() throws IOException { 65030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 65130fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the first 8192 bytes. 65230fb85f55cbd8df3005e652da3781f51294baf90ritchie // The full header should fit in here. 65330fb85f55cbd8df3005e652da3781f51294baf90ritchie // Apache's default header limit is 8KB. 65430fb85f55cbd8df3005e652da3781f51294baf90ritchie // Do NOT assume that a single read will get the entire header 65530fb85f55cbd8df3005e652da3781f51294baf90ritchie // at once! 65630fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buf = new byte[HTTPSession.BUFSIZE]; 65730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.splitbyte = 0; 65830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = 0; 6599c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie 6609c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie int read = -1; 6619c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie try { 6629c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); 6639c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } catch (Exception e) { 6649c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6659c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6669c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6679c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6689c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (read == -1) { 6699c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie // socket was been closed 6709c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 6719c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 6729c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 6739c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 6749c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie while (read > 0) { 6759c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.rlen += read; 6769c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.splitbyte = findHeaderEnd(buf, this.rlen); 6779c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (this.splitbyte > 0) { 6789c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie break; 67930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6809c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); 68130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 68230fb85f55cbd8df3005e652da3781f51294baf90ritchie 68330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.splitbyte < this.rlen) { 68430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.inputStream.unread(buf, this.splitbyte, this.rlen - this.splitbyte); 68530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 68630fb85f55cbd8df3005e652da3781f51294baf90ritchie 68730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.parms = new HashMap<String, String>(); 68830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null == this.headers) { 68930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 69030fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 69130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.clear(); 69230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 69330fb85f55cbd8df3005e652da3781f51294baf90ritchie 69430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null != this.remoteIp) { 69530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("remote-addr", this.remoteIp); 69630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.put("http-client-ip", this.remoteIp); 69730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 69830fb85f55cbd8df3005e652da3781f51294baf90ritchie 69930fb85f55cbd8df3005e652da3781f51294baf90ritchie // Create a BufferedReader for parsing the header. 70030fb85f55cbd8df3005e652da3781f51294baf90ritchie BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen))); 70130fb85f55cbd8df3005e652da3781f51294baf90ritchie 70230fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode the header into parms and header java properties 70330fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> pre = new HashMap<String, String>(); 70430fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeHeader(hin, pre, this.parms, this.headers); 70530fb85f55cbd8df3005e652da3781f51294baf90ritchie 70630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.method = Method.lookup(pre.get("method")); 70730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.method == null) { 70830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); 70930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 71030fb85f55cbd8df3005e652da3781f51294baf90ritchie 71130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.uri = pre.get("uri"); 71230fb85f55cbd8df3005e652da3781f51294baf90ritchie 71330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies = new CookieHandler(this.headers); 71430fb85f55cbd8df3005e652da3781f51294baf90ritchie 71530fb85f55cbd8df3005e652da3781f51294baf90ritchie // Ok, now do the serve() 71630fb85f55cbd8df3005e652da3781f51294baf90ritchie Response r = serve(this); 71730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (r == null) { 71830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 71930fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 72030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.unloadQueue(r); 72130fb85f55cbd8df3005e652da3781f51294baf90ritchie r.setRequestMethod(this.method); 72230fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 72330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 72430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketException e) { 72530fb85f55cbd8df3005e652da3781f51294baf90ritchie // throw it out to close socket object (finalAccept) 72630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw e; 72730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketTimeoutException ste) { 72830fb85f55cbd8df3005e652da3781f51294baf90ritchie // treat socket timeouts the same way we treat socket exceptions 72930fb85f55cbd8df3005e652da3781f51294baf90ritchie // i.e. close the stream & finalAccept object by throwing the 73030fb85f55cbd8df3005e652da3781f51294baf90ritchie // exception up the call stack. 73130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw ste; 73230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 73330fb85f55cbd8df3005e652da3781f51294baf90ritchie Response r = new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 73430fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 73530fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 73630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 73730fb85f55cbd8df3005e652da3781f51294baf90ritchie Response r = new Response(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 73830fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 73930fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 74030fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 74130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager.clear(); 742fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 743fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 744fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 74530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 74630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find byte index separating header from body. It must be the last byte 74730fb85f55cbd8df3005e652da3781f51294baf90ritchie * of the first two sequential new lines. 74830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 74930fb85f55cbd8df3005e652da3781f51294baf90ritchie private int findHeaderEnd(final byte[] buf, int rlen) { 75030fb85f55cbd8df3005e652da3781f51294baf90ritchie int splitbyte = 0; 75130fb85f55cbd8df3005e652da3781f51294baf90ritchie while (splitbyte + 3 < rlen) { 75230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 75330fb85f55cbd8df3005e652da3781f51294baf90ritchie return splitbyte + 4; 754fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 75530fb85f55cbd8df3005e652da3781f51294baf90ritchie splitbyte++; 756fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 75730fb85f55cbd8df3005e652da3781f51294baf90ritchie return 0; 758fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 759fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 76030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 76130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find the byte positions where multipart boundaries start. 76230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 76330fb85f55cbd8df3005e652da3781f51294baf90ritchie private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) { 76430fb85f55cbd8df3005e652da3781f51294baf90ritchie int matchcount = 0; 76530fb85f55cbd8df3005e652da3781f51294baf90ritchie int matchbyte = -1; 76630fb85f55cbd8df3005e652da3781f51294baf90ritchie List<Integer> matchbytes = new ArrayList<Integer>(); 76730fb85f55cbd8df3005e652da3781f51294baf90ritchie for (int i = 0; i < b.limit(); i++) { 76830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (b.get(i) == boundary[matchcount]) { 76930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (matchcount == 0) { 77030fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbyte = i; 77130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 77230fb85f55cbd8df3005e652da3781f51294baf90ritchie matchcount++; 77330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (matchcount == boundary.length) { 77430fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbytes.add(matchbyte); 77530fb85f55cbd8df3005e652da3781f51294baf90ritchie matchcount = 0; 77630fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbyte = -1; 77730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 77830fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 77930fb85f55cbd8df3005e652da3781f51294baf90ritchie i -= matchcount; 78030fb85f55cbd8df3005e652da3781f51294baf90ritchie matchcount = 0; 78130fb85f55cbd8df3005e652da3781f51294baf90ritchie matchbyte = -1; 78230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 78330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 78430fb85f55cbd8df3005e652da3781f51294baf90ritchie int[] ret = new int[matchbytes.size()]; 78530fb85f55cbd8df3005e652da3781f51294baf90ritchie for (int i = 0; i < ret.length; i++) { 78630fb85f55cbd8df3005e652da3781f51294baf90ritchie ret[i] = matchbytes.get(i); 78730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 78830fb85f55cbd8df3005e652da3781f51294baf90ritchie return ret; 789c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 790c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 79130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 79230fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler getCookies() { 79330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies; 794c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 795c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 79630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 79730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getHeaders() { 79830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.headers; 799c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 800c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 80130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 80230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final InputStream getInputStream() { 80330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.inputStream; 804c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 805c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 80630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 80730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Method getMethod() { 80830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.method; 809c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 810c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 81130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 81230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getParms() { 81330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.parms; 814c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 815c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 81630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 81730fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getQueryParameterString() { 81830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.queryParameterString; 819c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 820c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 82130fb85f55cbd8df3005e652da3781f51294baf90ritchie private RandomAccessFile getTmpBucket() { 82230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 82330fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile tempFile = this.tempFileManager.createTempFile(); 82430fb85f55cbd8df3005e652da3781f51294baf90ritchie return new RandomAccessFile(tempFile.getName(), "rw"); 82530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 82630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 8279788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8289788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 8297e4e4ae652c755faea626f1e2538d495f96e648esynapticloop 830bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 83130fb85f55cbd8df3005e652da3781f51294baf90ritchie public final String getUri() { 83230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.uri; 83393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 83493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 835bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 836bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel public void parseBody(Map<String, String> files) throws IOException, ResponseException { 83793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed RandomAccessFile randomAccessFile = null; 83893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed BufferedReader in = null; 83993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed try { 84093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 841412e2e136193da510e859f73f9ff2ac21565df69james.mcclure randomAccessFile = getTmpBucket(); 842d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 843745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed long size; 84430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.headers.containsKey("content-length")) { 84530fb85f55cbd8df3005e652da3781f51294baf90ritchie size = Integer.parseInt(this.headers.get("content-length")); 84630fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (this.splitbyte < this.rlen) { 84730fb85f55cbd8df3005e652da3781f51294baf90ritchie size = this.rlen - this.splitbyte; 848745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed } else { 849d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke size = 0; 8500cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 851d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 852d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke // Now read all the body and write it to f 853745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed byte[] buf = new byte[512]; 85430fb85f55cbd8df3005e652da3781f51294baf90ritchie while (this.rlen >= 0 && size > 0) { 85530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, 512)); 85630fb85f55cbd8df3005e652da3781f51294baf90ritchie size -= this.rlen; 85730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.rlen > 0) { 85830fb85f55cbd8df3005e652da3781f51294baf90ritchie randomAccessFile.write(buf, 0, this.rlen); 8590cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 860269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 861269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 862269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Get the raw body as a byte [] 863412e2e136193da510e859f73f9ff2ac21565df69james.mcclure ByteBuffer fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); 864412e2e136193da510e859f73f9ff2ac21565df69james.mcclure randomAccessFile.seek(0); 865269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 866269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Create a BufferedReader for easily reading it as string. 867412e2e136193da510e859f73f9ff2ac21565df69james.mcclure InputStream bin = new FileInputStream(randomAccessFile.getFD()); 86893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed in = new BufferedReader(new InputStreamReader(bin)); 869269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 870269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 871269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 87230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.POST.equals(this.method)) { 873269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 87430fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentTypeHeader = this.headers.get("content-type"); 8752b54eda5fee8430268a99d73b061e790362db544Tom Hermann 8762c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke StringTokenizer st = null; 8772c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (contentTypeHeader != null) { 8782c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke st = new StringTokenizer(contentTypeHeader, ",; "); 8792c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (st.hasMoreTokens()) { 8802c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke contentType = st.nextToken(); 8812c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke } 882269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 883269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 8840277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke if ("multipart/form-data".equalsIgnoreCase(contentType)) { 885269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 8860cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 8879058464950a9734da0a7ff2dc47f3081bbb5117critchie throw new ResponseException(Response.Status.BAD_REQUEST, 8889058464950a9734da0a7ff2dc47f3081bbb5117critchie "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 8890cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 8902b54eda5fee8430268a99d73b061e790362db544Tom Hermann 8914e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundaryStartString = "boundary="; 8922b54eda5fee8430268a99d73b061e790362db544Tom Hermann int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length(); 8934e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length()); 894220e1a21e7bbb831d06551c72799dfedc1db979fPaul Hawke if (boundary.startsWith("\"") && boundary.endsWith("\"")) { 8954e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke boundary = boundary.substring(1, boundary.length() - 1); 8964e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 897269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 89830fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeMultipartData(boundary, fbuf, in, this.parms, files); 899269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 900269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String postLine = ""; 90135b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright StringBuilder postLineBuffer = new StringBuilder(); 902269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke char pbuf[] = new char[512]; 903269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke int read = in.read(pbuf); 904269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke while (read >= 0 && !postLine.endsWith("\r\n")) { 90535b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright postLine = String.valueOf(pbuf, 0, read); 90635b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright postLineBuffer.append(postLine); 907269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke read = in.read(pbuf); 908269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 90935b077b8e55a8de374f4f314e151c9cb65eb050aTim Wright postLine = postLineBuffer.toString().trim(); 910e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown // Handle application/x-www-form-urlencoded 911e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) { 91230fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(postLine, this.parms); 913e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } else if (postLine.length() != 0) { 9149058464950a9734da0a7ff2dc47f3081bbb5117critchie // Special case for raw POST data => create a 9159058464950a9734da0a7ff2dc47f3081bbb5117critchie // special files entry "postData" with raw content 9169058464950a9734da0a7ff2dc47f3081bbb5117critchie // data 9179058464950a9734da0a7ff2dc47f3081bbb5117critchie files.put("postData", postLine); 918e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } 919269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 92030fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (Method.PUT.equals(this.method)) { 921dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke files.put("content", saveTmpFile(fbuf, 0, fbuf.limit())); 922b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } 9234e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } finally { 9249cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed safeClose(randomAccessFile); 92593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed safeClose(in); 926269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 927269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 928269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 929269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 93030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Retrieves the content of a sent file and saves it to a temporary 93130fb85f55cbd8df3005e652da3781f51294baf90ritchie * file. The full path to the saved file is returned. 932d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 93330fb85f55cbd8df3005e652da3781f51294baf90ritchie private String saveTmpFile(ByteBuffer b, int offset, int len) { 93430fb85f55cbd8df3005e652da3781f51294baf90ritchie String path = ""; 93530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (len > 0) { 93630fb85f55cbd8df3005e652da3781f51294baf90ritchie FileOutputStream fileOutputStream = null; 93730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 93830fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile tempFile = this.tempFileManager.createTempFile(); 93930fb85f55cbd8df3005e652da3781f51294baf90ritchie ByteBuffer src = b.duplicate(); 94030fb85f55cbd8df3005e652da3781f51294baf90ritchie fileOutputStream = new FileOutputStream(tempFile.getName()); 94130fb85f55cbd8df3005e652da3781f51294baf90ritchie FileChannel dest = fileOutputStream.getChannel(); 94230fb85f55cbd8df3005e652da3781f51294baf90ritchie src.position(offset).limit(offset + len); 94330fb85f55cbd8df3005e652da3781f51294baf90ritchie dest.write(src.slice()); 94430fb85f55cbd8df3005e652da3781f51294baf90ritchie path = tempFile.getName(); 94530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { // Catch exception if any 94630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 94730fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 94830fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(fileOutputStream); 9490cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 95030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 95130fb85f55cbd8df3005e652da3781f51294baf90ritchie return path; 95230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 9530cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke 95430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 95530fb85f55cbd8df3005e652da3781f51294baf90ritchie * It returns the offset separating multipart file headers from the 95630fb85f55cbd8df3005e652da3781f51294baf90ritchie * file's data. 95730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 95830fb85f55cbd8df3005e652da3781f51294baf90ritchie private int stripMultipartHeaders(ByteBuffer b, int offset) { 95930fb85f55cbd8df3005e652da3781f51294baf90ritchie int i; 96030fb85f55cbd8df3005e652da3781f51294baf90ritchie for (i = offset; i < b.limit(); i++) { 96130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (b.get(i) == '\r' && b.get(++i) == '\n' && b.get(++i) == '\r' && b.get(++i) == '\n') { 96230fb85f55cbd8df3005e652da3781f51294baf90ritchie break; 9630cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 96430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 96530fb85f55cbd8df3005e652da3781f51294baf90ritchie return i + 1; 96630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 96730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 968269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 96930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 97030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Handles one session, i.e. parses the HTTP request and returns the 97130fb85f55cbd8df3005e652da3781f51294baf90ritchie * response. 97230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 97330fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IHTTPSession { 974269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 97530fb85f55cbd8df3005e652da3781f51294baf90ritchie void execute() throws IOException; 976269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 97730fb85f55cbd8df3005e652da3781f51294baf90ritchie CookieHandler getCookies(); 978269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 97930fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getHeaders(); 980269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 98130fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream getInputStream(); 98230fb85f55cbd8df3005e652da3781f51294baf90ritchie 98330fb85f55cbd8df3005e652da3781f51294baf90ritchie Method getMethod(); 98430fb85f55cbd8df3005e652da3781f51294baf90ritchie 98530fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getParms(); 98630fb85f55cbd8df3005e652da3781f51294baf90ritchie 98730fb85f55cbd8df3005e652da3781f51294baf90ritchie String getQueryParameterString(); 988269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 989269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 99030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return the path part of the URL. 991d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 99230fb85f55cbd8df3005e652da3781f51294baf90ritchie String getUri(); 993269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 994269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 99530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds the files in the request body to the files map. 99630fb85f55cbd8df3005e652da3781f51294baf90ritchie * 99730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param files 99830fb85f55cbd8df3005e652da3781f51294baf90ritchie * map to modify 999d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 100030fb85f55cbd8df3005e652da3781f51294baf90ritchie void parseBody(Map<String, String> files) throws IOException, ResponseException; 100130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 100230fb85f55cbd8df3005e652da3781f51294baf90ritchie 100330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 100430fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP Request methods, with the ability to decode a <code>String</code> 100530fb85f55cbd8df3005e652da3781f51294baf90ritchie * back to its enum value. 100630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 100730fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Method { 100830fb85f55cbd8df3005e652da3781f51294baf90ritchie GET, 100930fb85f55cbd8df3005e652da3781f51294baf90ritchie PUT, 101030fb85f55cbd8df3005e652da3781f51294baf90ritchie POST, 101130fb85f55cbd8df3005e652da3781f51294baf90ritchie DELETE, 101230fb85f55cbd8df3005e652da3781f51294baf90ritchie HEAD, 101330fb85f55cbd8df3005e652da3781f51294baf90ritchie OPTIONS; 101430fb85f55cbd8df3005e652da3781f51294baf90ritchie 101530fb85f55cbd8df3005e652da3781f51294baf90ritchie static Method lookup(String method) { 101630fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Method m : Method.values()) { 101730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (m.toString().equalsIgnoreCase(method)) { 101830fb85f55cbd8df3005e652da3781f51294baf90ritchie return m; 1019269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1020269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 102130fb85f55cbd8df3005e652da3781f51294baf90ritchie return null; 102230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 102330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 102430fb85f55cbd8df3005e652da3781f51294baf90ritchie 102530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 102630fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP response. Return one of these from serve(). 102730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 102830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Response { 102930fb85f55cbd8df3005e652da3781f51294baf90ritchie 103030fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IStatus { 103130fb85f55cbd8df3005e652da3781f51294baf90ritchie 103230fb85f55cbd8df3005e652da3781f51294baf90ritchie String getDescription(); 103330fb85f55cbd8df3005e652da3781f51294baf90ritchie 103430fb85f55cbd8df3005e652da3781f51294baf90ritchie int getRequestStatus(); 1035269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1036269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1037269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 103830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Some HTTP response status codes 1039d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 104030fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Status implements IStatus { 104130fb85f55cbd8df3005e652da3781f51294baf90ritchie SWITCH_PROTOCOL(101, "Switching Protocols"), 104230fb85f55cbd8df3005e652da3781f51294baf90ritchie OK(200, "OK"), 104330fb85f55cbd8df3005e652da3781f51294baf90ritchie CREATED(201, "Created"), 104430fb85f55cbd8df3005e652da3781f51294baf90ritchie ACCEPTED(202, "Accepted"), 104530fb85f55cbd8df3005e652da3781f51294baf90ritchie NO_CONTENT(204, "No Content"), 104630fb85f55cbd8df3005e652da3781f51294baf90ritchie PARTIAL_CONTENT(206, "Partial Content"), 104730fb85f55cbd8df3005e652da3781f51294baf90ritchie REDIRECT(301, "Moved Permanently"), 104830fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_MODIFIED(304, "Not Modified"), 104930fb85f55cbd8df3005e652da3781f51294baf90ritchie BAD_REQUEST(400, "Bad Request"), 105030fb85f55cbd8df3005e652da3781f51294baf90ritchie UNAUTHORIZED(401, "Unauthorized"), 105130fb85f55cbd8df3005e652da3781f51294baf90ritchie FORBIDDEN(403, "Forbidden"), 105230fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_FOUND(404, "Not Found"), 105330fb85f55cbd8df3005e652da3781f51294baf90ritchie METHOD_NOT_ALLOWED(405, "Method Not Allowed"), 105430fb85f55cbd8df3005e652da3781f51294baf90ritchie RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), 105530fb85f55cbd8df3005e652da3781f51294baf90ritchie INTERNAL_ERROR(500, "Internal Server Error"); 105630fb85f55cbd8df3005e652da3781f51294baf90ritchie 105730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int requestStatus; 105830fb85f55cbd8df3005e652da3781f51294baf90ritchie 105930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String description; 106030fb85f55cbd8df3005e652da3781f51294baf90ritchie 106130fb85f55cbd8df3005e652da3781f51294baf90ritchie Status(int requestStatus, String description) { 106230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestStatus = requestStatus; 106330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.description = description; 1064269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 106530fb85f55cbd8df3005e652da3781f51294baf90ritchie 106630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 106730fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getDescription() { 106830fb85f55cbd8df3005e652da3781f51294baf90ritchie return "" + this.requestStatus + " " + this.description; 106930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 107030fb85f55cbd8df3005e652da3781f51294baf90ritchie 107130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 107230fb85f55cbd8df3005e652da3781f51294baf90ritchie public int getRequestStatus() { 107330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestStatus; 1074269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1075269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1076269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1077269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 107830fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP status code after processing, e.g. "200 OK", Status.OK 1079d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 108030fb85f55cbd8df3005e652da3781f51294baf90ritchie private IStatus status; 1081269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 108230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 108330fb85f55cbd8df3005e652da3781f51294baf90ritchie * MIME type of content, e.g. "text/html" 108430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 108530fb85f55cbd8df3005e652da3781f51294baf90ritchie private String mimeType; 108630fb85f55cbd8df3005e652da3781f51294baf90ritchie 108730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 108830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Data of the response, may be null. 108930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 109030fb85f55cbd8df3005e652da3781f51294baf90ritchie private InputStream data; 109130fb85f55cbd8df3005e652da3781f51294baf90ritchie 109230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 109330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Headers for the HTTP response. Use addHeader() to add lines. 109430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 109530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Map<String, String> header = new HashMap<String, String>(); 109630fb85f55cbd8df3005e652da3781f51294baf90ritchie 109730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 109830fb85f55cbd8df3005e652da3781f51294baf90ritchie * The request method that spawned this response. 109930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 110030fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method requestMethod; 110130fb85f55cbd8df3005e652da3781f51294baf90ritchie 110230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 110330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Use chunkedTransfer 110430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 110530fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean chunkedTransfer; 110630fb85f55cbd8df3005e652da3781f51294baf90ritchie 110730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 110830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Basic constructor. 110930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 111030fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response(IStatus status, String mimeType, InputStream data) { 111130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 111230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 111330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = data; 111430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 111530fb85f55cbd8df3005e652da3781f51294baf90ritchie 111630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 111730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Convenience method that makes an InputStream out of given text. 111830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 111930fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response(IStatus status, String mimeType, String txt) { 112030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 112130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 1122dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke try { 112330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = txt != null ? new ByteArrayInputStream(txt.getBytes("UTF-8")) : null; 112430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (java.io.UnsupportedEncodingException uee) { 112530fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem", uee); 1126dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke } 1127dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke } 1128dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke 1129269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 113030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default constructor: response = Status.OK, mime = MIME_HTML and your 113130fb85f55cbd8df3005e652da3781f51294baf90ritchie * supplied message 1132269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 113330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response(String msg) { 113430fb85f55cbd8df3005e652da3781f51294baf90ritchie this(Status.OK, NanoHTTPD.MIME_HTML, msg); 1135269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1136269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1137269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 113830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds given line to the header. 1139269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 114030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void addHeader(String name, String value) { 114130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.header.put(name, value); 114230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 114330fb85f55cbd8df3005e652da3781f51294baf90ritchie 114430fb85f55cbd8df3005e652da3781f51294baf90ritchie public InputStream getData() { 114530fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.data; 114630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 114730fb85f55cbd8df3005e652da3781f51294baf90ritchie 114830fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHeader(String name) { 114930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.header.get(name); 115030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 115130fb85f55cbd8df3005e652da3781f51294baf90ritchie 115230fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getMimeType() { 115330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.mimeType; 115430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 115530fb85f55cbd8df3005e652da3781f51294baf90ritchie 115630fb85f55cbd8df3005e652da3781f51294baf90ritchie public Method getRequestMethod() { 115730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestMethod; 115830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 115930fb85f55cbd8df3005e652da3781f51294baf90ritchie 116030fb85f55cbd8df3005e652da3781f51294baf90ritchie public IStatus getStatus() { 116130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 116230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 116330fb85f55cbd8df3005e652da3781f51294baf90ritchie 116430fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean headerAlreadySent(Map<String, String> header, String name) { 116530fb85f55cbd8df3005e652da3781f51294baf90ritchie boolean alreadySent = false; 116630fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 116730fb85f55cbd8df3005e652da3781f51294baf90ritchie alreadySent |= headerName.equalsIgnoreCase(name); 1168f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 116930fb85f55cbd8df3005e652da3781f51294baf90ritchie return alreadySent; 117030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1171269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 117230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 117330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sends given response to the socket. 117430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 117530fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void send(OutputStream outputStream) { 117630fb85f55cbd8df3005e652da3781f51294baf90ritchie String mime = this.mimeType; 117730fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 117830fb85f55cbd8df3005e652da3781f51294baf90ritchie gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 117930fb85f55cbd8df3005e652da3781f51294baf90ritchie 118030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 118130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.status == null) { 118230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error("sendResponse(): Status can't be null."); 118330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 118430fb85f55cbd8df3005e652da3781f51294baf90ritchie PrintWriter pw = new PrintWriter(outputStream); 118530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("HTTP/1.1 " + this.status.getDescription() + " \r\n"); 118630fb85f55cbd8df3005e652da3781f51294baf90ritchie 118730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mime != null) { 118830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Type: " + mime + "\r\n"); 118930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 119030fb85f55cbd8df3005e652da3781f51294baf90ritchie 119130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header == null || this.header.get("Date") == null) { 119230fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 119330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 119430fb85f55cbd8df3005e652da3781f51294baf90ritchie 119530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header != null) { 119630fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String key : this.header.keySet()) { 119730fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = this.header.get(key); 119830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print(key + ": " + value + "\r\n"); 119930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 120030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 120130fb85f55cbd8df3005e652da3781f51294baf90ritchie 120230fb85f55cbd8df3005e652da3781f51294baf90ritchie sendConnectionHeaderIfNotAlreadyPresent(pw, this.header); 120330fb85f55cbd8df3005e652da3781f51294baf90ritchie 120430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 120530fb85f55cbd8df3005e652da3781f51294baf90ritchie sendAsChunked(outputStream, pw); 1206b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } else { 120730fb85f55cbd8df3005e652da3781f51294baf90ritchie int pending = this.data != null ? this.data.available() : 0; 120830fb85f55cbd8df3005e652da3781f51294baf90ritchie pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, this.header, pending); 120930fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("\r\n"); 121030fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.flush(); 121130fb85f55cbd8df3005e652da3781f51294baf90ritchie sendAsFixedLength(outputStream, pending); 1212269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 121330fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.flush(); 121430fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.data); 121530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 121630fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe); 1217269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1218269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 121993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 122030fb85f55cbd8df3005e652da3781f51294baf90ritchie private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException { 122130fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Transfer-Encoding: chunked\r\n"); 122230fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("\r\n"); 122330fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.flush(); 122430fb85f55cbd8df3005e652da3781f51294baf90ritchie int BUFFER_SIZE = 16 * 1024; 122530fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] CRLF = "\r\n".getBytes(); 122630fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buff = new byte[BUFFER_SIZE]; 122730fb85f55cbd8df3005e652da3781f51294baf90ritchie int read; 122830fb85f55cbd8df3005e652da3781f51294baf90ritchie while ((read = this.data.read(buff)) > 0) { 122930fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(String.format("%x\r\n", read).getBytes()); 123030fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(buff, 0, read); 123130fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(CRLF); 123230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 123330fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(String.format("0\r\n\r\n").getBytes()); 123493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 123593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 123630fb85f55cbd8df3005e652da3781f51294baf90ritchie private void sendAsFixedLength(OutputStream outputStream, int pending) throws IOException { 123730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.data != null) { 123830fb85f55cbd8df3005e652da3781f51294baf90ritchie int BUFFER_SIZE = 16 * 1024; 123930fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buff = new byte[BUFFER_SIZE]; 124030fb85f55cbd8df3005e652da3781f51294baf90ritchie while (pending > 0) { 124130fb85f55cbd8df3005e652da3781f51294baf90ritchie int read = this.data.read(buff, 0, pending > BUFFER_SIZE ? BUFFER_SIZE : pending); 124230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (read <= 0) { 124330fb85f55cbd8df3005e652da3781f51294baf90ritchie break; 124430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 124530fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.write(buff, 0, read); 124630fb85f55cbd8df3005e652da3781f51294baf90ritchie pending -= read; 124730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 124830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1249fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke } 1250fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke 125130fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) { 125230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!headerAlreadySent(header, "connection")) { 125330fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Connection: keep-alive\r\n"); 125430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 125530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 125630fb85f55cbd8df3005e652da3781f51294baf90ritchie 125730fb85f55cbd8df3005e652da3781f51294baf90ritchie protected int sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, int size) { 125830fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 125930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (headerName.equalsIgnoreCase("content-length")) { 126030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 126130fb85f55cbd8df3005e652da3781f51294baf90ritchie return Integer.parseInt(header.get(headerName)); 126230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (NumberFormatException ex) { 126330fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 126430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 126530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 126630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 126730fb85f55cbd8df3005e652da3781f51294baf90ritchie 126830fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Length: " + size + "\r\n"); 126930fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 127030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127130fb85f55cbd8df3005e652da3781f51294baf90ritchie 127230fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setChunkedTransfer(boolean chunkedTransfer) { 127330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.chunkedTransfer = chunkedTransfer; 127430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127530fb85f55cbd8df3005e652da3781f51294baf90ritchie 127630fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setData(InputStream data) { 127730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = data; 127830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 127930fb85f55cbd8df3005e652da3781f51294baf90ritchie 128030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setMimeType(String mimeType) { 128130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 128230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 128330fb85f55cbd8df3005e652da3781f51294baf90ritchie 128430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setRequestMethod(Method requestMethod) { 128530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestMethod = requestMethod; 128630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 128730fb85f55cbd8df3005e652da3781f51294baf90ritchie 128830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setStatus(IStatus status) { 128930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 129030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 129230fb85f55cbd8df3005e652da3781f51294baf90ritchie 129330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final class ResponseException extends Exception { 129430fb85f55cbd8df3005e652da3781f51294baf90ritchie 129530fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final long serialVersionUID = 6569838532917408380L; 129630fb85f55cbd8df3005e652da3781f51294baf90ritchie 129730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Response.Status status; 129830fb85f55cbd8df3005e652da3781f51294baf90ritchie 129930fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message) { 130030fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message); 130130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 130230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 130330fb85f55cbd8df3005e652da3781f51294baf90ritchie 130430fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message, Exception e) { 130530fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message, e); 130630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 130730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 130830fb85f55cbd8df3005e652da3781f51294baf90ritchie 130930fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response.Status getStatus() { 131030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 131130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 131230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 131330fb85f55cbd8df3005e652da3781f51294baf90ritchie 131430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1315abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for the main listening thread. 1316abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1317abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ServerRunnable implements Runnable { 1318abcf1089ce1de49278970f088883cb32acb4f225ritchie 1319abcf1089ce1de49278970f088883cb32acb4f225ritchie private final int timeout; 1320abcf1089ce1de49278970f088883cb32acb4f225ritchie 1321abcf1089ce1de49278970f088883cb32acb4f225ritchie private ServerRunnable(int timeout) { 1322abcf1089ce1de49278970f088883cb32acb4f225ritchie this.timeout = timeout; 1323abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1324abcf1089ce1de49278970f088883cb32acb4f225ritchie 1325abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 1326abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 1327abcf1089ce1de49278970f088883cb32acb4f225ritchie do { 1328abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 1329abcf1089ce1de49278970f088883cb32acb4f225ritchie final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept(); 1330abcf1089ce1de49278970f088883cb32acb4f225ritchie finalAccept.setSoTimeout(this.timeout); 1331abcf1089ce1de49278970f088883cb32acb4f225ritchie final InputStream inputStream = finalAccept.getInputStream(); 1332abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream)); 1333abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (IOException e) { 1334abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 1335abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1336abcf1089ce1de49278970f088883cb32acb4f225ritchie } while (!NanoHTTPD.this.myServerSocket.isClosed()); 1337abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1338abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1339abcf1089ce1de49278970f088883cb32acb4f225ritchie 1340abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 134130fb85f55cbd8df3005e652da3781f51294baf90ritchie * A temp file. 134230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 134330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 134430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp files are responsible for managing the actual temporary storage and 134530fb85f55cbd8df3005e652da3781f51294baf90ritchie * cleaning themselves up when no longer needed. 134630fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 134730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 134830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFile { 134930fb85f55cbd8df3005e652da3781f51294baf90ritchie 135030fb85f55cbd8df3005e652da3781f51294baf90ritchie void delete() throws Exception; 135130fb85f55cbd8df3005e652da3781f51294baf90ritchie 135230fb85f55cbd8df3005e652da3781f51294baf90ritchie String getName(); 135330fb85f55cbd8df3005e652da3781f51294baf90ritchie 135430fb85f55cbd8df3005e652da3781f51294baf90ritchie OutputStream open() throws Exception; 135530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 135630fb85f55cbd8df3005e652da3781f51294baf90ritchie 135730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 135830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file manager. 135930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 136030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 136130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file managers are created 1-to-1 with incoming requests, to create 136230fb85f55cbd8df3005e652da3781f51294baf90ritchie * and cleanup temporary files created as a result of handling the request. 136330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 136430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 136530fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManager { 136630fb85f55cbd8df3005e652da3781f51294baf90ritchie 136730fb85f55cbd8df3005e652da3781f51294baf90ritchie void clear(); 136830fb85f55cbd8df3005e652da3781f51294baf90ritchie 136930fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFile createTempFile() throws Exception; 137030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 137130fb85f55cbd8df3005e652da3781f51294baf90ritchie 137230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 137330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Factory to create temp file managers. 137430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 137530fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManagerFactory { 137630fb85f55cbd8df3005e652da3781f51294baf90ritchie 137730fb85f55cbd8df3005e652da3781f51294baf90ritchie TempFileManager create(); 137830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 137930fb85f55cbd8df3005e652da3781f51294baf90ritchie 138030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 138130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) 138230fb85f55cbd8df3005e652da3781f51294baf90ritchie * This is required as the Keep-Alive HTTP connections would otherwise block 138330fb85f55cbd8df3005e652da3781f51294baf90ritchie * the socket reading thread forever (or as long the browser is open). 138430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 138530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int SOCKET_READ_TIMEOUT = 5000; 138630fb85f55cbd8df3005e652da3781f51294baf90ritchie 138730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 138830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: plain text 138930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 139030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_PLAINTEXT = "text/plain"; 139130fb85f55cbd8df3005e652da3781f51294baf90ritchie 139230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 139330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: html 139430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 139530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_HTML = "text/html"; 139630fb85f55cbd8df3005e652da3781f51294baf90ritchie 139730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 139830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pseudo-Parameter to use to store the actual query string in the 139930fb85f55cbd8df3005e652da3781f51294baf90ritchie * parameters map for later re-processing. 140030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 140130fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; 140230fb85f55cbd8df3005e652da3781f51294baf90ritchie 140330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 140430fb85f55cbd8df3005e652da3781f51294baf90ritchie * logger to log to. 140530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 14069c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName()); 140730fb85f55cbd8df3005e652da3781f51294baf90ritchie 140830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 140930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an 141030fb85f55cbd8df3005e652da3781f51294baf90ritchie * array of loaded KeyManagers. These objects must properly 141130fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded/initialized by the caller. 141230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 141330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException { 141430fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 141530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 141630fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 141730fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 141830fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 141930fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); 142030fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 142130fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 142230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 142330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 142430fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 142530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 142630fb85f55cbd8df3005e652da3781f51294baf90ritchie 142730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 142830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a 142930fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded KeyManagerFactory. These objects must properly loaded/initialized 143030fb85f55cbd8df3005e652da3781f51294baf90ritchie * by the caller. 143130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 143230fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException { 143330fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 143430fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 143530fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 143630fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 143730fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 143830fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(loadedKeyFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 143930fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 144030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 144130fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 144230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 144330fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 144430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 144530fb85f55cbd8df3005e652da3781f51294baf90ritchie 144630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 144730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your 144830fb85f55cbd8df3005e652da3781f51294baf90ritchie * certificate and passphrase 144930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 145030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException { 145130fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 145230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 145330fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 145430fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); 145530fb85f55cbd8df3005e652da3781f51294baf90ritchie keystore.load(keystoreStream, passphrase); 145630fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 145730fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(keystore); 145830fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 145930fb85f55cbd8df3005e652da3781f51294baf90ritchie keyManagerFactory.init(keystore, passphrase); 146030fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 146130fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 146230fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 146330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 146430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 146530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 146630fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 146730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 146830fb85f55cbd8df3005e652da3781f51294baf90ritchie 146930fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final void safeClose(Closeable closeable) { 147030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (closeable != null) { 147130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 147230fb85f55cbd8df3005e652da3781f51294baf90ritchie closeable.close(); 147330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException e) { 147430fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e); 147530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 147630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 147730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 147830fb85f55cbd8df3005e652da3781f51294baf90ritchie 147930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String hostname; 148030fb85f55cbd8df3005e652da3781f51294baf90ritchie 148130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int myPort; 148230fb85f55cbd8df3005e652da3781f51294baf90ritchie 148330fb85f55cbd8df3005e652da3781f51294baf90ritchie private ServerSocket myServerSocket; 148430fb85f55cbd8df3005e652da3781f51294baf90ritchie 148530fb85f55cbd8df3005e652da3781f51294baf90ritchie private SSLServerSocketFactory sslServerSocketFactory; 148630fb85f55cbd8df3005e652da3781f51294baf90ritchie 148730fb85f55cbd8df3005e652da3781f51294baf90ritchie private Thread myThread; 148830fb85f55cbd8df3005e652da3781f51294baf90ritchie 148930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 149030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 149130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 149230fb85f55cbd8df3005e652da3781f51294baf90ritchie private AsyncRunner asyncRunner; 149330fb85f55cbd8df3005e652da3781f51294baf90ritchie 149430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 149530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 149630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 149730fb85f55cbd8df3005e652da3781f51294baf90ritchie private TempFileManagerFactory tempFileManagerFactory; 149830fb85f55cbd8df3005e652da3781f51294baf90ritchie 149930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 150030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given port. 150130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 150230fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(int port) { 150330fb85f55cbd8df3005e652da3781f51294baf90ritchie this(null, port); 150430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 150530fb85f55cbd8df3005e652da3781f51294baf90ritchie 150630fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 150730fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 150830fb85f55cbd8df3005e652da3781f51294baf90ritchie // 150930fb85f55cbd8df3005e652da3781f51294baf90ritchie // Threading Strategy. 151030fb85f55cbd8df3005e652da3781f51294baf90ritchie // 151130fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 151230fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 151330fb85f55cbd8df3005e652da3781f51294baf90ritchie 151430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 151530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given hostname and port. 151630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 151730fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(String hostname, int port) { 151830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.hostname = hostname; 151930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myPort = port; 152030fb85f55cbd8df3005e652da3781f51294baf90ritchie setTempFileManagerFactory(new DefaultTempFileManagerFactory()); 152130fb85f55cbd8df3005e652da3781f51294baf90ritchie setAsyncRunner(new DefaultAsyncRunner()); 152230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 152330fb85f55cbd8df3005e652da3781f51294baf90ritchie 152430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 152530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Forcibly closes all connections that are open. 152630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 152730fb85f55cbd8df3005e652da3781f51294baf90ritchie public synchronized void closeAllConnections() { 1528abcf1089ce1de49278970f088883cb32acb4f225ritchie stop(); 1529abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1530abcf1089ce1de49278970f088883cb32acb4f225ritchie 1531abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1532abcf1089ce1de49278970f088883cb32acb4f225ritchie * create a instance of the client handler, subclasses can return a subclass 1533abcf1089ce1de49278970f088883cb32acb4f225ritchie * of the ClientHandler. 1534abcf1089ce1de49278970f088883cb32acb4f225ritchie * 1535abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param finalAccept 1536abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socket the cleint is connected to 1537abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param inputStream 1538abcf1089ce1de49278970f088883cb32acb4f225ritchie * the input stream 1539abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the client handler 1540abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1541abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) { 1542abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ClientHandler(inputStream, finalAccept); 1543abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1544abcf1089ce1de49278970f088883cb32acb4f225ritchie 1545abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1546abcf1089ce1de49278970f088883cb32acb4f225ritchie * Instantiate the server runnable, can be overwritten by subclasses to 1547abcf1089ce1de49278970f088883cb32acb4f225ritchie * provide a subclass of the ServerRunnable. 1548abcf1089ce1de49278970f088883cb32acb4f225ritchie * 1549abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param timeout 1550abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socet timeout to use. 1551abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the server runnable. 1552abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1553abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ServerRunnable createServerRunnable(final int timeout) { 1554abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ServerRunnable(timeout); 155530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 155630fb85f55cbd8df3005e652da3781f51294baf90ritchie 155730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 155830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 155930fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 156030fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 156130fb85f55cbd8df3005e652da3781f51294baf90ritchie * 156230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 156330fb85f55cbd8df3005e652da3781f51294baf90ritchie * original <b>NanoHTTPD</b> parameters values, as passed to the 156430fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>serve()</code> method. 156530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 156630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 156730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 156830fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(Map<String, String> parms) { 156930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER)); 157030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 157130fb85f55cbd8df3005e652da3781f51294baf90ritchie 1572abcf1089ce1de49278970f088883cb32acb4f225ritchie // ------------------------------------------------------------------------------- 1573abcf1089ce1de49278970f088883cb32acb4f225ritchie // // 1574abcf1089ce1de49278970f088883cb32acb4f225ritchie 157530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 157630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 157730fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 157830fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 157930fb85f55cbd8df3005e652da3781f51294baf90ritchie * 158030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param queryString 158130fb85f55cbd8df3005e652da3781f51294baf90ritchie * a query string pulled from the URL. 158230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 158330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 158430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 158530fb85f55cbd8df3005e652da3781f51294baf90ritchie protected Map<String, List<String>> decodeParameters(String queryString) { 158630fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, List<String>> parms = new HashMap<String, List<String>>(); 158730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (queryString != null) { 158830fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(queryString, "&"); 158930fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 159030fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 159130fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 159230fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); 159330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!parms.containsKey(propertyName)) { 159430fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(propertyName, new ArrayList<String>()); 159530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 159630fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null; 159730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (propertyValue != null) { 159830fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.get(propertyName).add(propertyValue); 159930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160193441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 160230fb85f55cbd8df3005e652da3781f51294baf90ritchie return parms; 160330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 160530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 160630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode percent encoded <code>String</code> values. 160730fb85f55cbd8df3005e652da3781f51294baf90ritchie * 160830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param str 160930fb85f55cbd8df3005e652da3781f51294baf90ritchie * the percent encoded <code>String</code> 161030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return expanded form of the input, for example "foo%20bar" becomes 161130fb85f55cbd8df3005e652da3781f51294baf90ritchie * "foo bar" 161230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 161330fb85f55cbd8df3005e652da3781f51294baf90ritchie protected String decodePercent(String str) { 161430fb85f55cbd8df3005e652da3781f51294baf90ritchie String decoded = null; 161530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 161630fb85f55cbd8df3005e652da3781f51294baf90ritchie decoded = URLDecoder.decode(str, "UTF8"); 161730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (UnsupportedEncodingException ignored) { 161830fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored); 161993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 162030fb85f55cbd8df3005e652da3781f51294baf90ritchie return decoded; 162130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 162293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 162330fb85f55cbd8df3005e652da3781f51294baf90ritchie public final int getListeningPort() { 162430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort(); 162530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1626964393fd7e5ed49088882a126cf82507184467efMartin M Reed 162730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean isAlive() { 162830fb85f55cbd8df3005e652da3781f51294baf90ritchie return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); 16298dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1630964393fd7e5ed49088882a126cf82507184467efMartin M Reed 163130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 163230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Call before start() to serve over HTTPS instead of HTTP 163330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 163430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void makeSecure(SSLServerSocketFactory sslServerSocketFactory) { 163530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.sslServerSocketFactory = sslServerSocketFactory; 163630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 16379058464950a9734da0a7ff2dc47f3081bbb5117critchie 163830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 163930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 164030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 164130fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 164230fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 164330fb85f55cbd8df3005e652da3781f51294baf90ritchie * 164430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param session 164530fb85f55cbd8df3005e652da3781f51294baf90ritchie * The HTTP session 164630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 164730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 164830fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(IHTTPSession session) { 164930fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> files = new HashMap<String, String>(); 165030fb85f55cbd8df3005e652da3781f51294baf90ritchie Method method = session.getMethod(); 165130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.PUT.equals(method) || Method.POST.equals(method)) { 165230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 165330fb85f55cbd8df3005e652da3781f51294baf90ritchie session.parseBody(files); 165430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 165530fb85f55cbd8df3005e652da3781f51294baf90ritchie return new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 165630fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 165730fb85f55cbd8df3005e652da3781f51294baf90ritchie return new Response(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 165830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 16595820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 16605820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 166130fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> parms = session.getParms(); 166230fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString()); 166330fb85f55cbd8df3005e652da3781f51294baf90ritchie return serve(session.getUri(), method, session.getHeaders(), parms, files); 166430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 16655820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 166630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 166730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 166830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 166930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 167030fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 167130fb85f55cbd8df3005e652da3781f51294baf90ritchie * 167230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param uri 167330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Percent-decoded URI without parameters, for example 167430fb85f55cbd8df3005e652da3781f51294baf90ritchie * "/index.cgi" 167530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param method 167630fb85f55cbd8df3005e652da3781f51294baf90ritchie * "GET", "POST" etc. 167730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 167830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Parsed, percent decoded parameters from URI and, in case of 167930fb85f55cbd8df3005e652da3781f51294baf90ritchie * POST, data. 168030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param headers 168130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Header entries, percent decoded 168230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 168330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 168430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Deprecated 168530fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) { 168630fb85f55cbd8df3005e652da3781f51294baf90ritchie return new Response(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); 168730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 16885820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 168930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 169030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 169130fb85f55cbd8df3005e652da3781f51294baf90ritchie * 169230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param asyncRunner 169330fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling threads. 169430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 169530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setAsyncRunner(AsyncRunner asyncRunner) { 169630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.asyncRunner = asyncRunner; 169730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 16985820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 169930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 170030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 170130fb85f55cbd8df3005e652da3781f51294baf90ritchie * 170230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param tempFileManagerFactory 170330fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling temp files. 170430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 170530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { 170630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManagerFactory = tempFileManagerFactory; 17075820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 17085820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 17098dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 171030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Start the server. 17119058464950a9734da0a7ff2dc47f3081bbb5117critchie * 171230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @throws IOException 171330fb85f55cbd8df3005e652da3781f51294baf90ritchie * if the socket is in use. 17148dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 171530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void start() throws IOException { 1716c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie start(NanoHTTPD.SOCKET_READ_TIMEOUT); 1717c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie } 1718c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie 1719c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie /** 1720c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * Start the server. 1721c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * 1722c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @param timeout 1723c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * timeout to use for socket connections. 1724c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @throws IOException 1725c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * if the socket is in use. 1726c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie */ 1727c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie public void start(final int timeout) throws IOException { 172830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.sslServerSocketFactory != null) { 172930fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocket ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); 173030fb85f55cbd8df3005e652da3781f51294baf90ritchie ss.setNeedClientAuth(false); 173130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = ss; 173230fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 173330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket = new ServerSocket(); 173430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 173530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.setReuseAddress(true); 173630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.bind(this.hostname != null ? new InetSocketAddress(this.hostname, this.myPort) : new InetSocketAddress(this.myPort)); 17379058464950a9734da0a7ff2dc47f3081bbb5117critchie 1738abcf1089ce1de49278970f088883cb32acb4f225ritchie this.myThread = new Thread(createServerRunnable(timeout)); 173930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setDaemon(true); 174030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setName("NanoHttpd Main Listener"); 174130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.start(); 174230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 17435820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 174430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 174530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Stop the server. 174630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 174730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void stop() { 174830fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 174930fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.myServerSocket); 1750abcf1089ce1de49278970f088883cb32acb4f225ritchie this.asyncRunner.closeAll(); 175130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.myThread != null) { 175230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.join(); 175330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 175430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 175530fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e); 1756964393fd7e5ed49088882a126cf82507184467efMartin M Reed } 175730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1758964393fd7e5ed49088882a126cf82507184467efMartin M Reed 175930fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean wasStarted() { 176030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket != null && this.myThread != null; 17617e423dde0fe863fadbd363de8a94746a30fca0eeMartin M Reed } 1762269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 1763